From 03eaba037fa332b4deef8914992a78f5af3a4d71 Mon Sep 17 00:00:00 2001 From: iwilltry42 Date: Thu, 10 Oct 2019 12:30:48 +0200 Subject: [PATCH] restructure docker runtime pkg --- pkg/runtimes/docker/container.go | 86 +---- pkg/runtimes/docker/node.go | 104 ++++++ vendor/github.com/go-test/deep/.gitignore | 2 + vendor/github.com/go-test/deep/.travis.yml | 13 + vendor/github.com/go-test/deep/CHANGES.md | 30 ++ vendor/github.com/go-test/deep/LICENSE | 21 ++ vendor/github.com/go-test/deep/README.md | 51 +++ vendor/github.com/go-test/deep/deep.go | 376 +++++++++++++++++++++ vendor/github.com/go-test/deep/go.mod | 1 + vendor/modules.txt | 2 + 10 files changed, 613 insertions(+), 73 deletions(-) create mode 100644 pkg/runtimes/docker/node.go create mode 100644 vendor/github.com/go-test/deep/.gitignore create mode 100644 vendor/github.com/go-test/deep/.travis.yml create mode 100644 vendor/github.com/go-test/deep/CHANGES.md create mode 100644 vendor/github.com/go-test/deep/LICENSE create mode 100644 vendor/github.com/go-test/deep/README.md create mode 100644 vendor/github.com/go-test/deep/deep.go create mode 100644 vendor/github.com/go-test/deep/go.mod diff --git a/pkg/runtimes/docker/container.go b/pkg/runtimes/docker/container.go index 91c1d0f0..1cd01940 100644 --- a/pkg/runtimes/docker/container.go +++ b/pkg/runtimes/docker/container.go @@ -26,47 +26,35 @@ import ( "context" "fmt" - "github.com/docker/docker/api/types/filters" - "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/network" "github.com/docker/docker/client" - k3d "github.com/rancher/k3d/pkg/types" log "github.com/sirupsen/logrus" ) -// CreateNode creates a new container -func (d Docker) CreateNode(node *k3d.Node) error { - log.Debugln("docker.CreateNode...") - ctx := context.Background() // TODO: check how kind handles contexts +// createContainer creates a new docker container from translated specs +func createContainer(dockerNode *NodeInDocker, name string) error { + + log.Debugf("Creating docker container with translated config\n%+v\n", dockerNode) // TODO: remove? + + // initialize docker client + ctx := context.Background() docker, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) if err != nil { - return fmt.Errorf("Failed to create docker client. %+v", err) - } - - containerConfig := container.Config{ - Cmd: node.Args, - Image: node.Image, - Labels: node.Labels, + log.Errorln("Failed to create docker client") + return err } - resp, err := docker.ContainerCreate(ctx, &containerConfig, &container.HostConfig{}, &network.NetworkingConfig{}, node.Name) + // start container // TODO: check first if image exists locally and pull if it doesn't + resp, err := docker.ContainerCreate(ctx, &dockerNode.ContainerConfig, &dockerNode.HostConfig, &dockerNode.NetworkingConfig, name) if err != nil { - log.Error("Couldn't create container") + log.Errorln("Failed to create container") return err } - log.Infoln("Created", resp.ID) + log.Infoln("Created container", resp.ID) return nil } -// createContainer creates a new docker container -// @return containerID, error -func createContainer(types.Container) (string, error) { - return "", nil -} - // removeContainer deletes a running container (like docker rm -f) func removeContainer(ID string) error { @@ -92,51 +80,3 @@ func removeContainer(ID string) error { return nil } - -// DeleteNode deletes a node -func (d Docker) DeleteNode(nodeSpec *k3d.Node) error { - log.Debugln("docker.DeleteNode...") - return removeContainer(nodeSpec.Name) -} - -func (d Docker) GetNodesByLabel(labels map[string]string) ([]*k3d.Node, error) { - - // (0) create docker client - ctx := context.Background() - docker, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) - if err != nil { - return nil, fmt.Errorf("Failed to create docker client. %+v", err) - } - - // (1) list containers which have the default k3d labels attached - filters := filters.NewArgs() - for k, v := range k3d.DefaultObjectLabels { - filters.Add("label", fmt.Sprintf("%s=%s", k, v)) - } - for k, v := range labels { - filters.Add("label", fmt.Sprintf("%s=%s", k, v)) - } - - containers, err := docker.ContainerList(ctx, types.ContainerListOptions{ - Filters: filters, - All: true, - }) - if err != nil { - log.Errorln("Failed to list containers") - return nil, err - } - - // (2) convert them to node structs - nodes := []*k3d.Node{} - for _, container := range containers { - node := &k3d.Node{ - Name: container.Names[0], - Role: container.Labels["role"], // TODO: catch keyerror - Labels: container.Labels, - } - nodes = append(nodes, node) - } - - return nodes, nil - -} diff --git a/pkg/runtimes/docker/node.go b/pkg/runtimes/docker/node.go new file mode 100644 index 00000000..77c9c311 --- /dev/null +++ b/pkg/runtimes/docker/node.go @@ -0,0 +1,104 @@ +/* +Copyright © 2019 Thorsten Klein + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +package docker + +import ( + "context" + "fmt" + + "github.com/docker/docker/api/types/filters" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/client" + k3d "github.com/rancher/k3d/pkg/types" + log "github.com/sirupsen/logrus" +) + +// CreateNode creates a new container +func (d Docker) CreateNode(node *k3d.Node) error { + log.Debugln("docker.CreateNode...") + + // translate node spec to docker container specs + dockerNode, err := TranslateNodeToContainer(node) + if err != nil { + log.Errorln("Failed to translate k3d node specification to docker container specifications") + return err + } + + // create node + if err := createContainer(dockerNode, node.Name); err != nil { + log.Errorln("Failed to create k3d node") + return err + } + + return nil +} + +// DeleteNode deletes a node +func (d Docker) DeleteNode(nodeSpec *k3d.Node) error { + log.Debugln("docker.DeleteNode...") + return removeContainer(nodeSpec.Name) +} + +// GetNodesByLabel returns a list of existing nodes +func (d Docker) GetNodesByLabel(labels map[string]string) ([]*k3d.Node, error) { + + // (0) create docker client + ctx := context.Background() + docker, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) + if err != nil { + return nil, fmt.Errorf("Failed to create docker client. %+v", err) + } + + // (1) list containers which have the default k3d labels attached + filters := filters.NewArgs() + for k, v := range k3d.DefaultObjectLabels { + filters.Add("label", fmt.Sprintf("%s=%s", k, v)) + } + for k, v := range labels { + filters.Add("label", fmt.Sprintf("%s=%s", k, v)) + } + + containers, err := docker.ContainerList(ctx, types.ContainerListOptions{ + Filters: filters, + All: true, + }) + if err != nil { + log.Errorln("Failed to list containers") + return nil, err + } + + // (2) convert them to node structs + nodes := []*k3d.Node{} + for _, container := range containers { + node := &k3d.Node{ + Name: container.Names[0], + Role: container.Labels["role"], // TODO: catch keyerror + Labels: container.Labels, + } + nodes = append(nodes, node) + } + + return nodes, nil + +} diff --git a/vendor/github.com/go-test/deep/.gitignore b/vendor/github.com/go-test/deep/.gitignore new file mode 100644 index 00000000..53f12f0f --- /dev/null +++ b/vendor/github.com/go-test/deep/.gitignore @@ -0,0 +1,2 @@ +*.swp +*.out diff --git a/vendor/github.com/go-test/deep/.travis.yml b/vendor/github.com/go-test/deep/.travis.yml new file mode 100644 index 00000000..df3972fc --- /dev/null +++ b/vendor/github.com/go-test/deep/.travis.yml @@ -0,0 +1,13 @@ +language: go + +go: + - "1.10" + - "1.11" + - "1.12" + +before_install: + - go get github.com/mattn/goveralls + - go get golang.org/x/tools/cover + +script: + - $HOME/gopath/bin/goveralls -service=travis-ci diff --git a/vendor/github.com/go-test/deep/CHANGES.md b/vendor/github.com/go-test/deep/CHANGES.md new file mode 100644 index 00000000..00f072b1 --- /dev/null +++ b/vendor/github.com/go-test/deep/CHANGES.md @@ -0,0 +1,30 @@ +# go-test/deep Changelog + +## v1.0.4 released 2019-09-15 + +* Added \`deep:"-"\` structure field tag to ignore field (PR #38) (@flga) + +## v1.0.3 released 2019-08-18 + +* Fixed issue #31: panic on typed primitives that implement error interface + +## v1.0.2 released 2019-07-14 + +* Enabled Go module (@radeksimko) +* Changed supported and tested Go versions: 1.10, 1.11, and 1.12 (dropped 1.9) +* Changed Error equality: additional struct fields are compared too (PR #29) (@andrewmostello) +* Fixed typos and ineffassign issues (PR #25) (@tariq1890) +* Fixed diff order for nil comparison (PR #16) (@gmarik) +* Fixed slice equality when slices are extracted from the same array (PR #11) (@risteli) +* Fixed test spelling and messages (PR #19) (@sofuture) +* Fixed issue #15: panic on comparing struct with anonymous time.Time +* Fixed issue #18: Panic when comparing structs with time.Time value and CompareUnexportedFields is true +* Fixed issue #21: Set default MaxDepth = 0 (disabled) (PR #23) + +## v1.0.1 released 2018-01-28 + +* Fixed issue #12: Arrays are not properly compared (@samlitowitz) + +## v1.0.0 releaesd 2017-10-27 + +* First release diff --git a/vendor/github.com/go-test/deep/LICENSE b/vendor/github.com/go-test/deep/LICENSE new file mode 100644 index 00000000..228ef16f --- /dev/null +++ b/vendor/github.com/go-test/deep/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright 2015-2017 Daniel Nichter + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/go-test/deep/README.md b/vendor/github.com/go-test/deep/README.md new file mode 100644 index 00000000..3b78eac7 --- /dev/null +++ b/vendor/github.com/go-test/deep/README.md @@ -0,0 +1,51 @@ +# Deep Variable Equality for Humans + +[![Go Report Card](https://goreportcard.com/badge/github.com/go-test/deep)](https://goreportcard.com/report/github.com/go-test/deep) [![Build Status](https://travis-ci.org/go-test/deep.svg?branch=master)](https://travis-ci.org/go-test/deep) [![Coverage Status](https://coveralls.io/repos/github/go-test/deep/badge.svg?branch=master)](https://coveralls.io/github/go-test/deep?branch=master) [![GoDoc](https://godoc.org/github.com/go-test/deep?status.svg)](https://godoc.org/github.com/go-test/deep) + +This package provides a single function: `deep.Equal`. It's like [reflect.DeepEqual](http://golang.org/pkg/reflect/#DeepEqual) but much friendlier to humans (or any sentient being) for two reason: + +* `deep.Equal` returns a list of differences +* `deep.Equal` does not compare unexported fields (by default) + +`reflect.DeepEqual` is good (like all things Golang!), but it's a game of [Hunt the Wumpus](https://en.wikipedia.org/wiki/Hunt_the_Wumpus). For large maps, slices, and structs, finding the difference is difficult. + +`deep.Equal` doesn't play games with you, it lists the differences: + +```go +package main_test + +import ( + "testing" + "github.com/go-test/deep" +) + +type T struct { + Name string + Numbers []float64 +} + +func TestDeepEqual(t *testing.T) { + // Can you spot the difference? + t1 := T{ + Name: "Isabella", + Numbers: []float64{1.13459, 2.29343, 3.010100010}, + } + t2 := T{ + Name: "Isabella", + Numbers: []float64{1.13459, 2.29843, 3.010100010}, + } + + if diff := deep.Equal(t1, t2); diff != nil { + t.Error(diff) + } +} +``` + + +``` +$ go test +--- FAIL: TestDeepEqual (0.00s) + main_test.go:25: [Numbers.slice[1]: 2.29343 != 2.29843] +``` + +The difference is in `Numbers.slice[1]`: the two values aren't equal using Go `==`. diff --git a/vendor/github.com/go-test/deep/deep.go b/vendor/github.com/go-test/deep/deep.go new file mode 100644 index 00000000..7f878951 --- /dev/null +++ b/vendor/github.com/go-test/deep/deep.go @@ -0,0 +1,376 @@ +// Package deep provides function deep.Equal which is like reflect.DeepEqual but +// returns a list of differences. This is helpful when comparing complex types +// like structures and maps. +package deep + +import ( + "errors" + "fmt" + "log" + "reflect" + "strings" +) + +var ( + // FloatPrecision is the number of decimal places to round float values + // to when comparing. + FloatPrecision = 10 + + // MaxDiff specifies the maximum number of differences to return. + MaxDiff = 10 + + // MaxDepth specifies the maximum levels of a struct to recurse into, + // if greater than zero. If zero, there is no limit. + MaxDepth = 0 + + // LogErrors causes errors to be logged to STDERR when true. + LogErrors = false + + // CompareUnexportedFields causes unexported struct fields, like s in + // T{s int}, to be compared when true. + CompareUnexportedFields = false +) + +var ( + // ErrMaxRecursion is logged when MaxDepth is reached. + ErrMaxRecursion = errors.New("recursed to MaxDepth") + + // ErrTypeMismatch is logged when Equal passed two different types of values. + ErrTypeMismatch = errors.New("variables are different reflect.Type") + + // ErrNotHandled is logged when a primitive Go kind is not handled. + ErrNotHandled = errors.New("cannot compare the reflect.Kind") +) + +type cmp struct { + diff []string + buff []string + floatFormat string +} + +var errorType = reflect.TypeOf((*error)(nil)).Elem() + +// Equal compares variables a and b, recursing into their structure up to +// MaxDepth levels deep (if greater than zero), and returns a list of differences, +// or nil if there are none. Some differences may not be found if an error is +// also returned. +// +// If a type has an Equal method, like time.Equal, it is called to check for +// equality. +// +// When comparing a struct, if a field has the tag `deep:"-"` then it will be +// ignored. +func Equal(a, b interface{}) []string { + aVal := reflect.ValueOf(a) + bVal := reflect.ValueOf(b) + c := &cmp{ + diff: []string{}, + buff: []string{}, + floatFormat: fmt.Sprintf("%%.%df", FloatPrecision), + } + if a == nil && b == nil { + return nil + } else if a == nil && b != nil { + c.saveDiff("", b) + } else if a != nil && b == nil { + c.saveDiff(a, "") + } + if len(c.diff) > 0 { + return c.diff + } + + c.equals(aVal, bVal, 0) + if len(c.diff) > 0 { + return c.diff // diffs + } + return nil // no diffs +} + +func (c *cmp) equals(a, b reflect.Value, level int) { + if MaxDepth > 0 && level > MaxDepth { + logError(ErrMaxRecursion) + return + } + + // Check if one value is nil, e.g. T{x: *X} and T.x is nil + if !a.IsValid() || !b.IsValid() { + if a.IsValid() && !b.IsValid() { + c.saveDiff(a.Type(), "") + } else if !a.IsValid() && b.IsValid() { + c.saveDiff("", b.Type()) + } + return + } + + // If different types, they can't be equal + aType := a.Type() + bType := b.Type() + if aType != bType { + c.saveDiff(aType, bType) + logError(ErrTypeMismatch) + return + } + + // Primitive https://golang.org/pkg/reflect/#Kind + aKind := a.Kind() + bKind := b.Kind() + + // Do a and b have underlying elements? Yes if they're ptr or interface. + aElem := aKind == reflect.Ptr || aKind == reflect.Interface + bElem := bKind == reflect.Ptr || bKind == reflect.Interface + + // If both types implement the error interface, compare the error strings. + // This must be done before dereferencing because the interface is on a + // pointer receiver. Re https://github.com/go-test/deep/issues/31, a/b might + // be primitive kinds; see TestErrorPrimitiveKind. + if aType.Implements(errorType) && bType.Implements(errorType) { + if (!aElem || !a.IsNil()) && (!bElem || !b.IsNil()) { + aString := a.MethodByName("Error").Call(nil)[0].String() + bString := b.MethodByName("Error").Call(nil)[0].String() + if aString != bString { + c.saveDiff(aString, bString) + return + } + } + } + + // Dereference pointers and interface{} + if aElem || bElem { + if aElem { + a = a.Elem() + } + if bElem { + b = b.Elem() + } + c.equals(a, b, level+1) + return + } + + switch aKind { + + ///////////////////////////////////////////////////////////////////// + // Iterable kinds + ///////////////////////////////////////////////////////////////////// + + case reflect.Struct: + /* + The variables are structs like: + type T struct { + FirstName string + LastName string + } + Type = .T, Kind = reflect.Struct + + Iterate through the fields (FirstName, LastName), recurse into their values. + */ + + // Types with an Equal() method, like time.Time, only if struct field + // is exported (CanInterface) + if eqFunc := a.MethodByName("Equal"); eqFunc.IsValid() && eqFunc.CanInterface() { + // Handle https://github.com/go-test/deep/issues/15: + // Don't call T.Equal if the method is from an embedded struct, like: + // type Foo struct { time.Time } + // First, we'll encounter Equal(Ttime, time.Time) but if we pass b + // as the 2nd arg we'll panic: "Call using pkg.Foo as type time.Time" + // As far as I can tell, there's no way to see that the method is from + // time.Time not Foo. So we check the type of the 1st (0) arg and skip + // unless it's b type. Later, we'll encounter the time.Time anonymous/ + // embedded field and then we'll have Equal(time.Time, time.Time). + funcType := eqFunc.Type() + if funcType.NumIn() == 1 && funcType.In(0) == bType { + retVals := eqFunc.Call([]reflect.Value{b}) + if !retVals[0].Bool() { + c.saveDiff(a, b) + } + return + } + } + + for i := 0; i < a.NumField(); i++ { + if aType.Field(i).PkgPath != "" && !CompareUnexportedFields { + continue // skip unexported field, e.g. s in type T struct {s string} + } + + if aType.Field(i).Tag.Get("deep") == "-" { + continue // field wants to be ignored + } + + c.push(aType.Field(i).Name) // push field name to buff + + // Get the Value for each field, e.g. FirstName has Type = string, + // Kind = reflect.String. + af := a.Field(i) + bf := b.Field(i) + + // Recurse to compare the field values + c.equals(af, bf, level+1) + + c.pop() // pop field name from buff + + if len(c.diff) >= MaxDiff { + break + } + } + case reflect.Map: + /* + The variables are maps like: + map[string]int{ + "foo": 1, + "bar": 2, + } + Type = map[string]int, Kind = reflect.Map + + Or: + type T map[string]int{} + Type = .T, Kind = reflect.Map + + Iterate through the map keys (foo, bar), recurse into their values. + */ + + if a.IsNil() || b.IsNil() { + if a.IsNil() && !b.IsNil() { + c.saveDiff("", b) + } else if !a.IsNil() && b.IsNil() { + c.saveDiff(a, "") + } + return + } + + if a.Pointer() == b.Pointer() { + return + } + + for _, key := range a.MapKeys() { + c.push(fmt.Sprintf("map[%s]", key)) + + aVal := a.MapIndex(key) + bVal := b.MapIndex(key) + if bVal.IsValid() { + c.equals(aVal, bVal, level+1) + } else { + c.saveDiff(aVal, "") + } + + c.pop() + + if len(c.diff) >= MaxDiff { + return + } + } + + for _, key := range b.MapKeys() { + if aVal := a.MapIndex(key); aVal.IsValid() { + continue + } + + c.push(fmt.Sprintf("map[%s]", key)) + c.saveDiff("", b.MapIndex(key)) + c.pop() + if len(c.diff) >= MaxDiff { + return + } + } + case reflect.Array: + n := a.Len() + for i := 0; i < n; i++ { + c.push(fmt.Sprintf("array[%d]", i)) + c.equals(a.Index(i), b.Index(i), level+1) + c.pop() + if len(c.diff) >= MaxDiff { + break + } + } + case reflect.Slice: + if a.IsNil() || b.IsNil() { + if a.IsNil() && !b.IsNil() { + c.saveDiff("", b) + } else if !a.IsNil() && b.IsNil() { + c.saveDiff(a, "") + } + return + } + + aLen := a.Len() + bLen := b.Len() + + if a.Pointer() == b.Pointer() && aLen == bLen { + return + } + + n := aLen + if bLen > aLen { + n = bLen + } + for i := 0; i < n; i++ { + c.push(fmt.Sprintf("slice[%d]", i)) + if i < aLen && i < bLen { + c.equals(a.Index(i), b.Index(i), level+1) + } else if i < aLen { + c.saveDiff(a.Index(i), "") + } else { + c.saveDiff("", b.Index(i)) + } + c.pop() + if len(c.diff) >= MaxDiff { + break + } + } + + ///////////////////////////////////////////////////////////////////// + // Primitive kinds + ///////////////////////////////////////////////////////////////////// + + case reflect.Float32, reflect.Float64: + // Avoid 0.04147685731961082 != 0.041476857319611 + // 6 decimal places is close enough + aval := fmt.Sprintf(c.floatFormat, a.Float()) + bval := fmt.Sprintf(c.floatFormat, b.Float()) + if aval != bval { + c.saveDiff(a.Float(), b.Float()) + } + case reflect.Bool: + if a.Bool() != b.Bool() { + c.saveDiff(a.Bool(), b.Bool()) + } + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + if a.Int() != b.Int() { + c.saveDiff(a.Int(), b.Int()) + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + if a.Uint() != b.Uint() { + c.saveDiff(a.Uint(), b.Uint()) + } + case reflect.String: + if a.String() != b.String() { + c.saveDiff(a.String(), b.String()) + } + + default: + logError(ErrNotHandled) + } +} + +func (c *cmp) push(name string) { + c.buff = append(c.buff, name) +} + +func (c *cmp) pop() { + if len(c.buff) > 0 { + c.buff = c.buff[0 : len(c.buff)-1] + } +} + +func (c *cmp) saveDiff(aval, bval interface{}) { + if len(c.buff) > 0 { + varName := strings.Join(c.buff, ".") + c.diff = append(c.diff, fmt.Sprintf("%s: %v != %v", varName, aval, bval)) + } else { + c.diff = append(c.diff, fmt.Sprintf("%v != %v", aval, bval)) + } +} + +func logError(err error) { + if LogErrors { + log.Println(err) + } +} diff --git a/vendor/github.com/go-test/deep/go.mod b/vendor/github.com/go-test/deep/go.mod new file mode 100644 index 00000000..6e8ca1d2 --- /dev/null +++ b/vendor/github.com/go-test/deep/go.mod @@ -0,0 +1 @@ +module github.com/go-test/deep diff --git a/vendor/modules.txt b/vendor/modules.txt index 52350d7e..399116c1 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -109,6 +109,8 @@ github.com/docker/go-connections/tlsconfig github.com/docker/go-events # github.com/docker/go-units v0.4.0 github.com/docker/go-units +# github.com/go-test/deep v1.0.4 +github.com/go-test/deep # github.com/gogo/googleapis v1.3.0 github.com/gogo/googleapis/google/rpc # github.com/gogo/protobuf v1.3.0