Refined engine implementations

Adapt the CLI to the host install model for 18.09.

Signed-off-by: Daniel Hiltgen <daniel.hiltgen@docker.com>
This commit is contained in:
Daniel Hiltgen
2018-09-14 10:53:56 -07:00
parent cfec8027ed
commit 342afe44fb
19 changed files with 482 additions and 438 deletions

View File

@ -12,7 +12,6 @@ import (
"github.com/docker/licensing/model"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"golang.org/x/sys/unix"
)
type activateOptions struct {
@ -68,7 +67,7 @@ https://hub.docker.com/ then specify the file with the '--license' flag.
}
func runActivate(cli command.Cli, options activateOptions) error {
if unix.Geteuid() != 0 {
if !isRoot() {
return errors.New("must be privileged to activate engine")
}
ctx := context.Background()
@ -108,12 +107,17 @@ func runActivate(cli command.Cli, options activateOptions) error {
EngineVersion: options.version,
}
return client.ActivateEngine(ctx, opts, cli.Out(), authConfig,
err = client.ActivateEngine(ctx, opts, cli.Out(), authConfig,
func(ctx context.Context) error {
client := cli.Client()
_, err := client.Ping(ctx)
return err
})
if err != nil {
return err
}
fmt.Fprintln(cli.Out(), "To complete the activation, please restart docker with 'systemctl restart docker'")
return nil
}
func getLicenses(ctx context.Context, authConfig *types.AuthConfig, cli command.Cli, options activateOptions) (*model.IssuedLicense, error) {

View File

@ -14,6 +14,7 @@ func TestActivateNoContainerd(t *testing.T) {
return nil, fmt.Errorf("some error")
},
)
isRoot = func() bool { return true }
cmd := newActivateCommand(testCli)
cmd.Flags().Set("license", "invalidpath")
cmd.SilenceUsage = true
@ -28,6 +29,7 @@ func TestActivateBadLicense(t *testing.T) {
return &fakeContainerizedEngineClient{}, nil
},
)
isRoot = func() bool { return true }
cmd := newActivateCommand(testCli)
cmd.SilenceUsage = true
cmd.SilenceErrors = true

View File

@ -0,0 +1,13 @@
// +build !windows
package engine
import (
"golang.org/x/sys/unix"
)
var (
isRoot = func() bool {
return unix.Geteuid() == 0
}
)

View File

@ -0,0 +1,9 @@
// +build windows
package engine
var (
isRoot = func() bool {
return true
}
)

View File

@ -1,14 +1,16 @@
package engine
import (
"context"
"fmt"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/formatter"
"github.com/docker/cli/internal/versions"
clitypes "github.com/docker/cli/types"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"golang.org/x/sys/unix"
)
const (
@ -37,7 +39,7 @@ func newCheckForUpdatesCommand(dockerCli command.Cli) *cobra.Command {
},
}
flags := cmd.Flags()
flags.StringVar(&options.registryPrefix, "registry-prefix", "", "Override the existing location where engine images are pulled")
flags.StringVar(&options.registryPrefix, "registry-prefix", "docker.io/store/docker", "Override the existing location where engine images are pulled")
flags.BoolVar(&options.downgrades, "downgrades", false, "Report downgrades (default omits older versions)")
flags.BoolVar(&options.preReleases, "pre-releases", false, "Include pre-release versions")
flags.BoolVar(&options.upgrades, "upgrades", true, "Report available upgrades")
@ -49,70 +51,67 @@ func newCheckForUpdatesCommand(dockerCli command.Cli) *cobra.Command {
}
func runCheck(dockerCli command.Cli, options checkOptions) error {
if unix.Geteuid() != 0 {
if !isRoot() {
return errors.New("must be privileged to activate engine")
}
ctx := context.Background()
client := dockerCli.Client()
serverVersion, err := client.ServerVersion(ctx)
if err != nil {
return err
}
/*
ctx := context.Background()
client, err := dockerCli.NewContainerizedEngineClient(options.sockPath)
if err != nil {
return errors.Wrap(err, "unable to access local containerd")
}
defer client.Close()
versions, err := client.GetEngineVersions(ctx, dockerCli.RegistryClient(false), currentVersion, imageName)
if err != nil {
return err
}
availVersions, err := versions.GetEngineVersions(ctx, dockerCli.RegistryClient(false), options.registryPrefix, serverVersion)
if err != nil {
return err
}
availUpdates := []clitypes.Update{
{Type: "current", Version: currentVersion},
}
if len(versions.Patches) > 0 {
availUpdates = append(availUpdates,
processVersions(
currentVersion,
"patch",
options.preReleases,
versions.Patches)...)
}
if options.upgrades {
availUpdates = append(availUpdates,
processVersions(
currentVersion,
"upgrade",
options.preReleases,
versions.Upgrades)...)
}
if options.downgrades {
availUpdates = append(availUpdates,
processVersions(
currentVersion,
"downgrade",
options.preReleases,
versions.Downgrades)...)
}
availUpdates := []clitypes.Update{
{Type: "current", Version: serverVersion.Version},
}
if len(availVersions.Patches) > 0 {
availUpdates = append(availUpdates,
processVersions(
serverVersion.Version,
"patch",
options.preReleases,
availVersions.Patches)...)
}
if options.upgrades {
availUpdates = append(availUpdates,
processVersions(
serverVersion.Version,
"upgrade",
options.preReleases,
availVersions.Upgrades)...)
}
if options.downgrades {
availUpdates = append(availUpdates,
processVersions(
serverVersion.Version,
"downgrade",
options.preReleases,
availVersions.Downgrades)...)
}
format := options.format
if len(format) == 0 {
format = formatter.TableFormatKey
}
format := options.format
if len(format) == 0 {
format = formatter.TableFormatKey
}
updatesCtx := formatter.Context{
Output: dockerCli.Out(),
Format: formatter.NewUpdatesFormat(format, options.quiet),
Trunc: false,
}
return formatter.UpdatesWrite(updatesCtx, availUpdates)
*/
return nil
updatesCtx := formatter.Context{
Output: dockerCli.Out(),
Format: formatter.NewUpdatesFormat(format, options.quiet),
Trunc: false,
}
return formatter.UpdatesWrite(updatesCtx, availUpdates)
}
func processVersions(currentVersion, verType string,
includePrerelease bool,
versions []clitypes.DockerVersion) []clitypes.Update {
availVersions []clitypes.DockerVersion) []clitypes.Update {
availUpdates := []clitypes.Update{}
for _, ver := range versions {
for _, ver := range availVersions {
if !includePrerelease && ver.Prerelease() != "" {
continue
}

View File

@ -5,11 +5,13 @@ import (
"fmt"
"testing"
registryclient "github.com/docker/cli/cli/registry/client"
manifesttypes "github.com/docker/cli/cli/manifest/types"
"github.com/docker/cli/internal/test"
clitypes "github.com/docker/cli/types"
"github.com/docker/distribution"
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
ver "github.com/hashicorp/go-version"
"github.com/opencontainers/go-digest"
"gotest.tools/assert"
"gotest.tools/golden"
)
@ -18,126 +20,87 @@ var (
testCli = test.NewFakeCli(&client.Client{})
)
func TestCheckForUpdatesNoContainerd(t *testing.T) {
testCli.SetContainerizedEngineClient(
func(string) (clitypes.ContainerizedClient, error) {
return nil, fmt.Errorf("some error")
},
)
cmd := newCheckForUpdatesCommand(testCli)
cmd.SilenceUsage = true
cmd.SilenceErrors = true
err := cmd.Execute()
assert.ErrorContains(t, err, "unable to access local containerd")
type verClient struct {
client.Client
ver types.Version
verErr error
}
func (c *verClient) ServerVersion(ctx context.Context) (types.Version, error) {
return c.ver, c.verErr
}
type testRegistryClient struct {
tags []string
}
func (c testRegistryClient) GetManifest(ctx context.Context, ref reference.Named) (manifesttypes.ImageManifest, error) {
return manifesttypes.ImageManifest{}, nil
}
func (c testRegistryClient) GetManifestList(ctx context.Context, ref reference.Named) ([]manifesttypes.ImageManifest, error) {
return nil, nil
}
func (c testRegistryClient) MountBlob(ctx context.Context, source reference.Canonical, target reference.Named) error {
return nil
}
func (c testRegistryClient) PutManifest(ctx context.Context, ref reference.Named, manifest distribution.Manifest) (digest.Digest, error) {
return "", nil
}
func (c testRegistryClient) GetTags(ctx context.Context, ref reference.Named) ([]string, error) {
return c.tags, nil
}
func TestCheckForUpdatesNoCurrentVersion(t *testing.T) {
retErr := fmt.Errorf("some failure")
getCurrentEngineVersionFunc := func(ctx context.Context) (clitypes.EngineInitOptions, error) {
return clitypes.EngineInitOptions{}, retErr
}
testCli.SetContainerizedEngineClient(
func(string) (clitypes.ContainerizedClient, error) {
return &fakeContainerizedEngineClient{
getCurrentEngineVersionFunc: getCurrentEngineVersionFunc,
}, nil
},
)
cmd := newCheckForUpdatesCommand(testCli)
isRoot = func() bool { return true }
c := test.NewFakeCli(&verClient{client.Client{}, types.Version{}, nil})
c.SetRegistryClient(testRegistryClient{})
cmd := newCheckForUpdatesCommand(c)
cmd.SilenceUsage = true
cmd.SilenceErrors = true
err := cmd.Execute()
assert.Assert(t, err == retErr)
}
func TestCheckForUpdatesGetEngineVersionsFail(t *testing.T) {
retErr := fmt.Errorf("some failure")
getEngineVersionsFunc := func(ctx context.Context,
registryClient registryclient.RegistryClient,
currentVersion, imageName string) (clitypes.AvailableVersions, error) {
return clitypes.AvailableVersions{}, retErr
}
testCli.SetContainerizedEngineClient(
func(string) (clitypes.ContainerizedClient, error) {
return &fakeContainerizedEngineClient{
getEngineVersionsFunc: getEngineVersionsFunc,
}, nil
},
)
cmd := newCheckForUpdatesCommand(testCli)
cmd.SilenceUsage = true
cmd.SilenceErrors = true
err := cmd.Execute()
assert.Assert(t, err == retErr)
assert.ErrorContains(t, err, "alformed version")
}
func TestCheckForUpdatesGetEngineVersionsHappy(t *testing.T) {
getCurrentEngineVersionFunc := func(ctx context.Context) (clitypes.EngineInitOptions, error) {
return clitypes.EngineInitOptions{
EngineImage: "current engine",
EngineVersion: "1.1.0",
}, nil
}
getEngineVersionsFunc := func(ctx context.Context,
registryClient registryclient.RegistryClient,
currentVersion, imageName string) (clitypes.AvailableVersions, error) {
return clitypes.AvailableVersions{
Downgrades: parseVersions(t, "1.0.1", "1.0.2", "1.0.3-beta1"),
Patches: parseVersions(t, "1.1.1", "1.1.2", "1.1.3-beta1"),
Upgrades: parseVersions(t, "1.2.0", "2.0.0", "2.1.0-beta1"),
}, nil
}
testCli.SetContainerizedEngineClient(
func(string) (clitypes.ContainerizedClient, error) {
return &fakeContainerizedEngineClient{
getEngineVersionsFunc: getEngineVersionsFunc,
getCurrentEngineVersionFunc: getCurrentEngineVersionFunc,
}, nil
},
)
cmd := newCheckForUpdatesCommand(testCli)
c := test.NewFakeCli(&verClient{client.Client{}, types.Version{Version: "1.1.0"}, nil})
c.SetRegistryClient(testRegistryClient{[]string{
"1.0.1", "1.0.2", "1.0.3-beta1",
"1.1.1", "1.1.2", "1.1.3-beta1",
"1.2.0", "2.0.0", "2.1.0-beta1",
}})
isRoot = func() bool { return true }
cmd := newCheckForUpdatesCommand(c)
cmd.Flags().Set("pre-releases", "true")
cmd.Flags().Set("downgrades", "true")
cmd.SilenceUsage = true
cmd.SilenceErrors = true
err := cmd.Execute()
assert.NilError(t, err)
golden.Assert(t, testCli.OutBuffer().String(), "check-all.golden")
golden.Assert(t, c.OutBuffer().String(), "check-all.golden")
testCli.OutBuffer().Reset()
c.OutBuffer().Reset()
cmd.Flags().Set("pre-releases", "false")
cmd.Flags().Set("downgrades", "true")
err = cmd.Execute()
assert.NilError(t, err)
fmt.Println(testCli.OutBuffer().String())
golden.Assert(t, testCli.OutBuffer().String(), "check-no-prerelease.golden")
fmt.Println(c.OutBuffer().String())
golden.Assert(t, c.OutBuffer().String(), "check-no-prerelease.golden")
testCli.OutBuffer().Reset()
c.OutBuffer().Reset()
cmd.Flags().Set("pre-releases", "false")
cmd.Flags().Set("downgrades", "false")
err = cmd.Execute()
assert.NilError(t, err)
fmt.Println(testCli.OutBuffer().String())
golden.Assert(t, testCli.OutBuffer().String(), "check-no-downgrades.golden")
fmt.Println(c.OutBuffer().String())
golden.Assert(t, c.OutBuffer().String(), "check-no-downgrades.golden")
testCli.OutBuffer().Reset()
c.OutBuffer().Reset()
cmd.Flags().Set("pre-releases", "false")
cmd.Flags().Set("downgrades", "false")
cmd.Flags().Set("upgrades", "false")
err = cmd.Execute()
assert.NilError(t, err)
fmt.Println(testCli.OutBuffer().String())
golden.Assert(t, testCli.OutBuffer().String(), "check-patches-only.golden")
}
func makeVersion(t *testing.T, tag string) clitypes.DockerVersion {
v, err := ver.NewVersion(tag)
assert.NilError(t, err)
return clitypes.DockerVersion{Version: *v, Tag: tag}
}
func parseVersions(t *testing.T, tags ...string) []clitypes.DockerVersion {
ret := make([]clitypes.DockerVersion, len(tags))
for i, tag := range tags {
ret[i] = makeVersion(t, tag)
}
return ret
fmt.Println(c.OutBuffer().String())
golden.Assert(t, c.OutBuffer().String(), "check-patches-only.golden")
}

View File

@ -10,5 +10,5 @@ func TestNewEngineCommand(t *testing.T) {
cmd := NewEngineCommand(testCli)
subcommands := cmd.Commands()
assert.Assert(t, len(subcommands) == 5)
assert.Assert(t, len(subcommands) == 3)
}

View File

@ -1,62 +1,10 @@
package engine
import (
"context"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
clitypes "github.com/docker/cli/types"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
type extendedEngineInitOptions struct {
clitypes.EngineInitOptions
sockPath string
}
func newInitCommand(dockerCli command.Cli) *cobra.Command {
var options extendedEngineInitOptions
cmd := &cobra.Command{
Use: "init [OPTIONS]",
Short: "Initialize a local engine",
Long: `This command will initialize a local engine running on containerd.
Configuration of the engine is managed through the daemon.json configuration
file on the host and may be pre-created before running the 'init' command.
`,
Args: cli.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
return runInit(dockerCli, options)
},
Annotations: map[string]string{"experimentalCLI": ""},
}
flags := cmd.Flags()
flags.StringVar(&options.EngineVersion, "version", cli.Version, "Specify engine version")
flags.StringVar(&options.EngineImage, "engine-image", clitypes.CommunityEngineImage, "Specify engine image")
flags.StringVar(&options.RegistryPrefix, "registry-prefix", "docker.io/store/docker", "Override the default location where engine images are pulled")
flags.StringVar(&options.ConfigFile, "config-file", "/etc/docker/daemon.json", "Specify the location of the daemon configuration file on the host")
flags.StringVar(&options.sockPath, "containerd", "", "override default location of containerd endpoint")
return cmd
}
func runInit(dockerCli command.Cli, options extendedEngineInitOptions) error {
ctx := context.Background()
client, err := dockerCli.NewContainerizedEngineClient(options.sockPath)
if err != nil {
return errors.Wrap(err, "unable to access local containerd")
}
defer client.Close()
authConfig, err := getRegistryAuth(dockerCli, options.RegistryPrefix)
if err != nil {
return err
}
return client.InitEngine(ctx, options.EngineInitOptions, dockerCli.Out(), authConfig,
func(ctx context.Context) error {
client := dockerCli.Client()
_, err := client.Ping(ctx)
return err
})
}

View File

@ -1,33 +0,0 @@
package engine
import (
"fmt"
"testing"
clitypes "github.com/docker/cli/types"
"gotest.tools/assert"
)
func TestInitNoContainerd(t *testing.T) {
testCli.SetContainerizedEngineClient(
func(string) (clitypes.ContainerizedClient, error) {
return nil, fmt.Errorf("some error")
},
)
cmd := newInitCommand(testCli)
cmd.SilenceUsage = true
cmd.SilenceErrors = true
err := cmd.Execute()
assert.ErrorContains(t, err, "unable to access local containerd")
}
func TestInitHappy(t *testing.T) {
testCli.SetContainerizedEngineClient(
func(string) (clitypes.ContainerizedClient, error) {
return &fakeContainerizedEngineClient{}, nil
},
)
cmd := newInitCommand(testCli)
err := cmd.Execute()
assert.NilError(t, err)
}

View File

@ -8,7 +8,6 @@ import (
"github.com/docker/cli/cli/command"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"golang.org/x/sys/unix"
)
func newUpdateCommand(dockerCli command.Cli) *cobra.Command {
@ -33,7 +32,7 @@ func newUpdateCommand(dockerCli command.Cli) *cobra.Command {
}
func runUpdate(dockerCli command.Cli, options extendedEngineInitOptions) error {
if unix.Geteuid() != 0 {
if !isRoot() {
return errors.New("must be privileged to activate engine")
}
ctx := context.Background()
@ -43,11 +42,8 @@ func runUpdate(dockerCli command.Cli, options extendedEngineInitOptions) error {
}
defer client.Close()
if options.EngineImage == "" || options.RegistryPrefix == "" {
if options.EngineImage == "" {
options.EngineImage = "docker/engine-community"
}
if options.RegistryPrefix == "" {
options.RegistryPrefix = "docker.io"
options.RegistryPrefix = "docker.io/store/docker"
}
}
authConfig, err := getRegistryAuth(dockerCli, options.RegistryPrefix)
@ -63,6 +59,6 @@ func runUpdate(dockerCli command.Cli, options extendedEngineInitOptions) error {
}); err != nil {
return err
}
fmt.Fprintln(dockerCli.Out(), "Success! The docker engine is now running.")
fmt.Fprintln(dockerCli.Out(), "To complete the update, please restart docker with 'systemctl restart docker'")
return nil
}