github.com/olli-ai/jx/v2@v2.0.400-0.20210921045218-14731b4dd448/pkg/cmd/add/add_app.go (about)

     1  package add
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/olli-ai/jx/v2/pkg/cmd/helper"
     7  
     8  	"github.com/olli-ai/jx/v2/pkg/cmd/opts"
     9  	"github.com/olli-ai/jx/v2/pkg/cmd/templates"
    10  
    11  	"github.com/olli-ai/jx/v2/pkg/apps"
    12  
    13  	"github.com/olli-ai/jx/v2/pkg/io/secrets"
    14  
    15  	"github.com/olli-ai/jx/v2/pkg/util"
    16  
    17  	jenkinsv1 "github.com/jenkins-x/jx-api/pkg/apis/jenkins.io/v1"
    18  
    19  	"github.com/olli-ai/jx/v2/pkg/kube"
    20  	"github.com/pkg/errors"
    21  	"github.com/spf13/cobra"
    22  )
    23  
    24  // AddAppOptions the options for the create spring command
    25  type AddAppOptions struct {
    26  	AddOptions
    27  
    28  	GitOps bool
    29  	DevEnv *jenkinsv1.Environment
    30  
    31  	Repo        string
    32  	Username    string
    33  	Password    string
    34  	Alias       string
    35  	Prefixes    []string
    36  	Namespace   string
    37  	Version     string
    38  	ReleaseName string
    39  	SetValues   []string
    40  	ValuesFiles []string
    41  	HelmUpdate  bool
    42  	AutoMerge   bool
    43  
    44  	// Used for testing
    45  	CloneDir string
    46  }
    47  
    48  const (
    49  	optionHelmUpdate = "helm-update"
    50  	optionValues     = "values"
    51  	optionSet        = "set"
    52  	optionAlias      = "alias"
    53  	optionNamespace  = "namespace"
    54  )
    55  
    56  var (
    57  	add_app_long = templates.LongDesc(`
    58  		Adds an App to Jenkins X (an app is similar to an addon),
    59  `)
    60  	add_app_example = templates.Examples(`
    61  		# Add an app
    62  		jx add app jx-app-jacoco
    63  
    64  		# Add an app from a local path
    65  		jx add app .
    66          
    67  		# Add an app from git repository
    68  		jx add app https://github.com/jenkins-x-apps/jx-app-kubeless.git
    69  `)
    70  )
    71  
    72  // NewCmdAddApp creates a command object for the "create" command
    73  func NewCmdAddApp(commonOpts *opts.CommonOptions) *cobra.Command {
    74  	options := &AddAppOptions{
    75  		AddOptions: AddOptions{
    76  			CommonOptions: commonOpts,
    77  		},
    78  	}
    79  
    80  	cmd := &cobra.Command{
    81  		Use:     "app",
    82  		Short:   "Adds an App (an app is similar to an addon)",
    83  		Long:    add_app_long,
    84  		Example: add_app_example,
    85  		Run: func(cmd *cobra.Command, args []string) {
    86  			options.Cmd = cmd
    87  			options.Args = args
    88  			err := options.Run()
    89  			helper.CheckErr(err)
    90  		},
    91  	}
    92  
    93  	options.addFlags(cmd, kube.DefaultNamespace)
    94  	return cmd
    95  }
    96  
    97  func (o *AddAppOptions) addFlags(cmd *cobra.Command, defaultNamespace string) {
    98  
    99  	// Common flags
   100  
   101  	cmd.Flags().StringVarP(&o.Version, "version", "v", "",
   102  		"The chart version to install")
   103  	cmd.Flags().StringVarP(&o.Repo, "repository", "", "",
   104  		"The repository from which the app should be installed (default specified in your dev environment)")
   105  	cmd.Flags().StringVarP(&o.Username, "username", "", "",
   106  		"The username for the repository")
   107  	cmd.Flags().StringVarP(&o.Password, "password", "", "",
   108  		"The password for the repository")
   109  	cmd.Flags().StringVarP(&o.Alias, optionAlias, "", "",
   110  		"An alias to use for the app if you wish to install multiple instances of the same app")
   111  	cmd.Flags().BoolVarP(&o.HelmUpdate, optionHelmUpdate, "", true,
   112  		"Should we run helm update first to ensure we use the latest version (available when NOT using GitOps for your dev environment)")
   113  	cmd.Flags().StringVarP(&o.Namespace, optionNamespace, "n", "", "The Namespace to install into (available when NOT using GitOps for your dev environment)")
   114  	cmd.Flags().StringArrayVarP(&o.ValuesFiles, optionValues, "f", []string{}, "List of locations for values files, "+
   115  		"can be local files or URLs (available when NOT using GitOps for your dev environment)")
   116  	cmd.Flags().StringArrayVarP(&o.SetValues, optionSet, "s", []string{},
   117  		"The chart set values (can specify multiple or separate values with commas: key1=val1,key2=val2) (available when NOT using GitOps for your dev environment)")
   118  	cmd.Flags().BoolVarP(&o.AutoMerge, "auto-merge", "", false, "Automatically merge GitOps pull requests that pass CI")
   119  }
   120  
   121  // Run implements this command
   122  func (o *AddAppOptions) Run() error {
   123  	o.GitOps, o.DevEnv = o.GetDevEnv()
   124  	if o.DevEnv == nil {
   125  		return helper.ErrDevEnvNotFound
   126  	}
   127  	if o.Repo == "" {
   128  		o.Repo = o.DevEnv.Spec.TeamSettings.AppsRepository
   129  	}
   130  	if o.Repo == "" {
   131  		o.Repo = kube.DefaultChartMuseumURL
   132  	}
   133  
   134  	jxClient, ns, err := o.JXClientAndDevNamespace()
   135  	if err != nil {
   136  		return errors.Wrapf(err, "getting jx client")
   137  	}
   138  	kubeClient, _, err := o.KubeClientAndDevNamespace()
   139  	if err != nil {
   140  		return errors.Wrapf(err, "getting kubeClient")
   141  	}
   142  	if o.Namespace == "" {
   143  		o.Namespace = ns
   144  	}
   145  	installOpts := apps.InstallOptions{
   146  		IOFileHandles: o.GetIOFileHandles(),
   147  		DevEnv:        o.DevEnv,
   148  		Verbose:       o.Verbose,
   149  		GitOps:        o.GitOps,
   150  		BatchMode:     o.BatchMode,
   151  		AutoMerge:     o.AutoMerge,
   152  		SecretsScheme: "vault",
   153  
   154  		Helmer:              o.Helm(),
   155  		Namespace:           o.Namespace,
   156  		KubeClient:          kubeClient,
   157  		JxClient:            jxClient,
   158  		InstallTimeout:      opts.DefaultInstallTimeout,
   159  		EnvironmentCloneDir: o.CloneDir,
   160  	}
   161  
   162  	if o.GitOps {
   163  		msg := "unable to specify --%s when using GitOps for your dev environment"
   164  		if !o.HelmUpdate {
   165  			return util.InvalidOptionf(optionHelmUpdate, o.HelmUpdate, msg, optionHelmUpdate)
   166  		}
   167  		if o.Namespace != "" && o.Namespace != kube.DefaultNamespace {
   168  			return util.InvalidOptionf(optionNamespace, o.Namespace, msg, optionNamespace)
   169  		}
   170  		if len(o.SetValues) > 0 {
   171  			return util.InvalidOptionf(optionSet, o.SetValues, msg, optionSet)
   172  		}
   173  		if len(o.ValuesFiles) > 1 {
   174  			return util.InvalidOptionf(optionValues, o.SetValues,
   175  				"no more than one --%s can be specified when using GitOps for your dev environment", optionValues)
   176  		}
   177  
   178  		gitProvider, _, err := o.CreateGitProviderForURLWithoutKind(o.DevEnv.Spec.Source.URL)
   179  		if err != nil {
   180  			return errors.Wrapf(err, "creating git provider for %s", o.DevEnv.Spec.Source.URL)
   181  		}
   182  		installOpts.GitProvider = gitProvider
   183  		installOpts.Gitter = o.Git()
   184  	}
   185  	if !o.GitOps {
   186  		if o.Alias != "" && o.ReleaseName == "" {
   187  			bin, noTiller, helmTemplate, err := o.TeamHelmBin()
   188  			if err != nil {
   189  				return err
   190  			}
   191  			if bin != "helm" || noTiller || helmTemplate {
   192  				o.ReleaseName = o.Alias
   193  			} else {
   194  				o.ReleaseName = o.Alias + "-" + o.Namespace
   195  			}
   196  		}
   197  
   198  	}
   199  	err = o.EnsureHelm()
   200  	if err != nil {
   201  		return errors.Wrap(err, "failed to ensure that helm is present")
   202  	}
   203  	if o.GetSecretsLocation() == secrets.VaultLocationKind {
   204  		teamName, _, err := o.TeamAndEnvironmentNames()
   205  		if err != nil {
   206  			return err
   207  		}
   208  		installOpts.TeamName = teamName
   209  		client, err := o.SystemVaultClient("")
   210  		if err != nil {
   211  			return err
   212  		}
   213  		installOpts.VaultClient = client
   214  	}
   215  
   216  	args := o.Args
   217  	if len(args) == 0 {
   218  		return o.Cmd.Help()
   219  	}
   220  	if len(args) > 1 {
   221  		return o.Cmd.Help()
   222  	}
   223  
   224  	if o.Repo == "" {
   225  		return fmt.Errorf("must specify a repository")
   226  	}
   227  
   228  	var version string
   229  	if o.Version != "" {
   230  		version = o.Version
   231  	}
   232  	app := args[0]
   233  	return installOpts.AddApp(app, version, o.Repo, o.Username, o.Password, o.ReleaseName, o.ValuesFiles, o.SetValues,
   234  		o.Alias, o.HelmUpdate)
   235  }
   236  
   237  // TODO create test for GitOps