restructure docker runtime pkg
This commit is contained in:
parent
9323179a3c
commit
03eaba037f
@ -26,47 +26,35 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types/filters"
|
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"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"
|
"github.com/docker/docker/client"
|
||||||
k3d "github.com/rancher/k3d/pkg/types"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CreateNode creates a new container
|
// createContainer creates a new docker container from translated specs
|
||||||
func (d Docker) CreateNode(node *k3d.Node) error {
|
func createContainer(dockerNode *NodeInDocker, name string) error {
|
||||||
log.Debugln("docker.CreateNode...")
|
|
||||||
ctx := context.Background() // TODO: check how kind handles contexts
|
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())
|
docker, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to create docker client. %+v", err)
|
log.Errorln("Failed to create docker client")
|
||||||
}
|
|
||||||
|
|
||||||
containerConfig := container.Config{
|
|
||||||
Cmd: node.Args,
|
|
||||||
Image: node.Image,
|
|
||||||
Labels: node.Labels,
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := docker.ContainerCreate(ctx, &containerConfig, &container.HostConfig{}, &network.NetworkingConfig{}, node.Name)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Couldn't create container")
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Infoln("Created", resp.ID)
|
|
||||||
|
// 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.Errorln("Failed to create container")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Infoln("Created container", resp.ID)
|
||||||
|
|
||||||
return nil
|
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)
|
// removeContainer deletes a running container (like docker rm -f)
|
||||||
func removeContainer(ID string) error {
|
func removeContainer(ID string) error {
|
||||||
|
|
||||||
@ -92,51 +80,3 @@ func removeContainer(ID string) error {
|
|||||||
|
|
||||||
return nil
|
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
|
|
||||||
|
|
||||||
}
|
|
||||||
|
104
pkg/runtimes/docker/node.go
Normal file
104
pkg/runtimes/docker/node.go
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2019 Thorsten Klein <iwilltry42@gmail.com>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
}
|
2
vendor/github.com/go-test/deep/.gitignore
generated
vendored
Normal file
2
vendor/github.com/go-test/deep/.gitignore
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*.swp
|
||||||
|
*.out
|
13
vendor/github.com/go-test/deep/.travis.yml
generated
vendored
Normal file
13
vendor/github.com/go-test/deep/.travis.yml
generated
vendored
Normal file
@ -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
|
30
vendor/github.com/go-test/deep/CHANGES.md
generated
vendored
Normal file
30
vendor/github.com/go-test/deep/CHANGES.md
generated
vendored
Normal file
@ -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
|
21
vendor/github.com/go-test/deep/LICENSE
generated
vendored
Normal file
21
vendor/github.com/go-test/deep/LICENSE
generated
vendored
Normal file
@ -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.
|
51
vendor/github.com/go-test/deep/README.md
generated
vendored
Normal file
51
vendor/github.com/go-test/deep/README.md
generated
vendored
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# Deep Variable Equality for Humans
|
||||||
|
|
||||||
|
[](https://goreportcard.com/report/github.com/go-test/deep) [](https://travis-ci.org/go-test/deep) [](https://coveralls.io/github/go-test/deep?branch=master) [](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 `==`.
|
376
vendor/github.com/go-test/deep/deep.go
generated
vendored
Normal file
376
vendor/github.com/go-test/deep/deep.go
generated
vendored
Normal file
@ -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("<nil pointer>", b)
|
||||||
|
} else if a != nil && b == nil {
|
||||||
|
c.saveDiff(a, "<nil pointer>")
|
||||||
|
}
|
||||||
|
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(), "<nil pointer>")
|
||||||
|
} else if !a.IsValid() && b.IsValid() {
|
||||||
|
c.saveDiff("<nil pointer>", 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 = <pkg>.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 = <pkg>.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("<nil map>", b)
|
||||||
|
} else if !a.IsNil() && b.IsNil() {
|
||||||
|
c.saveDiff(a, "<nil map>")
|
||||||
|
}
|
||||||
|
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, "<does not have key>")
|
||||||
|
}
|
||||||
|
|
||||||
|
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("<does not have key>", 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("<nil slice>", b)
|
||||||
|
} else if !a.IsNil() && b.IsNil() {
|
||||||
|
c.saveDiff(a, "<nil slice>")
|
||||||
|
}
|
||||||
|
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), "<no value>")
|
||||||
|
} else {
|
||||||
|
c.saveDiff("<no value>", 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)
|
||||||
|
}
|
||||||
|
}
|
1
vendor/github.com/go-test/deep/go.mod
generated
vendored
Normal file
1
vendor/github.com/go-test/deep/go.mod
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
module github.com/go-test/deep
|
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
@ -109,6 +109,8 @@ github.com/docker/go-connections/tlsconfig
|
|||||||
github.com/docker/go-events
|
github.com/docker/go-events
|
||||||
# github.com/docker/go-units v0.4.0
|
# github.com/docker/go-units v0.4.0
|
||||||
github.com/docker/go-units
|
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 v1.3.0
|
||||||
github.com/gogo/googleapis/google/rpc
|
github.com/gogo/googleapis/google/rpc
|
||||||
# github.com/gogo/protobuf v1.3.0
|
# github.com/gogo/protobuf v1.3.0
|
||||||
|
Loading…
Reference in New Issue
Block a user