Files
virtual-kubelet/vendor/github.com/vmware/vic/pkg/trace/operation.go
Loc Nguyen 513cebe7b7 VMware vSphere Integrated Containers provider (#206)
* Add Virtual Kubelet provider for VIC

Initial virtual kubelet provider for VMware VIC.  This provider currently
handles creating and starting of a pod VM via the VIC portlayer and persona
server.  Image store handling via the VIC persona server.  This provider
currently requires the feature/wolfpack branch of VIC.

* Added pod stop and delete.  Also added node capacity.

Added the ability to stop and delete pod VMs via VIC.  Also retrieve
node capacity information from the VCH.

* Cleanup and readme file

Some file clean up and added a Readme.md markdown file for the VIC
provider.

* Cleaned up errors, added function comments, moved operation code

1. Cleaned up error handling.  Set standard for creating errors.
2. Added method prototype comments for all interface functions.
3. Moved PodCreator, PodStarter, PodStopper, and PodDeleter to a new folder.

* Add mocking code and unit tests for podcache, podcreator, and podstarter

Used the unit test framework used in VIC to handle assertions in the provider's
unit test.  Mocking code generated using OSS project mockery, which is compatible
with the testify assertion framework.

* Vendored packages for the VIC provider

Requires feature/wolfpack branch of VIC and a few specific commit sha of
projects used within VIC.

* Implementation of POD Stopper and Deleter unit tests (#4)

* Updated files for initial PR
2018-06-04 15:41:32 -07:00

319 lines
8.8 KiB
Go

// Copyright 2016 VMware, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package trace
import (
"bytes"
"context"
"fmt"
"os"
"sync/atomic"
"time"
"github.com/Sirupsen/logrus"
"github.com/vmware/govmomi/vim25/types"
)
type OperationKey string
const OpTraceKey OperationKey = "traceKey"
var opIDPrefix = os.Getpid()
// opCount is a monotonic counter which increments on Start()
var opCount uint64
type Operation struct {
context.Context
operation
// Logger is used to configure an Operation-specific destination for log messages, in addition
// to the global logger. This logger is passed to any children which are created.
Logger *logrus.Logger
}
type operation struct {
t []Message
id string
}
func newOperation(ctx context.Context, id string, skip int, msg string) Operation {
op := operation{
// Can be used to trace based on this number which is unique per chain
// of operations
id: id,
// Start the trace.
t: []Message{*newTrace(msg, skip, id)},
}
// We need to be able to identify this operation across API (and process)
// boundaries. So add the trace as a value to the embedded context. We
// stash the values individually in the context because we can't assign
// the operation itself as a value to the embedded context (it's circular)
ctx = context.WithValue(ctx, OpTraceKey, op)
// By adding the op.id any operations passed to govmomi will result
// in the op.id being logged in vSphere (vpxa / hostd) as the prefix to opID
// For example if the op.id was 299.16 hostd would show
// verbose hostd[12281B70] [Originator@6876 sub=PropertyProvider opID=299.16-5b05 user=root]
ctx = context.WithValue(ctx, types.ID{}, op.id)
o := Operation{
Context: ctx,
operation: op,
}
return o
}
// Creates a header string to be printed.
func (o *Operation) header() string {
return fmt.Sprintf("op=%s", o.id)
}
// Err returns a non-nil error value after Done is closed. Err returns
// Canceled if the context was canceled or DeadlineExceeded if the
// context's deadline passed. No other values for Err are defined.
// After Done is closed, successive calls to Err return the same value.
func (o Operation) Err() error {
// Walk up the contexts from which this context was created and get their errors
if err := o.Context.Err(); err != nil {
buf := &bytes.Buffer{}
// Add a frame for this Err call, then walk the stack
currFrame := newTrace("Err", 2, o.id)
fmt.Fprintf(buf, "%s: %s error: %s\n", currFrame.funcName, o.t[0].msg, err)
// handle the carriage return
numFrames := len(o.t)
for i, t := range o.t {
fmt.Fprintf(buf, "%-15s:%d %s", t.funcName, t.lineNo, t.msg)
// don't add a cr on the last frame
if i != numFrames-1 {
buf.WriteByte('\n')
}
}
// Print the error
o.Errorf(buf.String())
return err
}
return nil
}
func (o Operation) String() string {
return o.header()
}
func (o *Operation) ID() string {
return o.id
}
func (o *Operation) Infof(format string, args ...interface{}) {
o.Info(fmt.Sprintf(format, args...))
}
func (o *Operation) Info(args ...interface{}) {
msg := fmt.Sprint(args...)
Logger.Infof("%s: %s", o.header(), msg)
if o.Logger != nil {
o.Logger.Info(msg)
}
}
func (o *Operation) Debugf(format string, args ...interface{}) {
o.Debug(fmt.Sprintf(format, args...))
}
func (o *Operation) Debug(args ...interface{}) {
msg := fmt.Sprint(args...)
Logger.Debugf("%s: %s", o.header(), msg)
if o.Logger != nil {
o.Logger.Debug(msg)
}
}
func (o *Operation) Warnf(format string, args ...interface{}) {
o.Warn(fmt.Sprintf(format, args...))
}
func (o *Operation) Warn(args ...interface{}) {
msg := fmt.Sprint(args...)
Logger.Warnf("%s: %s", o.header(), msg)
if o.Logger != nil {
o.Logger.Warn(msg)
}
}
func (o *Operation) Errorf(format string, args ...interface{}) {
o.Error(fmt.Sprintf(format, args...))
}
func (o *Operation) Error(args ...interface{}) {
msg := fmt.Sprint(args...)
Logger.Errorf("%s: %s", o.header(), msg)
if o.Logger != nil {
o.Logger.Error(msg)
}
}
func (o *Operation) Panicf(format string, args ...interface{}) {
o.Panic(fmt.Sprintf(format, args...))
}
func (o *Operation) Panic(args ...interface{}) {
msg := fmt.Sprint(args...)
Logger.Panicf("%s: %s", o.header(), msg)
if o.Logger != nil {
o.Logger.Panic(msg)
}
}
func (o *Operation) Fatalf(format string, args ...interface{}) {
o.Fatal(fmt.Sprintf(format, args...))
}
func (o *Operation) Fatal(args ...interface{}) {
msg := fmt.Sprint(args...)
Logger.Fatalf("%s: %s", o.header(), msg)
if o.Logger != nil {
o.Logger.Fatal(msg)
}
}
func (o *Operation) newChild(ctx context.Context, msg string) Operation {
child := newOperation(ctx, o.id, 4, msg)
child.t = append(child.t, o.t...)
child.Logger = o.Logger
return child
}
func opID(opNum uint64) string {
return fmt.Sprintf("%d.%d", opIDPrefix, opNum)
}
// NewOperation will return a new operation with operationID added as a value to the context
func NewOperation(ctx context.Context, format string, args ...interface{}) Operation {
o := newOperation(ctx, opID(atomic.AddUint64(&opCount, 1)), 3, fmt.Sprintf(format, args...))
frame := o.t[0]
o.Debugf("[NewOperation] %s [%s:%d]", o.header(), frame.funcName, frame.lineNo)
return o
}
// NewOperationWithLoggerFrom will return a new operation with operationID added as a value to the
// context and logging settings copied from the supplied operation.
//
// Deprecated: This method was added to aid in converting old code to use operation-based logging.
// Its use almost always indicates a broken context/operation model (e.g., a context
// being improperly stored in a struct instead of being passed between functions).
func NewOperationWithLoggerFrom(ctx context.Context, oldOp Operation, format string, args ...interface{}) Operation {
op := NewOperation(ctx, format, args...)
op.Logger = oldOp.Logger
return op
}
// WithTimeout creates a new operation from parent with context.WithTimeout
func WithTimeout(parent *Operation, timeout time.Duration, format string, args ...interface{}) (Operation, context.CancelFunc) {
ctx, cancelFunc := context.WithTimeout(parent.Context, timeout)
op := parent.newChild(ctx, fmt.Sprintf(format, args...))
return op, cancelFunc
}
// WithDeadline creates a new operation from parent with context.WithDeadline
func WithDeadline(parent *Operation, expiration time.Time, format string, args ...interface{}) (Operation, context.CancelFunc) {
ctx, cancelFunc := context.WithDeadline(parent.Context, expiration)
op := parent.newChild(ctx, fmt.Sprintf(format, args...))
return op, cancelFunc
}
// WithCancel creates a new operation from parent with context.WithCancel
func WithCancel(parent *Operation, format string, args ...interface{}) (Operation, context.CancelFunc) {
ctx, cancelFunc := context.WithCancel(parent.Context)
op := parent.newChild(ctx, fmt.Sprintf(format, args...))
return op, cancelFunc
}
// WithValue creates a new operation from parent with context.WithValue
func WithValue(parent *Operation, key, val interface{}, format string, args ...interface{}) Operation {
ctx := context.WithValue(parent.Context, key, val)
op := parent.newChild(ctx, fmt.Sprintf(format, args...))
return op
}
// FromOperation creates a child operation from the one supplied
// uses the same context as the parent
func FromOperation(parent Operation, format string, args ...interface{}) Operation {
return parent.newChild(parent.Context, fmt.Sprintf(format, args...))
}
// FromContext will return an Operation
//
// The Operation returned will be one of the following:
// The operation in the context value
// The operation passed as the context param
// A new operation
func FromContext(ctx context.Context, message string, args ...interface{}) Operation {
// do we have an operation
if op, ok := ctx.(Operation); ok {
return op
}
// do we have a context w/the op added as a value
if op, ok := ctx.Value(OpTraceKey).(operation); ok {
// ensure we have an initialized operation
if op.id == "" {
return NewOperation(ctx, message, args...)
}
// return an operation based off the context value
return Operation{
Context: ctx,
operation: op,
}
}
op := newOperation(ctx, opID(atomic.AddUint64(&opCount, 1)), 3, fmt.Sprintf(message, args...))
frame := op.t[0]
Logger.Debugf("%s: [OperationFromContext] [%s:%d]", op.id, frame.funcName, frame.lineNo)
// return the new operation
return op
}