| // Licensed to the Apache Software Foundation (ASF) under one or more |
| // contributor license agreements. See the NOTICE file distributed with |
| // this work for additional information regarding copyright ownership. |
| // The ASF licenses this file to You under the Apache License, Version 2.0 |
| // (the "License"); you may not use this file except in compliance with |
| // the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package cmd |
| |
| import ( |
| "fmt" |
| "os" |
| "strings" |
| ) |
| |
| import ( |
| "github.com/AlecAivazis/survey/v2" |
| |
| "github.com/ory/viper" |
| |
| "github.com/spf13/cobra" |
| ) |
| |
| import ( |
| "github.com/apache/dubbo-kubernetes/app/dubboctl/internal/builders/dockerfile" |
| "github.com/apache/dubbo-kubernetes/app/dubboctl/internal/builders/pack" |
| "github.com/apache/dubbo-kubernetes/app/dubboctl/internal/dubbo" |
| "github.com/apache/dubbo-kubernetes/app/dubboctl/internal/util" |
| ) |
| |
| func addBuild(baseCmd *cobra.Command, newClient ClientFactory) { |
| cmd := &cobra.Command{ |
| Use: "build", |
| Short: "Build the image for the application", |
| Long: ``, |
| SuggestFor: []string{"biuld", "buidl", "built"}, |
| PreRunE: bindEnv("useDockerfile", "image", "path", "push", "force", "envs", |
| "builder-image"), |
| RunE: func(cmd *cobra.Command, args []string) error { |
| return runBuildCmd(cmd, newClient) |
| }, |
| } |
| |
| cmd.Flags().StringP("builder-image", "b", "", |
| "Specify a custom builder image for use by the builder other than its default.") |
| cmd.Flags().BoolP("useDockerfile", "d", false, |
| "Use the dockerfile with the specified path to build") |
| cmd.Flags().StringP("image", "i", "", |
| "Container image( [registry]/[namespace]/[name]:[tag] )") |
| cmd.Flags().BoolP("push", "", true, |
| "Whether to push the image to the registry center by the way") |
| cmd.Flags().BoolP("force", "f", false, |
| "Whether to force build") |
| cmd.Flags().StringArrayP("envs", "e", []string{}, |
| "environment variable for an application, KEY=VALUE format") |
| addPathFlag(cmd) |
| baseCmd.AddCommand(cmd) |
| } |
| |
| func runBuildCmd(cmd *cobra.Command, newClient ClientFactory) error { |
| if err := util.CreatePaths(); err != nil { |
| return err |
| } |
| cfg := newBuildConfig(cmd) |
| f, err := dubbo.NewDubbo(cfg.Path) |
| if err != nil { |
| return err |
| } |
| |
| cfg, err = cfg.Prompt(f) |
| if err != nil { |
| return err |
| } |
| if !f.Initialized() { |
| return dubbo.NewErrNotInitialized(f.Root) |
| } |
| cfg.Configure(f) |
| |
| clientOptions, err := cfg.buildclientOptions() |
| if err != nil { |
| return err |
| } |
| client, done := newClient(clientOptions...) |
| defer done() |
| if f.Built() && !cfg.Force { |
| fmt.Fprintln(cmd.OutOrStdout(), "The Application is up to date, If you still want to build, use `--force true`") |
| return nil |
| } |
| if f, err = client.Build(cmd.Context(), f); err != nil { |
| return err |
| } |
| if cfg.Push { |
| if f, err = client.Push(cmd.Context(), f); err != nil { |
| return err |
| } |
| } |
| |
| if err = f.Write(); err != nil { |
| return err |
| } |
| |
| return nil |
| } |
| |
| func (c *buildConfig) Prompt(d *dubbo.Dubbo) (*buildConfig, error) { |
| var err error |
| if !util.InteractiveTerminal() { |
| return c, nil |
| } |
| |
| if c.Image == "" && d.Image == "" { |
| |
| qs := []*survey.Question{ |
| { |
| Name: "image", |
| Validate: survey.Required, |
| Prompt: &survey.Input{ |
| Message: "the container image( [registry]/[namespace]/[name]:[tag] ). For example: docker.io/sjmshsh/testapp:latest", |
| Default: c.Image, |
| }, |
| }, |
| } |
| if err = survey.Ask(qs, c); err != nil { |
| return c, err |
| } |
| } |
| return c, err |
| } |
| |
| func (c buildConfig) buildclientOptions() ([]dubbo.Option, error) { |
| var o []dubbo.Option |
| |
| if c.UseDockerfile { |
| o = append(o, dubbo.WithBuilder(dockerfile.NewBuilder())) |
| } else { |
| o = append(o, dubbo.WithBuilder(pack.NewBuilder())) |
| } |
| |
| return o, nil |
| } |
| |
| type buildConfig struct { |
| Envs []string |
| Force bool |
| UseDockerfile bool |
| // Push the resulting image to the registry after building. |
| Push bool |
| // BuilderImage is the image (name or mapping) to use for building. Usually |
| // set automatically. |
| BuilderImage string |
| Image string |
| |
| // Path of the application implementation on local disk. Defaults to current |
| // working directory of the process. |
| Path string |
| } |
| |
| func newBuildConfig(cmd *cobra.Command) *buildConfig { |
| c := &buildConfig{ |
| Envs: viper.GetStringSlice("envs"), |
| Force: viper.GetBool("force"), |
| UseDockerfile: viper.GetBool("useDockerfile"), |
| Push: viper.GetBool("push"), |
| BuilderImage: viper.GetString("builder-image"), |
| Image: viper.GetString("image"), |
| Path: viper.GetString("path"), |
| } |
| |
| var err error |
| if c.Envs, err = cmd.Flags().GetStringArray("envs"); err != nil { |
| fmt.Fprintf(cmd.OutOrStdout(), "error reading envs: %v\n", err) |
| } |
| return c |
| } |
| |
| func (c *buildConfig) Configure(f *dubbo.Dubbo) { |
| if c.Path == "" { |
| root, err := os.Getwd() |
| if err != nil { |
| return |
| } |
| f.Root = root |
| } else { |
| f.Root = c.Path |
| } |
| if c.BuilderImage != "" { |
| f.Build.BuilderImages["pack"] = c.BuilderImage |
| } |
| if c.Image != "" { |
| f.Image = c.Image |
| } |
| |
| var envs []dubbo.Env |
| for _, pair := range c.Envs { |
| parts := strings.Split(pair, "=") |
| |
| if len(parts) == 2 { |
| key := &parts[0] |
| value := &parts[1] |
| env := dubbo.Env{ |
| Name: key, |
| Value: value, |
| } |
| envs = append(envs, env) |
| } |
| } |
| f.Build.BuildEnvs = envs |
| } |