github.com/Azure/draft-classic@v0.16.0/cmd/draft/up.go (about) 1 package main 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "os" 8 "path/filepath" 9 10 "github.com/Azure/go-autorest/autorest" 11 azurecli "github.com/Azure/go-autorest/autorest/azure/cli" 12 "github.com/docker/cli/cli/command" 13 cliconfig "github.com/docker/cli/cli/config" 14 dockerdebug "github.com/docker/cli/cli/debug" 15 dockerflags "github.com/docker/cli/cli/flags" 16 "github.com/docker/cli/opts" 17 "github.com/docker/go-connections/tlsconfig" 18 "github.com/spf13/cobra" 19 "github.com/spf13/pflag" 20 "golang.org/x/net/context" 21 "k8s.io/client-go/rest" 22 23 "github.com/Azure/draft/pkg/azure/containerregistry" 24 "github.com/Azure/draft/pkg/azure/iam" 25 "github.com/Azure/draft/pkg/builder" 26 azurecontainerbuilder "github.com/Azure/draft/pkg/builder/azure" 27 dockercontainerbuilder "github.com/Azure/draft/pkg/builder/docker" 28 "github.com/Azure/draft/pkg/cmdline" 29 "github.com/Azure/draft/pkg/draft/draftpath" 30 "github.com/Azure/draft/pkg/local" 31 "github.com/Azure/draft/pkg/storage/kube/configmap" 32 "github.com/Azure/draft/pkg/tasks" 33 ) 34 35 const upDesc = ` 36 This command builds a container image using Docker, pushes it to a container registry 37 and then instructs helm to install the chart, referencing the image just built. 38 ` 39 40 const ( 41 ignoreFileName = ".draftignore" 42 dockerTLSEnvVar = "DOCKER_TLS" 43 dockerTLSVerifyEnvVar = "DOCKER_TLS_VERIFY" 44 tasksTOMLFile = ".draft-tasks.toml" 45 ) 46 47 var ( 48 dockerCertPath = os.Getenv("DOCKER_CERT_PATH") 49 autoConnect bool 50 ) 51 52 type upCmd struct { 53 out io.Writer 54 src string 55 home draftpath.Home 56 // storage engine draft should use for storing builds, logs, etc. 57 storageEngine string 58 // options common to the docker client and the daemon. 59 dockerClientOptions *dockerflags.ClientOptions 60 } 61 62 func defaultDockerTLS() bool { 63 return os.Getenv(dockerTLSEnvVar) != "" 64 } 65 66 func defaultDockerTLSVerify() bool { 67 return os.Getenv(dockerTLSVerifyEnvVar) != "" 68 } 69 70 func dockerPreRun(opts *dockerflags.ClientOptions) { 71 dockerflags.SetLogLevel(opts.Common.LogLevel) 72 73 if opts.ConfigDir != "" { 74 cliconfig.SetDir(opts.ConfigDir) 75 } 76 77 if opts.Common.Debug { 78 dockerdebug.Enable() 79 } 80 } 81 82 func newUpCmd(out io.Writer) *cobra.Command { 83 var ( 84 up = &upCmd{ 85 out: out, 86 dockerClientOptions: dockerflags.NewClientOptions(), 87 } 88 runningEnvironment string 89 f *pflag.FlagSet 90 ) 91 92 cmd := &cobra.Command{ 93 Use: "up [path]", 94 Short: "build and push Docker image, then install the Helm chart, referencing the image just built", 95 Long: upDesc, 96 PersistentPreRun: func(c *cobra.Command, args []string) { 97 rootCmd.PersistentPreRunE(c, args) 98 up.dockerClientOptions.Common.SetDefaultOptions(f) 99 dockerPreRun(up.dockerClientOptions) 100 }, 101 RunE: func(_ *cobra.Command, args []string) (err error) { 102 if len(args) > 0 { 103 up.src = args[0] 104 } 105 if up.src == "" || up.src == "." { 106 if up.src, err = os.Getwd(); err != nil { 107 return err 108 } 109 } 110 up.home = draftpath.Home(homePath()) 111 return up.run(runningEnvironment) 112 }, 113 } 114 115 f = cmd.Flags() 116 f.StringVarP(&runningEnvironment, environmentFlagName, environmentFlagShorthand, defaultDraftEnvironment(), environmentFlagUsage) 117 f.BoolVar(&up.dockerClientOptions.Common.Debug, "docker-debug", false, "Enable debug mode") 118 f.StringVar(&up.dockerClientOptions.Common.LogLevel, "docker-log-level", "info", `Set the logging level ("debug"|"info"|"warn"|"error"|"fatal")`) 119 f.BoolVar(&up.dockerClientOptions.Common.TLS, "docker-tls", defaultDockerTLS(), "Use TLS; implied by --tlsverify") 120 f.BoolVar(&up.dockerClientOptions.Common.TLSVerify, fmt.Sprintf("docker-%s", dockerflags.FlagTLSVerify), defaultDockerTLSVerify(), "Use TLS and verify the remote") 121 f.StringVar(&up.dockerClientOptions.ConfigDir, "docker-config", cliconfig.Dir(), "Location of client config files") 122 f.BoolVarP(&autoConnect, "auto-connect", "", false, "specifies if draft up should automatically connect to the application") 123 124 up.dockerClientOptions.Common.TLSOptions = &tlsconfig.Options{ 125 CAFile: filepath.Join(dockerCertPath, dockerflags.DefaultCaFile), 126 CertFile: filepath.Join(dockerCertPath, dockerflags.DefaultCertFile), 127 KeyFile: filepath.Join(dockerCertPath, dockerflags.DefaultKeyFile), 128 } 129 130 tlsOptions := up.dockerClientOptions.Common.TLSOptions 131 f.Var(opts.NewQuotedString(&tlsOptions.CAFile), "docker-tlscacert", "Trust certs signed only by this CA") 132 f.Var(opts.NewQuotedString(&tlsOptions.CertFile), "docker-tlscert", "Path to TLS certificate file") 133 f.Var(opts.NewQuotedString(&tlsOptions.KeyFile), "docker-tlskey", "Path to TLS key file") 134 135 hostOpt := opts.NewNamedListOptsRef("docker-hosts", &up.dockerClientOptions.Common.Hosts, opts.ValidateHost) 136 f.Var(hostOpt, "docker-host", "Daemon socket(s) to connect to") 137 138 return cmd 139 } 140 141 func (u *upCmd) run(environment string) (err error) { 142 var ( 143 buildctx *builder.Context 144 kubeConfig *rest.Config 145 ctx = context.Background() 146 bldr = builder.New() 147 ) 148 bldr.LogsDir = u.home.Logs() 149 150 taskList, err := tasks.Load(tasksTOMLFile) 151 if err != nil { 152 if err == tasks.ErrNoTaskFile { 153 debug(err.Error()) 154 } else { 155 return err 156 } 157 } else { 158 if _, err = taskList.Run(tasks.DefaultRunner, tasks.PreUp, ""); err != nil { 159 return err 160 } 161 } 162 163 if buildctx, err = builder.LoadWithEnv(u.src, environment); err != nil { 164 return fmt.Errorf("failed loading build context with env %q: %v", environment, err) 165 } 166 167 if configuredBuilder, ok := globalConfig[containerBuilder.name]; ok { 168 buildctx.Env.ContainerBuilder = configuredBuilder 169 } 170 171 // if a registry has been set in their global config but nothing was in draft.toml, use that instead 172 if reg, ok := globalConfig[registry.name]; ok { 173 buildctx.Env.Registry = reg 174 } 175 176 if configuredResourceGroup, ok := globalConfig[resourceGroupName.name]; ok { 177 buildctx.Env.ResourceGroupName = configuredResourceGroup 178 } 179 180 if buildctx.Env.Registry == "" { 181 // give a way for minikube users (and users who understand what they're doing) a way to opt out 182 if _, ok := globalConfig[disablePushWarning.name]; !ok { 183 fmt.Fprintln(u.out, "WARNING: no registry has been set, therefore Draft will not push to a container registry. This can be fixed by running `draft config set registry docker.io/myusername`") 184 fmt.Fprintln(u.out, "Hint: this warning can be disabled by running `draft config set disable-push-warning 1`") 185 } 186 } 187 188 var cb builder.ContainerBuilder 189 switch buildctx.Env.ContainerBuilder { 190 case "acrbuild": 191 subscription, err := getSubscriptionFromProfile() 192 if err != nil { 193 return fmt.Errorf("Could not retrieve azure profile information: %v", err) 194 } 195 token, err := iam.GetToken(iam.AuthGrantType()) 196 if err != nil { 197 return fmt.Errorf("Could not retrieve adal token: %v", err) 198 } 199 auth := autorest.NewBearerAuthorizer(&token) 200 registriesClient := containerregistry.NewRegistriesClient(subscription.ID) 201 registriesClient.Authorizer = auth 202 registriesClient.AddToUserAgent(containerregistry.UserAgent()) 203 buildsClient := containerregistry.NewBuildsClient(subscription.ID) 204 buildsClient.Authorizer = auth 205 buildsClient.AddToUserAgent(containerregistry.UserAgent()) 206 cb = &azurecontainerbuilder.Builder{ 207 RegistryClient: registriesClient, 208 BuildsClient: buildsClient, 209 AdalToken: token, 210 Subscription: subscription, 211 } 212 default: 213 // setup docker 214 cli := &command.DockerCli{} 215 if err := cli.Initialize(u.dockerClientOptions); err != nil { 216 return fmt.Errorf("failed to create docker client: %v", err) 217 } 218 cb = &dockercontainerbuilder.Builder{ 219 DockerClient: cli, 220 } 221 } 222 bldr.ContainerBuilder = cb 223 224 // setup kube 225 bldr.Kube, kubeConfig, err = getKubeClient(kubeContext) 226 if err != nil { 227 return fmt.Errorf("Could not get a kube client: %s", err) 228 } 229 bldr.Helm, err = setupHelm(bldr.Kube, kubeConfig, tillerNamespace) 230 if err != nil { 231 return fmt.Errorf("Could not get a helm client: %s", err) 232 } 233 234 // setup the storage engine 235 bldr.Storage = configmap.NewConfigMaps(bldr.Kube.CoreV1().ConfigMaps(tillerNamespace)) 236 progressC := bldr.Up(ctx, buildctx) 237 cmdline.Display(ctx, buildctx.Env.Name, progressC, cmdline.WithBuildID(bldr.ID)) 238 239 if buildctx.Env.AutoConnect || autoConnect { 240 c := newConnectCmd(u.out) 241 return c.RunE(c, []string{}) 242 } 243 244 if err := runPostDeployTasks(taskList, bldr.ID); err != nil { 245 debug(err.Error()) 246 return nil 247 } 248 249 return nil 250 } 251 252 func runPostDeployTasks(taskList *tasks.Tasks, buildID string) error { 253 if taskList == nil || len(taskList.PostDeploy) == 0 { 254 return errors.New("No post deploy tasks to run") 255 } 256 257 app, err := local.DeployedApplication(draftToml, runningEnvironment) 258 if err != nil { 259 return err 260 } 261 262 client, _, err := getKubeClient(kubeContext) 263 if err != nil { 264 return err 265 } 266 267 names, err := app.GetPodNames(buildID, client) 268 if err != nil { 269 return err 270 } 271 272 for _, name := range names { 273 _, err := taskList.Run(tasks.DefaultRunner, tasks.PostDeploy, name) 274 if err != nil { 275 debug("error running task: %v", err) 276 } 277 } 278 279 return nil 280 } 281 282 func getSubscriptionFromProfile() (azurecli.Subscription, error) { 283 profilePath, err := azurecli.ProfilePath() 284 if err != nil { 285 return azurecli.Subscription{}, err 286 } 287 profile, err := azurecli.LoadProfile(profilePath) 288 if err != nil { 289 return azurecli.Subscription{}, err 290 } 291 for _, sub := range profile.Subscriptions { 292 if sub.IsDefault { 293 return sub, nil 294 } 295 } 296 return azurecli.Subscription{}, fmt.Errorf("could not find a default subscription ID from %s", profilePath) 297 }