kubeadm: don't use the Docker SDK in util/system/docker_validator*

Instead of creating a Docker client and fetching an Info object
from the docker enpoint, call the "docker info" command
and populate a local dockerInfo struct from JSON output.

Also
- add unit tests.
- update import boss and bazel.

This change affects "test/e2e_node/e2e_node_suite_test.go"
as it consumes this Docker validator by calling
"system.ValidateSpec()".
This commit is contained in:
Lubomir I. Ivanov 2019-06-27 02:24:25 +03:00
parent d48f123a40
commit cb56f91cc6
4 changed files with 75 additions and 31 deletions

View File

@ -112,15 +112,7 @@
"github.com/coreos/etcd/pkg/tlsutil", "github.com/coreos/etcd/pkg/tlsutil",
"github.com/coreos/etcd/pkg/transport", "github.com/coreos/etcd/pkg/transport",
"github.com/davecgh/go-spew/spew", "github.com/davecgh/go-spew/spew",
"github.com/docker/distribution/digestset",
"github.com/docker/distribution/reference", "github.com/docker/distribution/reference",
"github.com/docker/docker/api",
"github.com/docker/docker/client",
"github.com/docker/docker/pkg/term",
"github.com/docker/go-connections/nat",
"github.com/docker/go-connections/sockets",
"github.com/docker/go-connections/tlsconfig",
"github.com/docker/go-units",
"github.com/docker/libnetwork/ipvs", "github.com/docker/libnetwork/ipvs",
"github.com/godbus/dbus", "github.com/godbus/dbus",
"github.com/gogo/protobuf/proto", "github.com/gogo/protobuf/proto",

View File

