blob: 44d9cb02ec2973d09d47385855b956ef79c826e6 [file] [log] [blame]
// 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
}