github.com/docker/app@v0.9.1-beta3.0.20210611140623-a48f773ab002/internal/commands/run.go (about) 1 package commands 2 3 import ( 4 "fmt" 5 "os" 6 7 "github.com/docker/app/internal/packager" 8 9 "github.com/docker/app/internal/image" 10 11 "github.com/deislabs/cnab-go/driver" 12 "github.com/docker/app/internal/cliopts" 13 14 "github.com/deislabs/cnab-go/action" 15 "github.com/deislabs/cnab-go/credentials" 16 bdl "github.com/docker/app/internal/bundle" 17 "github.com/docker/app/internal/cnab" 18 "github.com/docker/app/internal/store" 19 "github.com/docker/cli/cli" 20 "github.com/docker/cli/cli/command" 21 "github.com/docker/docker/pkg/namesgenerator" 22 "github.com/pkg/errors" 23 "github.com/sirupsen/logrus" 24 "github.com/spf13/cobra" 25 ) 26 27 type runOptions struct { 28 cliopts.ParametersOptions 29 credentialOptions 30 orchestrator string 31 kubeNamespace string 32 stackName string 33 cnabBundle string 34 labels []string 35 } 36 37 const longDescription = `Run an App from an App image.` 38 39 const runExample = `- $ docker app run --name myrunningapp myrepo/myapp:mytag 40 - $ docker app run 34be4a0c5f50 --name myrunningapp` 41 42 func runCmd(dockerCli command.Cli, installerContext *cliopts.InstallerContextOptions) *cobra.Command { 43 var opts runOptions 44 45 cmd := &cobra.Command{ 46 Use: "run [OPTIONS] APP_IMAGE", 47 Aliases: []string{"deploy"}, 48 Short: "Run an App from an App image", 49 Long: longDescription, 50 Example: runExample, 51 RunE: func(cmd *cobra.Command, args []string) error { 52 if opts.cnabBundle != "" && len(args) != 0 { 53 return errors.Errorf( 54 "%q cannot run a bundle and an App image", 55 cmd.CommandPath(), 56 ) 57 } 58 if opts.cnabBundle == "" { 59 if err := cli.ExactArgs(1)(cmd, args); err != nil { 60 return err 61 } 62 return runDockerApp(dockerCli, args[0], opts, installerContext) 63 } 64 return runCnab(dockerCli, opts, installerContext) 65 }, 66 } 67 opts.ParametersOptions.AddFlags(cmd.Flags()) 68 opts.credentialOptions.addFlags(cmd.Flags()) 69 cmd.Flags().StringVar(&opts.orchestrator, "orchestrator", "", "Orchestrator to install on (swarm, kubernetes)") 70 cmd.Flags().StringVar(&opts.kubeNamespace, "namespace", "default", "Kubernetes namespace to install into") 71 cmd.Flags().StringVar(&opts.stackName, "name", "", "Assign a name to the installation") 72 cmd.Flags().StringVar(&opts.cnabBundle, "cnab-bundle-json", "", "Run a CNAB bundle instead of a Docker App") 73 cmd.Flags().StringArrayVar(&opts.labels, "label", nil, "Label to add to services") 74 75 //nolint:errcheck 76 cmd.Flags().SetAnnotation("cnab-bundle-json", "experimentalCLI", []string{"true"}) 77 78 return cmd 79 } 80 81 func runCnab(dockerCli command.Cli, opts runOptions, installerContext *cliopts.InstallerContextOptions) error { 82 bndl, err := image.FromFile(opts.cnabBundle) 83 if err != nil { 84 return errors.Wrapf(err, "failed to read bundle %q", opts.cnabBundle) 85 } 86 return runBundle(dockerCli, bndl, opts, installerContext, "") 87 } 88 89 func runDockerApp(dockerCli command.Cli, appname string, opts runOptions, installerContext *cliopts.InstallerContextOptions) error { 90 imageStore, err := prepareImageStore() 91 if err != nil { 92 return err 93 } 94 95 bndl, ref, err := cnab.GetBundle(dockerCli, imageStore, appname) 96 if err != nil { 97 return errors.Wrapf(err, "Unable to find App %q", appname) 98 } 99 return runBundle(dockerCli, bndl, opts, installerContext, ref.String()) 100 } 101 102 func runBundle(dockerCli command.Cli, bndl *image.AppImage, opts runOptions, installerContext *cliopts.InstallerContextOptions, ref string) (err error) { 103 if err := packager.CheckAppVersion(dockerCli.Err(), bndl.Bundle); err != nil { 104 return err 105 } 106 _, installationStore, credentialStore, err := prepareStores(dockerCli.CurrentContext()) 107 if err != nil { 108 return err 109 } 110 if err := bndl.Validate(); err != nil { 111 return err 112 } 113 installationName := opts.stackName 114 if installationName == "" { 115 installationName = namesgenerator.GetRandomName(0) 116 } 117 logrus.Debugf(`Looking for a previous installation "%q"`, installationName) 118 if installation, err := installationStore.Read(installationName); err == nil { 119 // A failed installation can be overridden, but with a warning 120 if IsInstallationFailed(installation) { 121 fmt.Fprintf(dockerCli.Err(), "WARNING: installing over previously failed installation %q\n", installationName) 122 } else { 123 // Return an error in case of successful installation, or even failed upgrade, which means 124 // their was already a successful installation. 125 return fmt.Errorf("Installation %q already exists, use 'docker app update' instead", installationName) 126 } 127 } else { 128 logrus.Debug(err) 129 } 130 installation, err := store.NewInstallation(installationName, ref, bndl) 131 if err != nil { 132 return err 133 } 134 135 driverImpl, errBuf, err := cnab.SetupDriver(installation, dockerCli, installerContext, os.Stdout) 136 if err != nil { 137 return err 138 } 139 140 if err := bdl.MergeBundleParameters(installation, 141 bdl.WithFileParameters(opts.ParametersFiles), 142 bdl.WithCommandLineParameters(opts.Overrides), 143 bdl.WithLabels(opts.labels), 144 bdl.WithOrchestratorParameters(opts.orchestrator, opts.kubeNamespace), 145 bdl.WithSendRegistryAuth(opts.sendRegistryAuth), 146 ); err != nil { 147 return err 148 } 149 creds, err := prepareCredentialSet(bndl.Bundle, opts.CredentialSetOpts(dockerCli, credentialStore)...) 150 if err != nil { 151 return err 152 } 153 if err := credentials.Validate(creds, bndl.Credentials); err != nil { 154 return err 155 } 156 157 inst := &action.Install{ 158 Driver: driverImpl, 159 } 160 { 161 defer muteDockerCli(dockerCli)() 162 cfgFunc := func(op *driver.Operation) error { 163 op.Out = dockerCli.Out() 164 return nil 165 } 166 err = inst.Run(&installation.Claim, creds, cfgFunc, cnab.WithRelocationMap(installation)) 167 } 168 // Even if the installation failed, the installation is persisted with its failure status, 169 // so any installation needs a clean uninstallation. 170 err2 := installationStore.Store(installation) 171 if err != nil { 172 return fmt.Errorf("Failed to run App: %s\n%s", err, errBuf) 173 } 174 if err2 != nil { 175 return err2 176 } 177 178 fmt.Fprintf(os.Stdout, "App %q running on context %q\n", installationName, dockerCli.CurrentContext()) 179 return nil 180 }