github.com/itscaro/cli@v0.0.0-20190705081621-c9db0fe93829/cli/command/engine/activate.go (about) 1 package engine 2 3 import ( 4 "context" 5 "fmt" 6 "strings" 7 8 "github.com/docker/cli/cli/command" 9 "github.com/docker/cli/cli/command/formatter" 10 "github.com/docker/cli/internal/licenseutils" 11 clitypes "github.com/docker/cli/types" 12 "github.com/docker/docker/api/types" 13 "github.com/docker/licensing/model" 14 "github.com/pkg/errors" 15 "github.com/spf13/cobra" 16 ) 17 18 type activateOptions struct { 19 licenseFile string 20 version string 21 registryPrefix string 22 format string 23 image string 24 quiet bool 25 displayOnly bool 26 sockPath string 27 licenseLoginFunc func(ctx context.Context, authConfig *types.AuthConfig) (licenseutils.HubUser, error) 28 } 29 30 // newActivateCommand creates a new `docker engine activate` command 31 func newActivateCommand(dockerCli command.Cli) *cobra.Command { 32 var options activateOptions 33 options.licenseLoginFunc = licenseutils.Login 34 35 cmd := &cobra.Command{ 36 Use: "activate [OPTIONS]", 37 Short: "Activate Enterprise Edition", 38 Long: `Activate Enterprise Edition. 39 40 With this command you may apply an existing Docker enterprise license, or 41 interactively download one from Docker. In the interactive exchange, you can 42 sign up for a new trial, or download an existing license. If you are 43 currently running a Community Edition engine, the daemon will be updated to 44 the Enterprise Edition Docker engine with additional capabilities and long 45 term support. 46 47 For more information about different Docker Enterprise license types visit 48 https://www.docker.com/licenses 49 50 For non-interactive scriptable deployments, download your license from 51 https://hub.docker.com/ then specify the file with the '--license' flag. 52 `, 53 RunE: func(cmd *cobra.Command, args []string) error { 54 return runActivate(dockerCli, options) 55 }, 56 } 57 58 flags := cmd.Flags() 59 60 flags.StringVar(&options.licenseFile, "license", "", "License File") 61 flags.StringVar(&options.version, "version", "", "Specify engine version (default is to use currently running version)") 62 flags.StringVar(&options.registryPrefix, "registry-prefix", clitypes.RegistryPrefix, "Override the default location where engine images are pulled") 63 flags.StringVar(&options.image, "engine-image", "", "Specify engine image") 64 flags.StringVar(&options.format, "format", "", "Pretty-print licenses using a Go template") 65 flags.BoolVar(&options.displayOnly, "display-only", false, "only display license information and exit") 66 flags.BoolVar(&options.quiet, "quiet", false, "Only display available licenses by ID") 67 flags.StringVar(&options.sockPath, "containerd", "", "override default location of containerd endpoint") 68 69 return cmd 70 } 71 72 func runActivate(cli command.Cli, options activateOptions) error { 73 if !isRoot() { 74 return errors.New("this command must be run as a privileged user") 75 } 76 ctx := context.Background() 77 client, err := cli.NewContainerizedEngineClient(options.sockPath) 78 if err != nil { 79 return errors.Wrap(err, "unable to access local containerd") 80 } 81 defer client.Close() 82 83 authConfig, err := getRegistryAuth(cli, options.registryPrefix) 84 if err != nil { 85 return err 86 } 87 88 var license *model.IssuedLicense 89 90 // Lookup on hub if no license provided via params 91 if options.licenseFile == "" { 92 if license, err = getLicenses(ctx, authConfig, cli, options); err != nil { 93 return err 94 } 95 if options.displayOnly { 96 return nil 97 } 98 } else { 99 if license, err = licenseutils.LoadLocalIssuedLicense(ctx, options.licenseFile); err != nil { 100 return err 101 } 102 } 103 summary, err := licenseutils.GetLicenseSummary(ctx, *license) 104 if err != nil { 105 return err 106 } 107 fmt.Fprintf(cli.Out(), "License: %s\n", summary) 108 if options.displayOnly { 109 return nil 110 } 111 dclient := cli.Client() 112 if err = licenseutils.ApplyLicense(ctx, dclient, license); err != nil { 113 return err 114 } 115 116 // Short circuit if the user didn't specify a version and we're already running enterprise 117 if options.version == "" { 118 serverVersion, err := dclient.ServerVersion(ctx) 119 if err != nil { 120 return err 121 } 122 if strings.Contains(strings.ToLower(serverVersion.Platform.Name), "enterprise") { 123 fmt.Fprintln(cli.Out(), "Successfully activated engine license on existing enterprise engine.") 124 return nil 125 } 126 options.version = serverVersion.Version 127 } 128 129 opts := clitypes.EngineInitOptions{ 130 RegistryPrefix: options.registryPrefix, 131 EngineImage: options.image, 132 EngineVersion: options.version, 133 } 134 135 if err := client.ActivateEngine(ctx, opts, cli.Out(), authConfig); err != nil { 136 return err 137 } 138 fmt.Fprintln(cli.Out(), `Successfully activated engine. 139 Restart docker with 'systemctl restart docker' to complete the activation.`) 140 return nil 141 } 142 143 func getLicenses(ctx context.Context, authConfig *types.AuthConfig, cli command.Cli, options activateOptions) (*model.IssuedLicense, error) { 144 user, err := options.licenseLoginFunc(ctx, authConfig) 145 if err != nil { 146 return nil, err 147 } 148 fmt.Fprintf(cli.Out(), "Looking for existing licenses for %s...\n", user.User.Username) 149 subs, err := user.GetAvailableLicenses(ctx) 150 if err != nil { 151 return nil, err 152 } 153 if len(subs) == 0 { 154 return doTrialFlow(ctx, cli, user) 155 } 156 157 format := options.format 158 if len(format) == 0 { 159 format = formatter.TableFormatKey 160 } 161 162 updatesCtx := formatter.Context{ 163 Output: cli.Out(), 164 Format: NewSubscriptionsFormat(format, options.quiet), 165 Trunc: false, 166 } 167 if err := SubscriptionsWrite(updatesCtx, subs); err != nil { 168 return nil, err 169 } 170 if options.displayOnly { 171 return nil, nil 172 } 173 fmt.Fprintf(cli.Out(), "Please pick a license by number: ") 174 var num int 175 if _, err := fmt.Fscan(cli.In(), &num); err != nil { 176 return nil, errors.Wrap(err, "failed to read user input") 177 } 178 if num < 0 || num >= len(subs) { 179 return nil, fmt.Errorf("invalid choice") 180 } 181 return user.GetIssuedLicense(ctx, subs[num].ID) 182 } 183 184 func doTrialFlow(ctx context.Context, cli command.Cli, user licenseutils.HubUser) (*model.IssuedLicense, error) { 185 if !command.PromptForConfirmation(cli.In(), cli.Out(), 186 "No existing licenses found, would you like to set up a new Enterprise Basic Trial license?") { 187 return nil, fmt.Errorf("you must have an existing enterprise license or generate a new trial to use the Enterprise Docker Engine") 188 } 189 targetID := user.User.ID 190 // If the user is a member of any organizations, allow trials generated against them 191 if len(user.Orgs) > 0 { 192 fmt.Fprintf(cli.Out(), "%d\t%s\n", 0, user.User.Username) 193 for i, org := range user.Orgs { 194 fmt.Fprintf(cli.Out(), "%d\t%s\n", i+1, org.Orgname) 195 } 196 fmt.Fprintf(cli.Out(), "Please choose an account to generate the trial in:") 197 var num int 198 if _, err := fmt.Fscan(cli.In(), &num); err != nil { 199 return nil, errors.Wrap(err, "failed to read user input") 200 } 201 if num < 0 || num > len(user.Orgs) { 202 return nil, fmt.Errorf("invalid choice") 203 } 204 if num > 0 { 205 targetID = user.Orgs[num-1].ID 206 } 207 } 208 return user.GenerateTrialLicense(ctx, targetID) 209 }