@ -25,8 +25,6 @@ go_library(
deps = [ deps = [
"//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library",
"//vendor/github.com/blang/semver:go_default_library", "//vendor/github.com/blang/semver:go_default_library",
"//vendor/github.com/docker/docker/api/types:go_default_library",
"//vendor/github.com/docker/docker/client:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library", "//vendor/github.com/pkg/errors:go_default_library",
"//vendor/k8s.io/klog:go_default_library", "//vendor/k8s.io/klog:go_default_library",
], ],
@ -44,7 +42,6 @@ go_test(
embed = [":go_default_library"], embed = [":go_default_library"],
tags = ["e2e"], tags = ["e2e"],
deps = [ deps = [
"//vendor/github.com/docker/docker/api/types:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library", "//vendor/github.com/pkg/errors:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library", "//vendor/github.com/stretchr/testify/assert:go_default_library",
], ],

View File

@ -17,11 +17,10 @@ limitations under the License.
package system package system
import ( import (
"context" "encoding/json"
"os/exec"
"regexp" "regexp"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -32,6 +31,14 @@ type DockerValidator struct {
Reporter Reporter Reporter Reporter
} }
// dockerInfo holds a local subset of the Info struct from
// github.com/docker/docker/api/types.
// The JSON output from 'docker info' should map to this struct.
type dockerInfo struct {
Driver string `json:"Driver"`
ServerVersion string `json:"ServerVersion"`
}
// Name is part of the system.Validator interface. // Name is part of the system.Validator interface.
func (d *DockerValidator) Name() string { func (d *DockerValidator) Name() string {
return "docker" return "docker"
@ -51,18 +58,28 @@ func (d *DockerValidator) Validate(spec SysSpec) (error, error) {
return nil, nil return nil, nil
} }
c, err := client.NewClient(dockerEndpoint, "", nil, nil) // Run 'docker info' with a JSON output and unmarshal it into a dockerInfo object
info := dockerInfo{}
out, err := exec.Command("docker", "info", "--format", "{{json .}}").CombinedOutput()
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to create docker client") return nil, errors.Errorf(`failed executing "docker info --format '{{json .}}'"\noutput: %s\nerror: %v`, string(out), err)
} }
info, err := c.Info(context.Background()) if err := d.unmarshalDockerInfo(out, &info); err != nil {
if err != nil { return nil, err
return nil, errors.Wrap(err, "failed to get docker info")
} }
// validate the resulted docker info object against the spec
return d.validateDockerInfo(spec.RuntimeSpec.DockerSpec, info) return d.validateDockerInfo(spec.RuntimeSpec.DockerSpec, info)
} }
func (d *DockerValidator) validateDockerInfo(spec *DockerSpec, info types.Info) (error, error) { func (d *DockerValidator) unmarshalDockerInfo(b []byte, info *dockerInfo) error {
if err := json.Unmarshal(b, &info); err != nil {
return errors.Wrap(err, "could not unmarshal the JSON output of 'docker info'")
}
return nil
}
func (d *DockerValidator) validateDockerInfo(spec *DockerSpec, info dockerInfo) (error, error) {
// Validate docker version. // Validate docker version.
matched := false matched := false
for _, v := range spec.Version { for _, v := range spec.Version {

View File

@ -17,9 +17,9 @@ limitations under the License.
package system package system
import ( import (
"reflect"
"testing" "testing"
"github.com/docker/docker/api/types"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -33,61 +33,61 @@ func TestValidateDockerInfo(t *testing.T) {
} }
for _, test := range []struct { for _, test := range []struct {
name string name string
info types.Info info dockerInfo
err bool err bool
warn bool warn bool
}{ }{
{ {
name: "unsupported Docker version 1.12.1", name: "unsupported Docker version 1.12.1",
info: types.Info{Driver: "driver_2", ServerVersion: "1.12.1"}, info: dockerInfo{Driver: "driver_2", ServerVersion: "1.12.1"},
err: true, err: true,
warn: false, warn: false,
}, },
{ {
name: "unsupported driver", name: "unsupported driver",
info: types.Info{Driver: "bad_driver", ServerVersion: "1.13.1"}, info: dockerInfo{Driver: "bad_driver", ServerVersion: "1.13.1"},
err: true, err: true,
warn: false, warn: false,
}, },
{ {
name: "valid Docker version 1.13.1", name: "valid Docker version 1.13.1",
info: types.Info{Driver: "driver_1", ServerVersion: "1.13.1"}, info: dockerInfo{Driver: "driver_1", ServerVersion: "1.13.1"},
err: false, err: false,
warn: false, warn: false,
}, },
{ {
name: "valid Docker version 17.03.0-ce", name: "valid Docker version 17.03.0-ce",
info: types.Info{Driver: "driver_2", ServerVersion: "17.03.0-ce"}, info: dockerInfo{Driver: "driver_2", ServerVersion: "17.03.0-ce"},
err: false, err: false,
warn: false, warn: false,
}, },
{ {
name: "valid Docker version 17.06.0-ce", name: "valid Docker version 17.06.0-ce",
info: types.Info{Driver: "driver_2", ServerVersion: "17.06.0-ce"}, info: dockerInfo{Driver: "driver_2", ServerVersion: "17.06.0-ce"},
err: false, err: false,
warn: false, warn: false,
}, },
{ {
name: "valid Docker version 17.09.0-ce", name: "valid Docker version 17.09.0-ce",
info: types.Info{Driver: "driver_2", ServerVersion: "17.09.0-ce"}, info: dockerInfo{Driver: "driver_2", ServerVersion: "17.09.0-ce"},
err: false, err: false,
warn: false, warn: false,
}, },
{ {
name: "valid Docker version 18.06.0-ce", name: "valid Docker version 18.06.0-ce",
info: types.Info{Driver: "driver_2", ServerVersion: "18.06.0-ce"}, info: dockerInfo{Driver: "driver_2", ServerVersion: "18.06.0-ce"},
err: false, err: false,
warn: false, warn: false,
}, },
{ {
name: "valid Docker version 18.09.1-ce", name: "valid Docker version 18.09.1-ce",
info: types.Info{Driver: "driver_2", ServerVersion: "18.09.1-ce"}, info: dockerInfo{Driver: "driver_2", ServerVersion: "18.09.1-ce"},
err: false, err: false,
warn: false, warn: false,
}, },
{ {
name: "Docker version 19.01.0 is not in the list of validated versions", name: "Docker version 19.01.0 is not in the list of validated versions",
info: types.Info{Driver: "driver_2", ServerVersion: "19.01.0"}, info: dockerInfo{Driver: "driver_2", ServerVersion: "19.01.0"},
err: false, err: false,
warn: true, warn: true,
}, },
@ -107,3 +107,41 @@ func TestValidateDockerInfo(t *testing.T) {
}) })
} }
} }
func TestUnmarshalDockerInfo(t *testing.T) {
v := &DockerValidator{}
testCases := []struct {
name string
input string
expectedInfo dockerInfo
expectedError bool
}{
{
name: "valid: expected dockerInfo is valid",
input: `{ "Driver":"foo", "ServerVersion":"bar" }`,
expectedInfo: dockerInfo{Driver: "foo", ServerVersion: "bar"},
},
{
name: "invalid: the JSON input is not valid",
input: `{ "Driver":"foo"`,
expectedError: true,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
var err error
info := dockerInfo{}
if err = v.unmarshalDockerInfo([]byte(tc.input), &info); (err != nil) != tc.expectedError {
t.Fatalf("failed unmarshaling; expected error: %v, got: %v, error: %v", tc.expectedError, (err != nil), err)
}
if err != nil {
return
}
if !reflect.DeepEqual(tc.expectedInfo, info) {
t.Fatalf("dockerInfo do not match, expected: %#v, got: %#v", tc.expectedInfo, info)
}
})
}
}