diff --git a/cli/command/cli.go b/cli/command/cli.go index c2f601d866..7a839e3a59 100644 --- a/cli/command/cli.go +++ b/cli/command/cli.go @@ -22,9 +22,7 @@ import ( "github.com/docker/cli/cli/streams" "github.com/docker/cli/cli/trust" "github.com/docker/cli/cli/version" - "github.com/docker/cli/internal/containerizedengine" dopts "github.com/docker/cli/opts" - clitypes "github.com/docker/cli/types" "github.com/docker/docker/api" "github.com/docker/docker/api/types" registrytypes "github.com/docker/docker/api/types/registry" @@ -61,7 +59,6 @@ type Cli interface { ManifestStore() manifeststore.Store RegistryClient(bool) registryclient.RegistryClient ContentTrustEnabled() bool - NewContainerizedEngineClient(sockPath string) (clitypes.ContainerizedClient, error) ContextStore() store.Store CurrentContext() string StackOrchestrator(flagValue string) (Orchestrator, error) @@ -71,19 +68,18 @@ type Cli interface { // DockerCli is an instance the docker command line client. // Instances of the client can be returned from NewDockerCli. type DockerCli struct { - configFile *configfile.ConfigFile - in *streams.In - out *streams.Out - err io.Writer - client client.APIClient - serverInfo ServerInfo - clientInfo *ClientInfo - contentTrust bool - newContainerizeClient func(string) (clitypes.ContainerizedClient, error) - contextStore store.Store - currentContext string - dockerEndpoint docker.Endpoint - contextStoreConfig store.Config + configFile *configfile.ConfigFile + in *streams.In + out *streams.Out + err io.Writer + client client.APIClient + serverInfo ServerInfo + clientInfo *ClientInfo + contentTrust bool + contextStore store.Store + currentContext string + dockerEndpoint docker.Endpoint + contextStoreConfig store.Config } // DefaultVersion returns api.defaultVersion or DOCKER_API_VERSION if specified. @@ -407,11 +403,6 @@ func (cli *DockerCli) NotaryClient(imgRefAndAuth trust.ImageRefAndAuth, actions return trust.GetNotaryRepository(cli.In(), cli.Out(), UserAgent(), imgRefAndAuth.RepoInfo(), imgRefAndAuth.AuthConfig(), actions...) } -// NewContainerizedEngineClient returns a containerized engine client -func (cli *DockerCli) NewContainerizedEngineClient(sockPath string) (clitypes.ContainerizedClient, error) { - return cli.newContainerizeClient(sockPath) -} - // ContextStore returns the ContextStore func (cli *DockerCli) ContextStore() store.Store { return cli.contextStore @@ -471,13 +462,12 @@ type ClientInfo struct { } // NewDockerCli returns a DockerCli instance with all operators applied on it. -// It applies by default the standard streams, the content trust from -// environment and the default containerized client constructor operations. +// It applies by default the standard streams, and the content trust from +// environment. func NewDockerCli(ops ...DockerCliOption) (*DockerCli, error) { cli := &DockerCli{} defaultOps := []DockerCliOption{ WithContentTrustFromEnv(), - WithContainerizedClient(containerizedengine.NewClient), } cli.contextStoreConfig = DefaultContextStoreConfig() ops = append(defaultOps, ops...) diff --git a/cli/command/cli_options.go b/cli/command/cli_options.go index 607cd220a3..45d0086c4f 100644 --- a/cli/command/cli_options.go +++ b/cli/command/cli_options.go @@ -9,7 +9,6 @@ import ( "github.com/docker/cli/cli/context/docker" "github.com/docker/cli/cli/context/store" "github.com/docker/cli/cli/streams" - clitypes "github.com/docker/cli/types" "github.com/docker/docker/pkg/term" ) @@ -83,14 +82,6 @@ func WithContentTrust(enabled bool) DockerCliOption { } } -// WithContainerizedClient sets the containerized client constructor on a cli. -func WithContainerizedClient(containerizedFn func(string) (clitypes.ContainerizedClient, error)) DockerCliOption { - return func(cli *DockerCli) error { - cli.newContainerizeClient = containerizedFn - return nil - } -} - // WithContextEndpointType add support for an additional typed endpoint in the context store // Plugins should use this to store additional endpoints configuration in the context store func WithContextEndpointType(endpointName string, endpointType store.TypeGetter) DockerCliOption { diff --git a/cli/command/cli_test.go b/cli/command/cli_test.go index dcff1bb569..42b5e1c4e5 100644 --- a/cli/command/cli_test.go +++ b/cli/command/cli_test.go @@ -14,7 +14,6 @@ import ( cliconfig "github.com/docker/cli/cli/config" "github.com/docker/cli/cli/config/configfile" "github.com/docker/cli/cli/flags" - clitypes "github.com/docker/cli/types" "github.com/docker/docker/api" "github.com/docker/docker/api/types" "github.com/docker/docker/client" @@ -281,7 +280,6 @@ func TestNewDockerCliAndOperators(t *testing.T) { // Test default operations and also overriding default ones cli, err := NewDockerCli( WithContentTrust(true), - WithContainerizedClient(func(string) (clitypes.ContainerizedClient, error) { return nil, nil }), ) assert.NilError(t, err) // Check streams are initialized @@ -289,9 +287,6 @@ func TestNewDockerCliAndOperators(t *testing.T) { assert.Check(t, cli.Out() != nil) assert.Check(t, cli.Err() != nil) assert.Equal(t, cli.ContentTrustEnabled(), true) - client, err := cli.NewContainerizedEngineClient("") - assert.NilError(t, err) - assert.Equal(t, client, nil) // Apply can modify a dockerCli after construction inbuf := bytes.NewBuffer([]byte("input")) diff --git a/cli/command/commands/commands.go b/cli/command/commands/commands.go index 8cb5edd655..7c1ab20f7a 100644 --- a/cli/command/commands/commands.go +++ b/cli/command/commands/commands.go @@ -2,7 +2,6 @@ package commands import ( "os" - "runtime" "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command/builder" @@ -10,7 +9,6 @@ import ( "github.com/docker/cli/cli/command/config" "github.com/docker/cli/cli/command/container" "github.com/docker/cli/cli/command/context" - "github.com/docker/cli/cli/command/engine" "github.com/docker/cli/cli/command/image" "github.com/docker/cli/cli/command/manifest" "github.com/docker/cli/cli/command/network" @@ -125,10 +123,6 @@ func AddCommands(cmd *cobra.Command, dockerCli command.Cli) { hide(image.NewSaveCommand(dockerCli)), hide(image.NewTagCommand(dockerCli)), ) - if runtime.GOOS == "linux" { - // engine - cmd.AddCommand(engine.NewEngineCommand(dockerCli)) - } } func hide(cmd *cobra.Command) *cobra.Command { diff --git a/cli/command/engine/activate.go b/cli/command/engine/activate.go deleted file mode 100644 index fac6df853b..0000000000 --- a/cli/command/engine/activate.go +++ /dev/null @@ -1,209 +0,0 @@ -package engine - -import ( - "context" - "fmt" - "strings" - - "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/command/formatter" - "github.com/docker/cli/internal/licenseutils" - clitypes "github.com/docker/cli/types" - "github.com/docker/docker/api/types" - "github.com/docker/licensing/model" - "github.com/pkg/errors" - "github.com/spf13/cobra" -) - -type activateOptions struct { - licenseFile string - version string - registryPrefix string - format string - image string - quiet bool - displayOnly bool - sockPath string - licenseLoginFunc func(ctx context.Context, authConfig *types.AuthConfig) (licenseutils.HubUser, error) -} - -// newActivateCommand creates a new `docker engine activate` command -func newActivateCommand(dockerCli command.Cli) *cobra.Command { - var options activateOptions - options.licenseLoginFunc = licenseutils.Login - - cmd := &cobra.Command{ - Use: "activate [OPTIONS]", - Short: "Activate Enterprise Edition", - Long: `Activate Enterprise Edition. - -With this command you may apply an existing Docker enterprise license, or -interactively download one from Docker. In the interactive exchange, you can -sign up for a new trial, or download an existing license. If you are -currently running a Community Edition engine, the daemon will be updated to -the Enterprise Edition Docker engine with additional capabilities and long -term support. - -For more information about different Docker Enterprise license types visit -https://www.docker.com/licenses - -For non-interactive scriptable deployments, download your license from -https://hub.docker.com/ then specify the file with the '--license' flag. -`, - RunE: func(cmd *cobra.Command, args []string) error { - return runActivate(dockerCli, options) - }, - } - - flags := cmd.Flags() - - flags.StringVar(&options.licenseFile, "license", "", "License File") - flags.StringVar(&options.version, "version", "", "Specify engine version (default is to use currently running version)") - flags.StringVar(&options.registryPrefix, "registry-prefix", clitypes.RegistryPrefix, "Override the default location where engine images are pulled") - flags.StringVar(&options.image, "engine-image", "", "Specify engine image") - flags.StringVar(&options.format, "format", "", "Pretty-print licenses using a Go template") - flags.BoolVar(&options.displayOnly, "display-only", false, "only display license information and exit") - flags.BoolVar(&options.quiet, "quiet", false, "Only display available licenses by ID") - flags.StringVar(&options.sockPath, "containerd", "", "override default location of containerd endpoint") - - return cmd -} - -func runActivate(cli command.Cli, options activateOptions) error { - if !isRoot() { - return errors.New("this command must be run as a privileged user") - } - ctx := context.Background() - client, err := cli.NewContainerizedEngineClient(options.sockPath) - if err != nil { - return errors.Wrap(err, "unable to access local containerd") - } - defer client.Close() - - authConfig, err := getRegistryAuth(cli, options.registryPrefix) - if err != nil { - return err - } - - var license *model.IssuedLicense - - // Lookup on hub if no license provided via params - if options.licenseFile == "" { - if license, err = getLicenses(ctx, authConfig, cli, options); err != nil { - return err - } - if options.displayOnly { - return nil - } - } else { - if license, err = licenseutils.LoadLocalIssuedLicense(ctx, options.licenseFile); err != nil { - return err - } - } - summary, err := licenseutils.GetLicenseSummary(ctx, *license) - if err != nil { - return err - } - fmt.Fprintf(cli.Out(), "License: %s\n", summary) - if options.displayOnly { - return nil - } - dclient := cli.Client() - if err = licenseutils.ApplyLicense(ctx, dclient, license); err != nil { - return err - } - - // Short circuit if the user didn't specify a version and we're already running enterprise - if options.version == "" { - serverVersion, err := dclient.ServerVersion(ctx) - if err != nil { - return err - } - if strings.Contains(strings.ToLower(serverVersion.Platform.Name), "enterprise") { - fmt.Fprintln(cli.Out(), "Successfully activated engine license on existing enterprise engine.") - return nil - } - options.version = serverVersion.Version - } - - opts := clitypes.EngineInitOptions{ - RegistryPrefix: options.registryPrefix, - EngineImage: options.image, - EngineVersion: options.version, - } - - if err := client.ActivateEngine(ctx, opts, cli.Out(), authConfig); err != nil { - return err - } - fmt.Fprintln(cli.Out(), `Successfully activated engine. -Restart docker with 'systemctl restart docker' to complete the activation.`) - return nil -} - -func getLicenses(ctx context.Context, authConfig *types.AuthConfig, cli command.Cli, options activateOptions) (*model.IssuedLicense, error) { - user, err := options.licenseLoginFunc(ctx, authConfig) - if err != nil { - return nil, err - } - fmt.Fprintf(cli.Out(), "Looking for existing licenses for %s...\n", user.User.Username) - subs, err := user.GetAvailableLicenses(ctx) - if err != nil { - return nil, err - } - if len(subs) == 0 { - return doTrialFlow(ctx, cli, user) - } - - format := options.format - if len(format) == 0 { - format = formatter.TableFormatKey - } - - updatesCtx := formatter.Context{ - Output: cli.Out(), - Format: NewSubscriptionsFormat(format, options.quiet), - Trunc: false, - } - if err := SubscriptionsWrite(updatesCtx, subs); err != nil { - return nil, err - } - if options.displayOnly { - return nil, nil - } - fmt.Fprintf(cli.Out(), "Please pick a license by number: ") - var num int - if _, err := fmt.Fscan(cli.In(), &num); err != nil { - return nil, errors.Wrap(err, "failed to read user input") - } - if num < 0 || num >= len(subs) { - return nil, fmt.Errorf("invalid choice") - } - return user.GetIssuedLicense(ctx, subs[num].ID) -} - -func doTrialFlow(ctx context.Context, cli command.Cli, user licenseutils.HubUser) (*model.IssuedLicense, error) { - if !command.PromptForConfirmation(cli.In(), cli.Out(), - "No existing licenses found, would you like to set up a new Enterprise Basic Trial license?") { - return nil, fmt.Errorf("you must have an existing enterprise license or generate a new trial to use the Enterprise Docker Engine") - } - targetID := user.User.ID - // If the user is a member of any organizations, allow trials generated against them - if len(user.Orgs) > 0 { - fmt.Fprintf(cli.Out(), "%d\t%s\n", 0, user.User.Username) - for i, org := range user.Orgs { - fmt.Fprintf(cli.Out(), "%d\t%s\n", i+1, org.Orgname) - } - fmt.Fprintf(cli.Out(), "Please choose an account to generate the trial in:") - var num int - if _, err := fmt.Fscan(cli.In(), &num); err != nil { - return nil, errors.Wrap(err, "failed to read user input") - } - if num < 0 || num > len(user.Orgs) { - return nil, fmt.Errorf("invalid choice") - } - if num > 0 { - targetID = user.Orgs[num-1].ID - } - } - return user.GenerateTrialLicense(ctx, targetID) -} diff --git a/cli/command/engine/activate_test.go b/cli/command/engine/activate_test.go deleted file mode 100644 index ccaa0534e8..0000000000 --- a/cli/command/engine/activate_test.go +++ /dev/null @@ -1,148 +0,0 @@ -package engine - -import ( - "context" - "fmt" - "os" - "testing" - "time" - - "github.com/docker/cli/internal/licenseutils" - "github.com/docker/cli/internal/test" - clitypes "github.com/docker/cli/types" - "github.com/docker/docker/api/types" - "github.com/docker/docker/client" - "github.com/docker/licensing" - "github.com/docker/licensing/model" - "gotest.tools/assert" - "gotest.tools/fs" - "gotest.tools/golden" -) - -const ( - // nolint: lll - expiredLicense = `{"key_id":"irlYm3b9fdD8hMUXjazF39im7VQSSbAm9tfHK8cKUxJt","private_key":"aH5tTRDAVJpCRS2CRetTQVXIKgWUPfoCHODhDvNPvAbz","authorization":"ewogICAicGF5bG9hZCI6ICJleUpsZUhCcGNtRjBhVzl1SWpvaU1qQXhPQzB3TXkweE9GUXdOem93TURvd01Gb2lMQ0owYjJ0bGJpSTZJbkZtTVMxMlVtRmtialp5YjFaMldXdHJlVXN4VFdKMGNGUmpXR1ozVjA4MVRWZFFTM2cwUnpJd2NIYzlJaXdpYldGNFJXNW5hVzVsY3lJNk1Td2ljMk5oYm01cGJtZEZibUZpYkdWa0lqcDBjblZsTENKc2FXTmxibk5sVkhsd1pTSTZJazltWm14cGJtVWlMQ0owYVdWeUlqb2lVSEp2WkhWamRHbHZiaUo5IiwKICAgInNpZ25hdHVyZXMiOiBbCiAgICAgIHsKICAgICAgICAgImhlYWRlciI6IHsKICAgICAgICAgICAgImp3ayI6IHsKICAgICAgICAgICAgICAgImUiOiAiQVFBQiIsCiAgICAgICAgICAgICAgICJrZXlJRCI6ICJKN0xEOjY3VlI6TDVIWjpVN0JBOjJPNEc6NEFMMzpPRjJOOkpIR0I6RUZUSDo1Q1ZROk1GRU86QUVJVCIsCiAgICAgICAgICAgICAgICJraWQiOiAiSjdMRDo2N1ZSOkw1SFo6VTdCQToyTzRHOjRBTDM6T0YyTjpKSEdCOkVGVEg6NUNWUTpNRkVPOkFFSVQiLAogICAgICAgICAgICAgICAia3R5IjogIlJTQSIsCiAgICAgICAgICAgICAgICJuIjogInlkSXktbFU3bzdQY2VZLTQtcy1DUTVPRWdDeUY4Q3hJY1FJV3VLODRwSWlaY2lZNjczMHlDWW53TFNLVGx3LVU2VUNfUVJlV1Jpb01OTkU1RHM1VFlFWGJHRzZvbG0ycWRXYkJ3Y0NnLTJVVUhfT2NCOVd1UDZnUlBIcE1GTXN4RHpXd3ZheThKVXVIZ1lVTFVwbTFJdi1tcTdscDVuUV9SeHJUMEtaUkFRVFlMRU1FZkd3bTNoTU9fZ2VMUFMtaGdLUHRJSGxrZzZfV2NveFRHb0tQNzlkX3dhSFl4R05sN1doU25laUJTeGJwYlFBS2syMWxnNzk4WGI3dlp5RUFURE1yUlI5TWVFNkFkajVISnBZM0NveVJBUENtYUtHUkNLNHVvWlNvSXUwaEZWbEtVUHliYncwMDBHTy13YTJLTjhVd2dJSW0waTVJMXVXOUdrcTR6akJ5NXpoZ3F1VVhiRzliV1BBT1lycTVRYTgxRHhHY0JsSnlIWUFwLUREUEU5VEdnNHpZbVhqSm54WnFIRWR1R3FkZXZaOFhNSTB1a2ZrR0lJMTR3VU9pTUlJSXJYbEVjQmZfNDZJOGdRV0R6eHljWmVfSkdYLUxBdWF5WHJ5clVGZWhWTlVkWlVsOXdYTmFKQi1rYUNxejVRd2FSOTNzR3ctUVNmdEQwTnZMZTdDeU9ILUU2dmc2U3RfTmVUdmd2OFluaENpWElsWjhIT2ZJd05lN3RFRl9VY3o1T2JQeWttM3R5bHJOVWp0MFZ5QW10dGFjVkkyaUdpaGNVUHJtazRsVklaN1ZEX0xTVy1pN3lvU3VydHBzUFhjZTJwS0RJbzMwbEpHaE9fM0tVbWwyU1VaQ3F6SjF5RW1LcHlzSDVIRFc5Y3NJRkNBM2RlQWpmWlV2TjdVIgogICAgICAgICAgICB9LAogICAgICAgICAgICAiYWxnIjogIlJTMjU2IgogICAgICAgICB9LAogICAgICAgICAic2lnbmF0dXJlIjogIm5saTZIdzRrbW5KcTBSUmRXaGVfbkhZS2VJLVpKenM1U0d5SUpDakh1dWtnVzhBYklpVzFZYWJJR2NqWUt0QTY4dWN6T1hyUXZreGxWQXJLSlgzMDJzN0RpbzcxTlNPRzJVcnhsSjlibDFpd0F3a3ZyTEQ2T0p5MGxGLVg4WnRabXhPVmNQZmwzcmJwZFQ0dnlnWTdNcU1QRXdmb0IxTmlWZDYyZ1cxU2NSREZZcWw3R0FVaFVKNkp4QU15VzVaOXl5YVE0NV8wd0RMUk5mRjA5YWNXeVowTjRxVS1hZjhrUTZUUWZUX05ERzNCR3pRb2V3cHlEajRiMFBHb0diOFhLdDlwekpFdEdxM3lQM25VMFFBbk90a2gwTnZac1l1UFcyUnhDT3lRNEYzVlR3UkF2eF9HSTZrMVRpYmlKNnByUWluUy16Sjh6RE8zUjBuakE3OFBwNXcxcVpaUE9BdmtzZFNSYzJDcVMtcWhpTmF5YUhOVHpVNnpyOXlOZHR2S0o1QjNST0FmNUtjYXNiWURjTnVpeXBUNk90LUtqQ2I1dmYtWVpnc2FRNzJBdFBhSU4yeUpNREZHbmEwM0hpSjMxcTJRUlp5eTZrd3RYaGtwcDhTdEdIcHYxSWRaV09SVWttb0g5SFBzSGk4SExRLTZlM0tEY2x1RUQyMTNpZnljaVhtN0YzdHdaTTNHeDd1UXR1SldHaUlTZ2Z0QW9lVjZfUmI2VThkMmZxNzZuWHYxak5nckRRcE5waEZFd2tCdGRtZHZ2THByZVVYX3BWangza1AxN3pWbXFKNmNOOWkwWUc4WHg2VmRzcUxsRXUxQ2Rhd3Q0eko1M3VHMFlKTjRnUDZwc25yUS1uM0U1aFdlMDJ3d3dBZ3F3bGlPdmd4V1RTeXJyLXY2eDI0IiwKICAgICAgICAgInByb3RlY3RlZCI6ICJleUptYjNKdFlYUk1aVzVuZEdnaU9qRTNNeXdpWm05eWJXRjBWR0ZwYkNJNkltWlJJaXdpZEdsdFpTSTZJakl3TVRjdE1EVXRNRFZVTWpFNk5UYzZNek5hSW4wIgogICAgICB9CiAgIF0KfQ=="}` -) - -func TestActivateNoContainerd(t *testing.T) { - testCli.SetContainerizedEngineClient( - func(string) (clitypes.ContainerizedClient, error) { - return nil, fmt.Errorf("some error") - }, - ) - isRoot = func() bool { return true } - cmd := newActivateCommand(testCli) - cmd.Flags().Set("license", "invalidpath") - cmd.SilenceUsage = true - cmd.SilenceErrors = true - err := cmd.Execute() - assert.ErrorContains(t, err, "unable to access local containerd") -} - -func TestActivateBadLicense(t *testing.T) { - isRoot = func() bool { return true } - c := test.NewFakeCli(&verClient{client.Client{}, types.Version{}, nil, types.Info{}, nil}) - c.SetContainerizedEngineClient( - func(string) (clitypes.ContainerizedClient, error) { - return &fakeContainerizedEngineClient{}, nil - }, - ) - cmd := newActivateCommand(c) - cmd.SilenceUsage = true - cmd.SilenceErrors = true - cmd.Flags().Set("license", "invalidpath") - err := cmd.Execute() - assert.Assert(t, os.IsNotExist(err)) -} - -func TestActivateExpiredLicenseDryRun(t *testing.T) { - dir := fs.NewDir(t, "license", fs.WithFile("docker.lic", expiredLicense, fs.WithMode(0644))) - defer dir.Remove() - filename := dir.Join("docker.lic") - isRoot = func() bool { return true } - c := test.NewFakeCli(&verClient{client.Client{}, types.Version{}, nil, types.Info{}, nil}) - c.SetContainerizedEngineClient( - func(string) (clitypes.ContainerizedClient, error) { - return &fakeContainerizedEngineClient{}, nil - }, - ) - cmd := newActivateCommand(c) - cmd.SilenceUsage = true - cmd.SilenceErrors = true - cmd.Flags().Set("license", filename) - cmd.Flags().Set("display-only", "true") - c.OutBuffer().Reset() - err := cmd.Execute() - assert.NilError(t, err) - golden.Assert(t, c.OutBuffer().String(), "expired-license-display-only.golden") -} - -type mockLicenseClient struct{} - -func (c mockLicenseClient) LoginViaAuth(ctx context.Context, username, password string) (authToken string, err error) { - return "", fmt.Errorf("not implemented") -} - -func (c mockLicenseClient) GetHubUserOrgs(ctx context.Context, authToken string) (orgs []model.Org, err error) { - return nil, fmt.Errorf("not implemented") -} -func (c mockLicenseClient) GetHubUserByName(ctx context.Context, username string) (user *model.User, err error) { - return nil, fmt.Errorf("not implemented") -} -func (c mockLicenseClient) VerifyLicense(ctx context.Context, license model.IssuedLicense) (res *model.CheckResponse, err error) { - return nil, fmt.Errorf("not implemented") -} -func (c mockLicenseClient) GenerateNewTrialSubscription(ctx context.Context, authToken, dockerID string) (subscriptionID string, err error) { - return "", fmt.Errorf("not implemented") -} -func (c mockLicenseClient) ListSubscriptions(ctx context.Context, authToken, dockerID string) (response []*model.Subscription, err error) { - expires := time.Date(2010, time.January, 1, 0, 0, 0, 0, time.UTC) - return []*model.Subscription{ - { - State: "active", - Expires: &expires, - }, - }, nil -} -func (c mockLicenseClient) ListSubscriptionsDetails(ctx context.Context, authToken, dockerID string) (response []*model.SubscriptionDetail, err error) { - return nil, fmt.Errorf("not implemented") -} -func (c mockLicenseClient) DownloadLicenseFromHub(ctx context.Context, authToken, subscriptionID string) (license *model.IssuedLicense, err error) { - return nil, fmt.Errorf("not implemented") -} -func (c mockLicenseClient) ParseLicense(license []byte) (parsedLicense *model.IssuedLicense, err error) { - return nil, fmt.Errorf("not implemented") -} -func (c mockLicenseClient) StoreLicense(ctx context.Context, dclnt licensing.WrappedDockerClient, licenses *model.IssuedLicense, localRootDir string) error { - return fmt.Errorf("not implemented") -} -func (c mockLicenseClient) LoadLocalLicense(ctx context.Context, dclnt licensing.WrappedDockerClient) (*model.Subscription, error) { - return nil, fmt.Errorf("not implemented") -} -func (c mockLicenseClient) SummarizeLicense(res *model.CheckResponse) *model.Subscription { - return nil -} -func TestActivateDisplayOnlyHub(t *testing.T) { - isRoot = func() bool { return true } - c := test.NewFakeCli(&verClient{client.Client{}, types.Version{}, nil, types.Info{}, nil}) - c.SetContainerizedEngineClient( - func(string) (clitypes.ContainerizedClient, error) { - return &fakeContainerizedEngineClient{}, nil - }, - ) - - hubUser := licenseutils.HubUser{ - Client: mockLicenseClient{}, - } - options := activateOptions{ - licenseLoginFunc: func(ctx context.Context, authConfig *types.AuthConfig) (licenseutils.HubUser, error) { - return hubUser, nil - }, - displayOnly: true, - } - c.OutBuffer().Reset() - err := runActivate(c, options) - - assert.NilError(t, err) - golden.Assert(t, c.OutBuffer().String(), "expired-hub-license-display-only.golden") -} diff --git a/cli/command/engine/activate_unix.go b/cli/command/engine/activate_unix.go deleted file mode 100644 index ed4777ae2e..0000000000 --- a/cli/command/engine/activate_unix.go +++ /dev/null @@ -1,13 +0,0 @@ -// +build !windows - -package engine - -import ( - "golang.org/x/sys/unix" -) - -var ( - isRoot = func() bool { - return unix.Geteuid() == 0 - } -) diff --git a/cli/command/engine/activate_windows.go b/cli/command/engine/activate_windows.go deleted file mode 100644 index 35a4e88a36..0000000000 --- a/cli/command/engine/activate_windows.go +++ /dev/null @@ -1,9 +0,0 @@ -// +build windows - -package engine - -var ( - isRoot = func() bool { - return true - } -) diff --git a/cli/command/engine/auth.go b/cli/command/engine/auth.go deleted file mode 100644 index fee3e7b2c5..0000000000 --- a/cli/command/engine/auth.go +++ /dev/null @@ -1,34 +0,0 @@ -package engine - -import ( - "context" - - "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/trust" - clitypes "github.com/docker/cli/types" - "github.com/docker/distribution/reference" - "github.com/docker/docker/api/types" - registrytypes "github.com/docker/docker/api/types/registry" - "github.com/pkg/errors" -) - -func getRegistryAuth(cli command.Cli, registryPrefix string) (*types.AuthConfig, error) { - if registryPrefix == "" { - registryPrefix = clitypes.RegistryPrefix - } - distributionRef, err := reference.ParseNormalizedNamed(registryPrefix) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse image name: %s", registryPrefix) - } - imgRefAndAuth, err := trust.GetImageReferencesAndAuth(context.Background(), nil, authResolver(cli), distributionRef.String()) - if err != nil { - return nil, errors.Wrap(err, "failed to get imgRefAndAuth") - } - return imgRefAndAuth.AuthConfig(), nil -} - -func authResolver(cli command.Cli) func(ctx context.Context, index *registrytypes.IndexInfo) types.AuthConfig { - return func(ctx context.Context, index *registrytypes.IndexInfo) types.AuthConfig { - return command.ResolveAuthConfig(ctx, cli, index) - } -} diff --git a/cli/command/engine/check.go b/cli/command/engine/check.go deleted file mode 100644 index 07e4734a77..0000000000 --- a/cli/command/engine/check.go +++ /dev/null @@ -1,125 +0,0 @@ -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" -) - -type checkOptions struct { - registryPrefix string - preReleases bool - engineImage string - downgrades bool - upgrades bool - format string - quiet bool - sockPath string -} - -func newCheckForUpdatesCommand(dockerCli command.Cli) *cobra.Command { - var options checkOptions - - cmd := &cobra.Command{ - Use: "check [OPTIONS]", - Short: "Check for available engine updates", - Args: cli.NoArgs, - RunE: func(cmd *cobra.Command, args []string) error { - return runCheck(dockerCli, options) - }, - } - flags := cmd.Flags() - flags.StringVar(&options.registryPrefix, "registry-prefix", clitypes.RegistryPrefix, "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.StringVar(&options.engineImage, "engine-image", "", "Specify engine image (default uses the same image as currently running)") - flags.BoolVar(&options.upgrades, "upgrades", true, "Report available upgrades") - flags.StringVar(&options.format, "format", "", "Pretty-print updates using a Go template") - flags.BoolVarP(&options.quiet, "quiet", "q", false, "Only display available versions") - flags.StringVar(&options.sockPath, "containerd", "", "override default location of containerd endpoint") - - return cmd -} - -func runCheck(dockerCli command.Cli, options checkOptions) error { - if !isRoot() { - return errors.New("this command must be run as a privileged user") - } - ctx := context.Background() - client := dockerCli.Client() - serverVersion, err := client.ServerVersion(ctx) - if err != nil { - return err - } - - availVersions, err := versions.GetEngineVersions(ctx, dockerCli.RegistryClient(false), options.registryPrefix, options.engineImage, serverVersion.Version) - if err != nil { - return err - } - - 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 - } - - updatesCtx := formatter.Context{ - Output: dockerCli.Out(), - Format: NewUpdatesFormat(format, options.quiet), - Trunc: false, - } - return UpdatesWrite(updatesCtx, availUpdates) -} - -func processVersions(currentVersion, verType string, - includePrerelease bool, - availVersions []clitypes.DockerVersion) []clitypes.Update { - availUpdates := []clitypes.Update{} - for _, ver := range availVersions { - if !includePrerelease && ver.Prerelease() != "" { - continue - } - if ver.Tag != currentVersion { - availUpdates = append(availUpdates, clitypes.Update{ - Type: verType, - Version: ver.Tag, - Notes: fmt.Sprintf("%s/%s", clitypes.ReleaseNotePrefix, ver.Tag), - }) - } - } - return availUpdates -} diff --git a/cli/command/engine/check_test.go b/cli/command/engine/check_test.go deleted file mode 100644 index d1bfd933fc..0000000000 --- a/cli/command/engine/check_test.go +++ /dev/null @@ -1,114 +0,0 @@ -package engine - -import ( - "context" - "fmt" - "testing" - - manifesttypes "github.com/docker/cli/cli/manifest/types" - "github.com/docker/cli/internal/test" - "github.com/docker/distribution" - "github.com/docker/distribution/reference" - "github.com/docker/docker/api/types" - "github.com/docker/docker/client" - "github.com/opencontainers/go-digest" - "gotest.tools/assert" - "gotest.tools/golden" -) - -var ( - testCli = test.NewFakeCli(&client.Client{}) -) - -type verClient struct { - client.Client - ver types.Version - verErr error - info types.Info - infoErr error -} - -func (c *verClient) ServerVersion(ctx context.Context) (types.Version, error) { - return c.ver, c.verErr -} - -func (c *verClient) Info(ctx context.Context) (types.Info, error) { - return c.info, c.infoErr -} - -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) { - isRoot = func() bool { return true } - c := test.NewFakeCli(&verClient{client.Client{}, types.Version{}, nil, types.Info{}, nil}) - c.SetRegistryClient(testRegistryClient{}) - cmd := newCheckForUpdatesCommand(c) - cmd.SilenceUsage = true - cmd.SilenceErrors = true - err := cmd.Execute() - assert.ErrorContains(t, err, "no such file or directory") -} - -func TestCheckForUpdatesGetEngineVersionsHappy(t *testing.T) { - c := test.NewFakeCli(&verClient{client.Client{}, types.Version{Version: "1.1.0"}, nil, types.Info{ServerVersion: "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.Flags().Set("engine-image", "engine-community") - cmd.SilenceUsage = true - cmd.SilenceErrors = true - err := cmd.Execute() - assert.NilError(t, err) - golden.Assert(t, c.OutBuffer().String(), "check-all.golden") - - c.OutBuffer().Reset() - cmd.Flags().Set("pre-releases", "false") - cmd.Flags().Set("downgrades", "true") - err = cmd.Execute() - assert.NilError(t, err) - fmt.Println(c.OutBuffer().String()) - golden.Assert(t, c.OutBuffer().String(), "check-no-prerelease.golden") - - c.OutBuffer().Reset() - cmd.Flags().Set("pre-releases", "false") - cmd.Flags().Set("downgrades", "false") - err = cmd.Execute() - assert.NilError(t, err) - fmt.Println(c.OutBuffer().String()) - golden.Assert(t, c.OutBuffer().String(), "check-no-downgrades.golden") - - 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(c.OutBuffer().String()) - golden.Assert(t, c.OutBuffer().String(), "check-patches-only.golden") -} diff --git a/cli/command/engine/client_test.go b/cli/command/engine/client_test.go deleted file mode 100644 index e646666fdf..0000000000 --- a/cli/command/engine/client_test.go +++ /dev/null @@ -1,101 +0,0 @@ -package engine - -import ( - "context" - - "github.com/containerd/containerd" - registryclient "github.com/docker/cli/cli/registry/client" - clitypes "github.com/docker/cli/types" - "github.com/docker/docker/api/types" -) - -type ( - fakeContainerizedEngineClient struct { - closeFunc func() error - activateEngineFunc func(ctx context.Context, - opts clitypes.EngineInitOptions, - out clitypes.OutStream, - authConfig *types.AuthConfig) error - initEngineFunc func(ctx context.Context, - opts clitypes.EngineInitOptions, - out clitypes.OutStream, - authConfig *types.AuthConfig, - healthfn func(context.Context) error) error - doUpdateFunc func(ctx context.Context, - opts clitypes.EngineInitOptions, - out clitypes.OutStream, - authConfig *types.AuthConfig) error - getEngineVersionsFunc func(ctx context.Context, - registryClient registryclient.RegistryClient, - currentVersion, - imageName string) (clitypes.AvailableVersions, error) - - getEngineFunc func(ctx context.Context) (containerd.Container, error) - removeEngineFunc func(ctx context.Context) error - getCurrentEngineVersionFunc func(ctx context.Context) (clitypes.EngineInitOptions, error) - } -) - -func (w *fakeContainerizedEngineClient) Close() error { - if w.closeFunc != nil { - return w.closeFunc() - } - return nil -} - -func (w *fakeContainerizedEngineClient) ActivateEngine(ctx context.Context, - opts clitypes.EngineInitOptions, - out clitypes.OutStream, - authConfig *types.AuthConfig) error { - if w.activateEngineFunc != nil { - return w.activateEngineFunc(ctx, opts, out, authConfig) - } - return nil -} -func (w *fakeContainerizedEngineClient) InitEngine(ctx context.Context, - opts clitypes.EngineInitOptions, - out clitypes.OutStream, - authConfig *types.AuthConfig, - healthfn func(context.Context) error) error { - if w.initEngineFunc != nil { - return w.initEngineFunc(ctx, opts, out, authConfig, healthfn) - } - return nil -} -func (w *fakeContainerizedEngineClient) DoUpdate(ctx context.Context, - opts clitypes.EngineInitOptions, - out clitypes.OutStream, - authConfig *types.AuthConfig) error { - if w.doUpdateFunc != nil { - return w.doUpdateFunc(ctx, opts, out, authConfig) - } - return nil -} -func (w *fakeContainerizedEngineClient) GetEngineVersions(ctx context.Context, - registryClient registryclient.RegistryClient, - currentVersion, imageName string) (clitypes.AvailableVersions, error) { - - if w.getEngineVersionsFunc != nil { - return w.getEngineVersionsFunc(ctx, registryClient, currentVersion, imageName) - } - return clitypes.AvailableVersions{}, nil -} - -func (w *fakeContainerizedEngineClient) GetEngine(ctx context.Context) (containerd.Container, error) { - if w.getEngineFunc != nil { - return w.getEngineFunc(ctx) - } - return nil, nil -} -func (w *fakeContainerizedEngineClient) RemoveEngine(ctx context.Context) error { - if w.removeEngineFunc != nil { - return w.removeEngineFunc(ctx) - } - return nil -} -func (w *fakeContainerizedEngineClient) GetCurrentEngineVersion(ctx context.Context) (clitypes.EngineInitOptions, error) { - if w.getCurrentEngineVersionFunc != nil { - return w.getCurrentEngineVersionFunc(ctx) - } - return clitypes.EngineInitOptions{}, nil -} diff --git a/cli/command/engine/cmd.go b/cli/command/engine/cmd.go deleted file mode 100644 index 66e56631f0..0000000000 --- a/cli/command/engine/cmd.go +++ /dev/null @@ -1,23 +0,0 @@ -package engine - -import ( - "github.com/docker/cli/cli" - "github.com/docker/cli/cli/command" - "github.com/spf13/cobra" -) - -// NewEngineCommand returns a cobra command for `engine` subcommands -func NewEngineCommand(dockerCli command.Cli) *cobra.Command { - cmd := &cobra.Command{ - Use: "engine COMMAND", - Short: "Manage the docker engine", - Args: cli.NoArgs, - RunE: command.ShowHelp(dockerCli.Err()), - } - cmd.AddCommand( - newActivateCommand(dockerCli), - newCheckForUpdatesCommand(dockerCli), - newUpdateCommand(dockerCli), - ) - return cmd -} diff --git a/cli/command/engine/cmd_test.go b/cli/command/engine/cmd_test.go deleted file mode 100644 index 30639cbf3c..0000000000 --- a/cli/command/engine/cmd_test.go +++ /dev/null @@ -1,14 +0,0 @@ -package engine - -import ( - "testing" - - "gotest.tools/assert" -) - -func TestNewEngineCommand(t *testing.T) { - cmd := NewEngineCommand(testCli) - - subcommands := cmd.Commands() - assert.Assert(t, len(subcommands) == 3) -} diff --git a/cli/command/engine/init.go b/cli/command/engine/init.go deleted file mode 100644 index f29001d086..0000000000 --- a/cli/command/engine/init.go +++ /dev/null @@ -1,10 +0,0 @@ -package engine - -import ( - clitypes "github.com/docker/cli/types" -) - -type extendedEngineInitOptions struct { - clitypes.EngineInitOptions - sockPath string -} diff --git a/cli/command/engine/licenses.go b/cli/command/engine/licenses.go deleted file mode 100644 index 37090ac28e..0000000000 --- a/cli/command/engine/licenses.go +++ /dev/null @@ -1,155 +0,0 @@ -package engine - -import ( - "time" - - "github.com/docker/cli/cli/command/formatter" - "github.com/docker/cli/internal/licenseutils" - "github.com/docker/licensing/model" -) - -const ( - defaultSubscriptionsTableFormat = "table {{.Num}}\t{{.Owner}}\t{{.ProductID}}\t{{.Expires}}\t{{.ComponentsString}}" - defaultSubscriptionsQuietFormat = "{{.Num}}:{{.Summary}}" - - numHeader = "NUM" - ownerHeader = "OWNER" - licenseNameHeader = "NAME" - idHeader = "ID" - dockerIDHeader = "DOCKER ID" - productIDHeader = "PRODUCT ID" - productRatePlanHeader = "PRODUCT RATE PLAN" - productRatePlanIDHeader = "PRODUCT RATE PLAN ID" - startHeader = "START" - expiresHeader = "EXPIRES" - stateHeader = "STATE" - eusaHeader = "EUSA" - pricingComponentsHeader = "PRICING COMPONENTS" -) - -// NewSubscriptionsFormat returns a Format for rendering using a license Context -func NewSubscriptionsFormat(source string, quiet bool) formatter.Format { - switch source { - case formatter.TableFormatKey: - if quiet { - return defaultSubscriptionsQuietFormat - } - return defaultSubscriptionsTableFormat - case formatter.RawFormatKey: - if quiet { - return `license: {{.ID}}` - } - return `license: {{.ID}}\nname: {{.Name}}\nowner: {{.Owner}}\ncomponents: {{.ComponentsString}}\n` - } - return formatter.Format(source) -} - -// SubscriptionsWrite writes the context -func SubscriptionsWrite(ctx formatter.Context, subs []licenseutils.LicenseDisplay) error { - render := func(format func(subContext formatter.SubContext) error) error { - for _, sub := range subs { - licenseCtx := &licenseContext{trunc: ctx.Trunc, l: sub} - if err := format(licenseCtx); err != nil { - return err - } - } - return nil - } - licenseCtx := licenseContext{} - licenseCtx.Header = map[string]string{ - "Num": numHeader, - "Owner": ownerHeader, - "Name": licenseNameHeader, - "ID": idHeader, - "DockerID": dockerIDHeader, - "ProductID": productIDHeader, - "ProductRatePlan": productRatePlanHeader, - "ProductRatePlanID": productRatePlanIDHeader, - "Start": startHeader, - "Expires": expiresHeader, - "State": stateHeader, - "Eusa": eusaHeader, - "ComponentsString": pricingComponentsHeader, - } - return ctx.Write(&licenseCtx, render) -} - -type licenseContext struct { - formatter.HeaderContext - trunc bool - l licenseutils.LicenseDisplay -} - -func (c *licenseContext) MarshalJSON() ([]byte, error) { - return formatter.MarshalJSON(c) -} - -func (c *licenseContext) Num() int { - return c.l.Num -} - -func (c *licenseContext) Owner() string { - return c.l.Owner -} - -func (c *licenseContext) ComponentsString() string { - return c.l.ComponentsString -} - -func (c *licenseContext) Summary() string { - return c.l.String() -} - -func (c *licenseContext) Name() string { - return c.l.Name -} - -func (c *licenseContext) ID() string { - return c.l.ID -} - -func (c *licenseContext) DockerID() string { - return c.l.DockerID -} - -func (c *licenseContext) ProductID() string { - return c.l.ProductID -} - -func (c *licenseContext) ProductRatePlan() string { - return c.l.ProductRatePlan -} - -func (c *licenseContext) ProductRatePlanID() string { - return c.l.ProductRatePlanID -} - -func (c *licenseContext) Start() *time.Time { - return c.l.Start -} - -func (c *licenseContext) Expires() *time.Time { - return c.l.Expires -} - -func (c *licenseContext) State() string { - return c.l.State -} - -func (c *licenseContext) Eusa() *model.EusaState { - return c.l.Eusa -} - -func (c *licenseContext) PricingComponents() []model.SubscriptionPricingComponent { - // Dereference the pricing component pointers in the pricing components - // so it can be rendered properly with the template formatter - - var ret []model.SubscriptionPricingComponent - for _, spc := range c.l.PricingComponents { - if spc == nil { - continue - } - ret = append(ret, *spc) - } - return ret -} diff --git a/cli/command/engine/licenses_test.go b/cli/command/engine/licenses_test.go deleted file mode 100644 index bf6b58bd5c..0000000000 --- a/cli/command/engine/licenses_test.go +++ /dev/null @@ -1,257 +0,0 @@ -package engine - -import ( - "bytes" - "encoding/json" - "strings" - "testing" - "time" - - "github.com/docker/cli/cli/command/formatter" - "github.com/docker/cli/internal/licenseutils" - "github.com/docker/licensing/model" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" -) - -func TestSubscriptionContextWrite(t *testing.T) { - cases := []struct { - context formatter.Context - expected string - }{ - // Errors - { - formatter.Context{Format: "{{InvalidFunction}}"}, - `Template parsing error: template: :1: function "InvalidFunction" not defined -`, - }, - { - formatter.Context{Format: "{{nil}}"}, - `Template parsing error: template: :1:2: executing "" at : nil is not a command -`, - }, - // Table format - { - formatter.Context{Format: NewSubscriptionsFormat("table", false)}, - `NUM OWNER PRODUCT ID EXPIRES PRICING COMPONENTS -1 owner1 productid1 2020-01-01 10:00:00 +0000 UTC compstring -2 owner2 productid2 2020-01-01 10:00:00 +0000 UTC compstring -`, - }, - { - formatter.Context{Format: NewSubscriptionsFormat("table", true)}, - `1:License Name: name1 Components: 10 nodes Expiration date: 2020-01-01 -2:License Name: name2 Components: 20 nodes Expiration date: 2020-01-01 -`, - }, - { - formatter.Context{Format: NewSubscriptionsFormat("table {{.Owner}}", false)}, - `OWNER -owner1 -owner2 -`, - }, - { - formatter.Context{Format: NewSubscriptionsFormat("table {{.Owner}}", true)}, - `OWNER -owner1 -owner2 -`, - }, - // Raw Format - { - formatter.Context{Format: NewSubscriptionsFormat("raw", false)}, - `license: id1 -name: name1 -owner: owner1 -components: compstring - -license: id2 -name: name2 -owner: owner2 -components: compstring - -`, - }, - { - formatter.Context{Format: NewSubscriptionsFormat("raw", true)}, - `license: id1 -license: id2 -`, - }, - // Custom Format - { - formatter.Context{Format: NewSubscriptionsFormat("{{.Owner}}", false)}, - `owner1 -owner2 -`, - }, - } - - expiration, _ := time.Parse(time.RFC822, "01 Jan 20 10:00 UTC") - - for _, testcase := range cases { - subscriptions := []licenseutils.LicenseDisplay{ - { - Num: 1, - Owner: "owner1", - Subscription: model.Subscription{ - ID: "id1", - Name: "name1", - ProductID: "productid1", - Expires: &expiration, - PricingComponents: model.PricingComponents{ - &model.SubscriptionPricingComponent{ - Name: "nodes", - Value: 10, - }, - }, - }, - ComponentsString: "compstring", - }, - { - Num: 2, - Owner: "owner2", - Subscription: model.Subscription{ - ID: "id2", - Name: "name2", - ProductID: "productid2", - Expires: &expiration, - PricingComponents: model.PricingComponents{ - &model.SubscriptionPricingComponent{ - Name: "nodes", - Value: 20, - }, - }, - }, - ComponentsString: "compstring", - }, - } - out := &bytes.Buffer{} - testcase.context.Output = out - err := SubscriptionsWrite(testcase.context, subscriptions) - if err != nil { - assert.Error(t, err, testcase.expected) - } else { - assert.Check(t, is.Equal(testcase.expected, out.String())) - } - } -} - -func TestSubscriptionContextWriteJSON(t *testing.T) { - expiration, _ := time.Parse(time.RFC822, "01 Jan 20 10:00 UTC") - subscriptions := []licenseutils.LicenseDisplay{ - { - Num: 1, - Owner: "owner1", - Subscription: model.Subscription{ - ID: "id1", - Name: "name1", - ProductID: "productid1", - Expires: &expiration, - PricingComponents: model.PricingComponents{ - &model.SubscriptionPricingComponent{ - Name: "nodes", - Value: 10, - }, - }, - }, - ComponentsString: "compstring", - }, - { - Num: 2, - Owner: "owner2", - Subscription: model.Subscription{ - ID: "id2", - Name: "name2", - ProductID: "productid2", - Expires: &expiration, - PricingComponents: model.PricingComponents{ - &model.SubscriptionPricingComponent{ - Name: "nodes", - Value: 20, - }, - }, - }, - ComponentsString: "compstring", - }, - } - expectedJSONs := []map[string]interface{}{ - { - "Owner": "owner1", - "ComponentsString": "compstring", - "Expires": "2020-01-01T10:00:00Z", - "DockerID": "", - "Eusa": nil, - "ID": "id1", - "Start": nil, - "Name": "name1", - "Num": float64(1), - "PricingComponents": []interface{}{ - map[string]interface{}{ - "name": "nodes", - "value": float64(10), - }, - }, - "ProductID": "productid1", - "ProductRatePlan": "", - "ProductRatePlanID": "", - "State": "", - "Summary": "License Name: name1\tComponents: 10 nodes\tExpiration date: 2020-01-01", - }, - { - "Owner": "owner2", - "ComponentsString": "compstring", - "Expires": "2020-01-01T10:00:00Z", - "DockerID": "", - "Eusa": nil, - "ID": "id2", - "Start": nil, - "Name": "name2", - "Num": float64(2), - "PricingComponents": []interface{}{ - map[string]interface{}{ - "name": "nodes", - "value": float64(20), - }, - }, - "ProductID": "productid2", - "ProductRatePlan": "", - "ProductRatePlanID": "", - "State": "", - "Summary": "License Name: name2\tComponents: 20 nodes\tExpiration date: 2020-01-01", - }, - } - - out := &bytes.Buffer{} - err := SubscriptionsWrite(formatter.Context{Format: "{{json .}}", Output: out}, subscriptions) - if err != nil { - t.Fatal(err) - } - for i, line := range strings.Split(strings.TrimSpace(out.String()), "\n") { - var m map[string]interface{} - if err := json.Unmarshal([]byte(line), &m); err != nil { - t.Fatal(err) - } - assert.Check(t, is.DeepEqual(expectedJSONs[i], m)) - } -} - -func TestSubscriptionContextWriteJSONField(t *testing.T) { - subscriptions := []licenseutils.LicenseDisplay{ - {Num: 1, Owner: "owner1"}, - {Num: 2, Owner: "owner2"}, - } - out := &bytes.Buffer{} - err := SubscriptionsWrite(formatter.Context{Format: "{{json .Owner}}", Output: out}, subscriptions) - if err != nil { - t.Fatal(err) - } - for i, line := range strings.Split(strings.TrimSpace(out.String()), "\n") { - var s string - if err := json.Unmarshal([]byte(line), &s); err != nil { - t.Fatal(err) - } - assert.Check(t, is.Equal(subscriptions[i].Owner, s)) - } -} diff --git a/cli/command/engine/testdata/check-all.golden b/cli/command/engine/testdata/check-all.golden deleted file mode 100644 index a6a5c70fe9..0000000000 --- a/cli/command/engine/testdata/check-all.golden +++ /dev/null @@ -1,11 +0,0 @@ -TYPE VERSION NOTES -current 1.1.0 -patch 1.1.1 https://docs.docker.com/releasenotes/1.1.1 -patch 1.1.2 https://docs.docker.com/releasenotes/1.1.2 -patch 1.1.3-beta1 https://docs.docker.com/releasenotes/1.1.3-beta1 -upgrade 1.2.0 https://docs.docker.com/releasenotes/1.2.0 -upgrade 2.0.0 https://docs.docker.com/releasenotes/2.0.0 -upgrade 2.1.0-beta1 https://docs.docker.com/releasenotes/2.1.0-beta1 -downgrade 1.0.1 https://docs.docker.com/releasenotes/1.0.1 -downgrade 1.0.2 https://docs.docker.com/releasenotes/1.0.2 -downgrade 1.0.3-beta1 https://docs.docker.com/releasenotes/1.0.3-beta1 diff --git a/cli/command/engine/testdata/check-no-downgrades.golden b/cli/command/engine/testdata/check-no-downgrades.golden deleted file mode 100644 index 790b7dd176..0000000000 --- a/cli/command/engine/testdata/check-no-downgrades.golden +++ /dev/null @@ -1,6 +0,0 @@ -TYPE VERSION NOTES -current 1.1.0 -patch 1.1.1 https://docs.docker.com/releasenotes/1.1.1 -patch 1.1.2 https://docs.docker.com/releasenotes/1.1.2 -upgrade 1.2.0 https://docs.docker.com/releasenotes/1.2.0 -upgrade 2.0.0 https://docs.docker.com/releasenotes/2.0.0 diff --git a/cli/command/engine/testdata/check-no-prerelease.golden b/cli/command/engine/testdata/check-no-prerelease.golden deleted file mode 100644 index acb5053563..0000000000 --- a/cli/command/engine/testdata/check-no-prerelease.golden +++ /dev/null @@ -1,8 +0,0 @@ -TYPE VERSION NOTES -current 1.1.0 -patch 1.1.1 https://docs.docker.com/releasenotes/1.1.1 -patch 1.1.2 https://docs.docker.com/releasenotes/1.1.2 -upgrade 1.2.0 https://docs.docker.com/releasenotes/1.2.0 -upgrade 2.0.0 https://docs.docker.com/releasenotes/2.0.0 -downgrade 1.0.1 https://docs.docker.com/releasenotes/1.0.1 -downgrade 1.0.2 https://docs.docker.com/releasenotes/1.0.2 diff --git a/cli/command/engine/testdata/check-patches-only.golden b/cli/command/engine/testdata/check-patches-only.golden deleted file mode 100644 index d572970380..0000000000 --- a/cli/command/engine/testdata/check-patches-only.golden +++ /dev/null @@ -1,4 +0,0 @@ -TYPE VERSION NOTES -current 1.1.0 -patch 1.1.1 https://docs.docker.com/releasenotes/1.1.1 -patch 1.1.2 https://docs.docker.com/releasenotes/1.1.2 diff --git a/cli/command/engine/testdata/expired-hub-license-display-only.golden b/cli/command/engine/testdata/expired-hub-license-display-only.golden deleted file mode 100644 index 71e97bbd83..0000000000 --- a/cli/command/engine/testdata/expired-hub-license-display-only.golden +++ /dev/null @@ -1,3 +0,0 @@ -Looking for existing licenses for ... -NUM OWNER PRODUCT ID EXPIRES PRICING COMPONENTS -0 2010-01-01 00:00:00 +0000 UTC diff --git a/cli/command/engine/testdata/expired-license-display-only.golden b/cli/command/engine/testdata/expired-license-display-only.golden deleted file mode 100644 index 8586daf136..0000000000 --- a/cli/command/engine/testdata/expired-license-display-only.golden +++ /dev/null @@ -1 +0,0 @@ -License: Components: 1 Nodes Expiration date: 2018-03-18 Expired! You will no longer receive updates. Please renew at https://docker.com/licensing diff --git a/cli/command/engine/update.go b/cli/command/engine/update.go deleted file mode 100644 index e28fc459b3..0000000000 --- a/cli/command/engine/update.go +++ /dev/null @@ -1,55 +0,0 @@ -package engine - -import ( - "context" - "fmt" - - "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" -) - -func newUpdateCommand(dockerCli command.Cli) *cobra.Command { - var options extendedEngineInitOptions - - cmd := &cobra.Command{ - Use: "update [OPTIONS]", - Short: "Update a local engine", - Args: cli.NoArgs, - RunE: func(cmd *cobra.Command, args []string) error { - return runUpdate(dockerCli, options) - }, - } - flags := cmd.Flags() - - flags.StringVar(&options.EngineVersion, "version", "", "Specify engine version") - flags.StringVar(&options.EngineImage, "engine-image", "", "Specify engine image (default uses the same image as currently running)") - flags.StringVar(&options.RegistryPrefix, "registry-prefix", clitypes.RegistryPrefix, "Override the current location where engine images are pulled") - flags.StringVar(&options.sockPath, "containerd", "", "override default location of containerd endpoint") - - return cmd -} - -func runUpdate(dockerCli command.Cli, options extendedEngineInitOptions) error { - if !isRoot() { - return errors.New("this command must be run as a privileged user") - } - 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 - } - if err := client.DoUpdate(ctx, options.EngineInitOptions, dockerCli.Out(), authConfig); err != nil { - return err - } - fmt.Fprintln(dockerCli.Out(), `Successfully updated engine. -Restart docker with 'systemctl restart docker' to complete the update.`) - return nil -} diff --git a/cli/command/engine/update_test.go b/cli/command/engine/update_test.go deleted file mode 100644 index 641b8458c7..0000000000 --- a/cli/command/engine/update_test.go +++ /dev/null @@ -1,40 +0,0 @@ -package engine - -import ( - "fmt" - "testing" - - "github.com/docker/cli/internal/test" - clitypes "github.com/docker/cli/types" - "github.com/docker/docker/api/types" - "github.com/docker/docker/client" - "gotest.tools/assert" -) - -func TestUpdateNoContainerd(t *testing.T) { - testCli.SetContainerizedEngineClient( - func(string) (clitypes.ContainerizedClient, error) { - return nil, fmt.Errorf("some error") - }, - ) - cmd := newUpdateCommand(testCli) - cmd.SilenceUsage = true - cmd.SilenceErrors = true - err := cmd.Execute() - assert.ErrorContains(t, err, "unable to access local containerd") -} - -func TestUpdateHappy(t *testing.T) { - c := test.NewFakeCli(&verClient{client.Client{}, types.Version{Version: "1.1.0"}, nil, types.Info{ServerVersion: "1.1.0"}, nil}) - c.SetContainerizedEngineClient( - func(string) (clitypes.ContainerizedClient, error) { - return &fakeContainerizedEngineClient{}, nil - }, - ) - cmd := newUpdateCommand(c) - cmd.Flags().Set("registry-prefix", clitypes.RegistryPrefix) - cmd.Flags().Set("version", "someversion") - cmd.Flags().Set("engine-image", "someimage") - err := cmd.Execute() - assert.NilError(t, err) -} diff --git a/cli/command/engine/updates.go b/cli/command/engine/updates.go deleted file mode 100644 index 2ca4a55292..0000000000 --- a/cli/command/engine/updates.go +++ /dev/null @@ -1,74 +0,0 @@ -package engine - -import ( - "github.com/docker/cli/cli/command/formatter" - clitypes "github.com/docker/cli/types" -) - -const ( - defaultUpdatesTableFormat = "table {{.Type}}\t{{.Version}}\t{{.Notes}}" - defaultUpdatesQuietFormat = "{{.Version}}" - - updatesTypeHeader = "TYPE" - versionHeader = "VERSION" - notesHeader = "NOTES" -) - -// NewUpdatesFormat returns a Format for rendering using a updates context -func NewUpdatesFormat(source string, quiet bool) formatter.Format { - switch source { - case formatter.TableFormatKey: - if quiet { - return defaultUpdatesQuietFormat - } - return defaultUpdatesTableFormat - case formatter.RawFormatKey: - if quiet { - return `update_version: {{.Version}}` - } - return `update_version: {{.Version}}\ntype: {{.Type}}\nnotes: {{.Notes}}\n` - } - return formatter.Format(source) -} - -// UpdatesWrite writes the context -func UpdatesWrite(ctx formatter.Context, availableUpdates []clitypes.Update) error { - render := func(format func(subContext formatter.SubContext) error) error { - for _, update := range availableUpdates { - updatesCtx := &updateContext{trunc: ctx.Trunc, u: update} - if err := format(updatesCtx); err != nil { - return err - } - } - return nil - } - updatesCtx := updateContext{} - updatesCtx.Header = map[string]string{ - "Type": updatesTypeHeader, - "Version": versionHeader, - "Notes": notesHeader, - } - return ctx.Write(&updatesCtx, render) -} - -type updateContext struct { - formatter.HeaderContext - trunc bool - u clitypes.Update -} - -func (c *updateContext) MarshalJSON() ([]byte, error) { - return formatter.MarshalJSON(c) -} - -func (c *updateContext) Type() string { - return c.u.Type -} - -func (c *updateContext) Version() string { - return c.u.Version -} - -func (c *updateContext) Notes() string { - return c.u.Notes -} diff --git a/cli/command/engine/updates_test.go b/cli/command/engine/updates_test.go deleted file mode 100644 index 7fb2b0f152..0000000000 --- a/cli/command/engine/updates_test.go +++ /dev/null @@ -1,144 +0,0 @@ -package engine - -import ( - "bytes" - "encoding/json" - "strings" - "testing" - - "github.com/docker/cli/cli/command/formatter" - clitypes "github.com/docker/cli/types" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" -) - -func TestUpdateContextWrite(t *testing.T) { - cases := []struct { - context formatter.Context - expected string - }{ - // Errors - { - formatter.Context{Format: "{{InvalidFunction}}"}, - `Template parsing error: template: :1: function "InvalidFunction" not defined -`, - }, - { - formatter.Context{Format: "{{nil}}"}, - `Template parsing error: template: :1:2: executing "" at : nil is not a command -`, - }, - // Table format - { - formatter.Context{Format: NewUpdatesFormat("table", false)}, - `TYPE VERSION NOTES -updateType1 version1 description 1 -updateType2 version2 description 2 -`, - }, - { - formatter.Context{Format: NewUpdatesFormat("table", true)}, - `version1 -version2 -`, - }, - { - formatter.Context{Format: NewUpdatesFormat("table {{.Version}}", false)}, - `VERSION -version1 -version2 -`, - }, - { - formatter.Context{Format: NewUpdatesFormat("table {{.Version}}", true)}, - `VERSION -version1 -version2 -`, - }, - // Raw Format - { - formatter.Context{Format: NewUpdatesFormat("raw", false)}, - `update_version: version1 -type: updateType1 -notes: description 1 - -update_version: version2 -type: updateType2 -notes: description 2 - -`, - }, - { - formatter.Context{Format: NewUpdatesFormat("raw", true)}, - `update_version: version1 -update_version: version2 -`, - }, - // Custom Format - { - formatter.Context{Format: NewUpdatesFormat("{{.Version}}", false)}, - `version1 -version2 -`, - }, - } - - for _, testcase := range cases { - updates := []clitypes.Update{ - {Type: "updateType1", Version: "version1", Notes: "description 1"}, - {Type: "updateType2", Version: "version2", Notes: "description 2"}, - } - out := &bytes.Buffer{} - testcase.context.Output = out - err := UpdatesWrite(testcase.context, updates) - if err != nil { - assert.Error(t, err, testcase.expected) - } else { - assert.Check(t, is.Equal(testcase.expected, out.String())) - } - } -} - -func TestUpdateContextWriteJSON(t *testing.T) { - updates := []clitypes.Update{ - {Type: "updateType1", Version: "version1", Notes: "note1"}, - {Type: "updateType2", Version: "version2", Notes: "note2"}, - } - expectedJSONs := []map[string]interface{}{ - {"Version": "version1", "Notes": "note1", "Type": "updateType1"}, - {"Version": "version2", "Notes": "note2", "Type": "updateType2"}, - } - - out := &bytes.Buffer{} - err := UpdatesWrite(formatter.Context{Format: "{{json .}}", Output: out}, updates) - if err != nil { - t.Fatal(err) - } - for i, line := range strings.Split(strings.TrimSpace(out.String()), "\n") { - var m map[string]interface{} - if err := json.Unmarshal([]byte(line), &m); err != nil { - t.Fatal(err) - } - assert.Check(t, is.DeepEqual(expectedJSONs[i], m)) - } -} - -func TestUpdateContextWriteJSONField(t *testing.T) { - updates := []clitypes.Update{ - {Type: "updateType1", Version: "version1"}, - {Type: "updateType2", Version: "version2"}, - } - out := &bytes.Buffer{} - err := UpdatesWrite(formatter.Context{Format: "{{json .Type}}", Output: out}, updates) - if err != nil { - t.Fatal(err) - } - for i, line := range strings.Split(strings.TrimSpace(out.String()), "\n") { - var s string - if err := json.Unmarshal([]byte(line), &s); err != nil { - t.Fatal(err) - } - assert.Check(t, is.Equal(updates[i].Type, s)) - } -} diff --git a/contrib/completion/bash/docker b/contrib/completion/bash/docker index c44d6b3eb1..ed8d1d6b9f 100644 --- a/contrib/completion/bash/docker +++ b/contrib/completion/bash/docker @@ -2700,67 +2700,6 @@ _docker_diff() { } -_docker_engine() { - local subcommands=" - activate - check - update - " - __docker_subcommands "$subcommands" && return - - case "$cur" in - -*) - COMPREPLY=( $( compgen -W "--help" -- "$cur" ) ) - ;; - *) - COMPREPLY=( $( compgen -W "$subcommands" -- "$cur" ) ) - ;; - esac -} - -_docker_engine_activate() { - case "$prev" in - --containerd|--engine-image|--format|--license|--registry-prefix|--version) - return - ;; - esac - - case "$cur" in - -*) - COMPREPLY=( $( compgen -W "--containerd --display-only --engine-image --format --help --license --quiet --registry-prefix --version" -- "$cur" ) ) - ;; - esac -} - -_docker_engine_check() { - case "$prev" in - --containerd|--engine-image|--format|--registry-prefix) - return - ;; - esac - - case "$cur" in - -*) - COMPREPLY=( $( compgen -W "--containerd --downgrades --engine-image --format --help --pre-releases --quiet -q --registry-prefix --upgrades" -- "$cur" ) ) - ;; - esac -} - -_docker_engine_update() { - case "$prev" in - --containerd|--engine-image|--registry-prefix|--version) - return - ;; - esac - - case "$cur" in - -*) - COMPREPLY=( $( compgen -W "--containerd --engine-image --help --registry-prefix --version" -- "$cur" ) ) - ;; - esac -} - - _docker_events() { _docker_system_events } @@ -5456,7 +5395,6 @@ _docker() { config container context - engine image network node diff --git a/docs/reference/commandline/cli.md b/docs/reference/commandline/cli.md index fb62a0bd7a..b5aef34692 100644 --- a/docs/reference/commandline/cli.md +++ b/docs/reference/commandline/cli.md @@ -2,6 +2,11 @@ title: "Use the Docker command line" description: "Docker's CLI command description and usage" keywords: "Docker, Docker documentation, CLI, command line" +redirect_from: + - /engine/reference/commandline/engine/ + - /engine/reference/commandline/engine_activate/ + - /engine/reference/commandline/engine_check/ + - /engine/reference/commandline/engine_update/ ---