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

     1  package create
     2  
     3  import (
     4  	"encoding/base64"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  	"sort"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/olli-ai/jx/v2/pkg/cmd/opts/upgrade"
    14  	"github.com/olli-ai/jx/v2/pkg/errorutil"
    15  	"github.com/olli-ai/jx/v2/pkg/vault/create"
    16  
    17  	createoptions "github.com/olli-ai/jx/v2/pkg/cmd/create/options"
    18  	cmdvault "github.com/olli-ai/jx/v2/pkg/cmd/create/vault"
    19  
    20  	"github.com/olli-ai/jx/v2/pkg/packages"
    21  
    22  	"github.com/olli-ai/jx/v2/pkg/prow"
    23  
    24  	"github.com/olli-ai/jx/v2/pkg/platform"
    25  
    26  	"github.com/olli-ai/jx/v2/pkg/cmd/opts/step"
    27  
    28  	"github.com/olli-ai/jx/v2/pkg/versionstream"
    29  
    30  	"github.com/olli-ai/jx/v2/pkg/cmd/edit"
    31  	"github.com/olli-ai/jx/v2/pkg/cmd/initcmd"
    32  	"github.com/olli-ai/jx/v2/pkg/kube/naming"
    33  
    34  	"github.com/spf13/viper"
    35  
    36  	"github.com/olli-ai/jx/v2/pkg/cmd/step/env"
    37  
    38  	"github.com/olli-ai/jx/v2/pkg/cmd/helper"
    39  
    40  	gkeStorage "github.com/olli-ai/jx/v2/pkg/cloud/gke/storage"
    41  	"github.com/olli-ai/jx/v2/pkg/kube/cluster"
    42  
    43  	"k8s.io/helm/pkg/chartutil"
    44  
    45  	"github.com/olli-ai/jx/v2/pkg/cloud"
    46  	"github.com/olli-ai/jx/v2/pkg/cloud/gke"
    47  	version2 "github.com/olli-ai/jx/v2/pkg/version"
    48  
    49  	"github.com/ghodss/yaml"
    50  
    51  	"github.com/Pallinder/go-randomdata"
    52  	"github.com/olli-ai/jx/v2/pkg/io/secrets"
    53  	kubevault "github.com/olli-ai/jx/v2/pkg/kube/vault"
    54  	"github.com/olli-ai/jx/v2/pkg/vault"
    55  
    56  	jenkinsio "github.com/jenkins-x/jx-api/pkg/apis/jenkins.io"
    57  
    58  	v1 "github.com/jenkins-x/jx-api/pkg/apis/jenkins.io/v1"
    59  	"github.com/jenkins-x/jx-logging/pkg/log"
    60  	"github.com/olli-ai/jx/v2/pkg/addon"
    61  	"github.com/olli-ai/jx/v2/pkg/auth"
    62  	"github.com/olli-ai/jx/v2/pkg/cloud/aks"
    63  	"github.com/olli-ai/jx/v2/pkg/cloud/amazon"
    64  	"github.com/olli-ai/jx/v2/pkg/cloud/iks"
    65  	"github.com/olli-ai/jx/v2/pkg/cmd/opts"
    66  	"github.com/olli-ai/jx/v2/pkg/cmd/templates"
    67  	"github.com/olli-ai/jx/v2/pkg/config"
    68  	"github.com/olli-ai/jx/v2/pkg/features"
    69  	"github.com/olli-ai/jx/v2/pkg/gits"
    70  	"github.com/olli-ai/jx/v2/pkg/helm"
    71  	configio "github.com/olli-ai/jx/v2/pkg/io"
    72  	"github.com/olli-ai/jx/v2/pkg/kube"
    73  	"github.com/olli-ai/jx/v2/pkg/util"
    74  	pkgvault "github.com/olli-ai/jx/v2/pkg/vault"
    75  	"github.com/pkg/errors"
    76  	"github.com/spf13/cobra"
    77  	"gopkg.in/AlecAivazis/survey.v1"
    78  	"gopkg.in/src-d/go-git.v4"
    79  	core_v1 "k8s.io/api/core/v1"
    80  	storagev1 "k8s.io/api/storage/v1"
    81  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    82  	"k8s.io/client-go/kubernetes"
    83  )
    84  
    85  // ModifySecretCallback a callback for modifying a Secret for a given name
    86  type ModifySecretCallback func(string, func(*core_v1.Secret) error) (*core_v1.Secret, error)
    87  
    88  // ModifyConfigMapCallback a callback for modifying a ConfigMap for a given name
    89  type ModifyConfigMapCallback func(string, func(*core_v1.ConfigMap) error) (*core_v1.ConfigMap, error)
    90  
    91  // InstallOptions is the start of the data required to perform the operation.  As new fields are added, add them here instead of
    92  // referencing the cmd.Flags()
    93  type InstallOptions struct {
    94  	*opts.CommonOptions
    95  	gits.GitRepositoryOptions
    96  	CreateJenkinsUserOptions
    97  	CreateEnvOptions
    98  	config.AdminSecretsService
    99  	kubevault.AWSConfig
   100  
   101  	InitOptions initcmd.InitOptions
   102  	Flags       InstallFlags `mapstructure:"install"`
   103  
   104  	modifyConfigMapCallback ModifyConfigMapCallback
   105  	modifySecretCallback    ModifySecretCallback
   106  
   107  	installValues map[string]string
   108  }
   109  
   110  // InstallFlags flags for the install command
   111  type InstallFlags struct {
   112  	ConfigFile                  string
   113  	InstallOnly                 bool
   114  	Domain                      string
   115  	ExposeControllerURLTemplate string
   116  	ExposeControllerPathMode    string
   117  	AzureRegistrySubscription   string
   118  	DockerRegistry              string
   119  	DockerRegistryOrg           string
   120  	Provider                    string
   121  	VersionsRepository          string
   122  	VersionsGitRef              string
   123  	Version                     string
   124  	LocalHelmRepoName           string
   125  	Namespace                   string
   126  	CloudEnvRepository          string
   127  	NoDefaultEnvironments       bool
   128  	RemoteEnvironments          bool
   129  	DefaultEnvironmentPrefix    string
   130  	LocalCloudEnvironment       bool
   131  	EnvironmentGitOwner         string
   132  	Timeout                     string
   133  	HelmTLS                     bool
   134  	RegisterLocalHelmRepo       bool
   135  	CleanupTempFiles            bool
   136  	Prow                        bool
   137  	DisableSetKubeContext       bool
   138  	Dir                         string
   139  	Vault                       bool
   140  	RecreateVaultBucket         bool
   141  	Tekton                      bool
   142  	BuildPackName               string
   143  	Kaniko                      bool
   144  	GitOpsMode                  bool
   145  	NoGitOpsEnvApply            bool
   146  	NoGitOpsEnvRepo             bool
   147  	NoGitOpsEnvSetup            bool
   148  	NoGitOpsVault               bool
   149  	NextGeneration              bool `mapstructure:"next-generation"`
   150  	StaticJenkins               bool
   151  	LongTermStorage             bool   `mapstructure:"long-term-storage"`
   152  	LongTermStorageBucketName   string `mapstructure:"lts-bucket"`
   153  }
   154  
   155  // Secrets struct for secrets
   156  type Secrets struct {
   157  	Login string
   158  	Token string
   159  }
   160  
   161  const (
   162  	JX_GIT_TOKEN = "JX_GIT_TOKEN" // #nosec
   163  	JX_GIT_USER  = "JX_GIT_USER"
   164  
   165  	ServerlessJenkins = "Serverless Jenkins X Pipelines with Tekton"
   166  
   167  	GitOpsChartYAML = `name: env
   168  version: 0.0.1
   169  description: GitOps Environment for this Environment
   170  maintainers:
   171    - name: Team
   172  icon: https://www.cloudbees.com/sites/default/files/Jenkins_8.png
   173  `
   174  
   175  	devGitOpsGitIgnore = `
   176  # lets not accidentally check in Secret YAMLs!
   177  secrets.yaml
   178  mysecrets.yaml
   179  `
   180  
   181  	devGitOpsReadMe = `
   182  ## Jenkins X Development Environment
   183  
   184  This repository contains the source code for the Jenkins X Development Environment so that it can be managed via GitOps.
   185  `
   186  
   187  	devGitOpsJenkinsfile = `pipeline {
   188    agent {
   189      label "jenkins-jx-base"
   190    }
   191    environment {
   192      DEPLOY_NAMESPACE = "%s"
   193    }
   194    stages {
   195      stage('Validate Environment') {
   196        steps {
   197          container('jx-base') {
   198            dir('env') {
   199              sh 'jx step helm build'
   200            }
   201          }
   202        }
   203      }
   204      stage('Update Environment') {
   205        when {
   206          branch 'master'
   207        }
   208        steps {
   209          container('jx-base') {
   210            dir('env') {
   211              sh 'jx step env apply'
   212            }
   213          }
   214        }
   215      }
   216    }
   217  }
   218  `
   219  
   220  	devGitOpsJenkinsfileProw = `pipeline {
   221    agent any
   222    environment {
   223      DEPLOY_NAMESPACE = "%s"
   224    }
   225    stages {
   226      stage('Validate Environment') {
   227        steps {
   228          dir('env') {
   229            sh 'jx step helm build'
   230          }
   231        }
   232      }
   233      stage('Update Environment') {
   234        when {
   235          branch 'master'
   236        }
   237        steps {
   238          dir('env') {
   239            sh 'jx step env apply'
   240          }
   241        }
   242      }
   243    }
   244  }
   245  `
   246  	longTermStorageFlagName = "long-term-storage"
   247  	ltsBucketFlagName       = "lts-bucket"
   248  	kanikoFlagName          = "kaniko"
   249  	namespaceFlagName       = "namespace"
   250  	tektonFlagName          = "tekton"
   251  	prowFlagName            = "prow"
   252  	staticJenkinsFlagName   = "static-jenkins"
   253  	gitOpsFlagName          = "gitops"
   254  )
   255  
   256  var (
   257  	InstalLong = templates.LongDesc(`
   258  		Installs the Jenkins X platform on a Kubernetes cluster
   259  
   260  		Requires a --git-username and --git-api-token that can be used to create a new token.
   261  		This is so the Jenkins X platform can git tag your releases
   262  
   263  		For more documentation see: [https://jenkins-x.io/getting-started/install-on-cluster/](https://jenkins-x.io/getting-started/install-on-cluster/)
   264  
   265  		The current requirements are:
   266  
   267  		*RBAC is enabled on the cluster
   268  
   269  		*Insecure Docker registry is enabled for Docker registries running locally inside Kubernetes on the service IP range. See the above documentation for more detail
   270  
   271  `)
   272  
   273  	InstalExample = templates.Examples(`
   274  		# Default installer which uses interactive prompts to generate git secrets
   275  		jx install
   276  
   277  		# Install with a GitHub personal access token
   278  		jx install --git-username jenkins-x-bot --git-api-token 9fdbd2d070cd81eb12bca87861bcd850
   279  
   280  		# If you know the cloud provider you can pass this as a CLI argument. E.g. for AWS
   281  		jx install --provider=aws
   282  `)
   283  )
   284  
   285  // NewCmdInstall creates a command object for the generic "install" action, which
   286  // installs the jenkins-x platform on a Kubernetes cluster.
   287  func NewCmdInstall(commonOpts *opts.CommonOptions) *cobra.Command {
   288  
   289  	options := CreateInstallOptions(commonOpts)
   290  
   291  	cmd := &cobra.Command{
   292  		Use:     "install [flags]",
   293  		Short:   "Install Jenkins X in the current Kubernetes cluster",
   294  		Long:    InstalLong,
   295  		Example: InstalExample,
   296  		Run: func(cmd *cobra.Command, args []string) {
   297  			options.Cmd = cmd
   298  			options.Args = args
   299  			err := options.Run()
   300  			helper.CheckErr(err)
   301  		},
   302  	}
   303  
   304  	options.AddInstallFlags(cmd, false)
   305  
   306  	cmd.Flags().StringVarP(&options.Flags.Provider, "provider", "", "", "Cloud service providing the Kubernetes cluster.  Supported providers: "+cloud.KubernetesProviderOptions())
   307  
   308  	cmd.AddCommand(NewCmdInstallDependencies(commonOpts))
   309  	cmdvault.AwsCreateVaultOptions(cmd, &options.AWSConfig)
   310  
   311  	return cmd
   312  }
   313  
   314  // CreateInstallOptions creates the options for jx install
   315  func CreateInstallOptions(commonOpts *opts.CommonOptions) InstallOptions {
   316  	commonOptsBatch := *commonOpts
   317  	commonOptsBatch.BatchMode = true
   318  	options := InstallOptions{
   319  		CreateJenkinsUserOptions: CreateJenkinsUserOptions{
   320  			Username: "admin",
   321  			CreateOptions: createoptions.CreateOptions{
   322  				CommonOptions: commonOpts,
   323  			},
   324  		},
   325  		GitRepositoryOptions: gits.GitRepositoryOptions{},
   326  		CommonOptions:        commonOpts,
   327  		CreateEnvOptions: CreateEnvOptions{
   328  			HelmValuesConfig: config.HelmValuesConfig{
   329  				ExposeController: &config.ExposeController{
   330  					Config: config.ExposeControllerConfig{
   331  						HTTP:    "true",
   332  						TLSAcme: "false",
   333  						Exposer: "Ingress",
   334  					},
   335  				},
   336  			},
   337  			Options: v1.Environment{
   338  				ObjectMeta: metav1.ObjectMeta{},
   339  				Spec: v1.EnvironmentSpec{
   340  					PromotionStrategy: v1.PromotionStrategyTypeAutomatic,
   341  				},
   342  			},
   343  			PromotionStrategy:      string(v1.PromotionStrategyTypeAutomatic),
   344  			ForkEnvironmentGitRepo: kube.DefaultEnvironmentGitRepoURL,
   345  			CreateOptions: createoptions.CreateOptions{
   346  				CommonOptions: &commonOptsBatch,
   347  			},
   348  		},
   349  		InitOptions: initcmd.InitOptions{
   350  			CommonOptions: commonOpts,
   351  			Flags:         initcmd.InitFlags{},
   352  		},
   353  		AdminSecretsService: config.AdminSecretsService{},
   354  	}
   355  	return options
   356  }
   357  
   358  func (options *InstallOptions) AddInstallFlags(cmd *cobra.Command, includesInit bool) {
   359  	flags := &options.Flags
   360  	flags.AddCloudEnvOptions(cmd)
   361  	cmd.Flags().StringVarP(&flags.LocalHelmRepoName, "local-helm-repo-name", "", kube.LocalHelmRepoName, "The name of the helm repository for the installed ChartMuseum")
   362  	cmd.Flags().BoolVarP(&flags.NoDefaultEnvironments, "no-default-environments", "", false, "Disables the creation of the default Staging and Production environments")
   363  	cmd.Flags().BoolVarP(&flags.RemoteEnvironments, "remote-environments", "", false, "Indicates you intend Staging and Production environments to run in remote clusters. See https://jenkins-x.io/getting-started/multi-cluster/")
   364  	cmd.Flags().StringVarP(&flags.DefaultEnvironmentPrefix, "default-environment-prefix", "", "", "Default environment repo prefix, your Git repos will be of the form 'environment-$prefix-$envName'")
   365  	cmd.Flags().StringVarP(&flags.Namespace, namespaceFlagName, "", "jx", "The namespace the Jenkins X platform should be installed into")
   366  	cmd.Flags().StringVarP(&flags.Timeout, "timeout", "", opts.DefaultInstallTimeout, "The number of seconds to wait for the helm install to complete")
   367  	cmd.Flags().StringVarP(&flags.EnvironmentGitOwner, "environment-git-owner", "", "", "The Git provider organisation to create the environment Git repositories in")
   368  	cmd.Flags().BoolVarP(&flags.RegisterLocalHelmRepo, "register-local-helmrepo", "", false, "Registers the Jenkins X ChartMuseum registry with your helm client [default false]")
   369  	cmd.Flags().BoolVarP(&flags.CleanupTempFiles, "cleanup-temp-files", "", true, "Cleans up any temporary values.yaml used by helm install [default true]")
   370  	cmd.Flags().BoolVarP(&flags.HelmTLS, "helm-tls", "", false, "Whether to use TLS with helm")
   371  	cmd.Flags().BoolVarP(&flags.InstallOnly, "install-only", "", false, "Force the install command to fail if there is already an installation. Otherwise lets update the installation")
   372  	cmd.Flags().StringVarP(&flags.AzureRegistrySubscription, "azure-acr-subscription", "", "", "The Azure subscription under which the specified docker-registry is located")
   373  	cmd.Flags().StringVarP(&flags.DockerRegistry, "docker-registry", "", "", "The Docker Registry host or host:port which is used when tagging and pushing images. If not specified it defaults to the internal registry unless there is a better provider default (e.g. ECR on AWS/EKS)")
   374  	cmd.Flags().StringVarP(&flags.DockerRegistryOrg, "docker-registry-org", "", "", "The Docker Registry organiation/user to create images inside. On GCP this is typically your Google Project ID.")
   375  	cmd.Flags().StringVarP(&flags.ExposeControllerURLTemplate, "exposecontroller-urltemplate", "", "", "The ExposeController urltemplate for how services should be exposed as URLs. Defaults to being empty, which in turn defaults to \"{{.Service}}.{{.Namespace}}.{{.Domain}}\".")
   376  	cmd.Flags().StringVarP(&flags.ExposeControllerPathMode, "exposecontroller-pathmode", "", "", "The ExposeController path mode for how services should be exposed as URLs. Defaults to using subnets. Use a value of `path` to use relative paths within the domain host such as when using AWS ELB host names")
   377  
   378  	cmd.Flags().StringVarP(&flags.Version, "version", "", "", "The specific platform version to install")
   379  	cmd.Flags().BoolVarP(&flags.Prow, prowFlagName, "", false, "Enable Prow to implement Serverless Jenkins and support ChatOps on Pull Requests")
   380  	cmd.Flags().BoolVarP(&flags.Tekton, tektonFlagName, "", false, "Enables the Tekton pipeline engine (which used to be called knative build pipeline) along with Prow to provide Serverless Jenkins. Otherwise we default to use Knative Build if you enable Prow")
   381  	cmd.Flags().BoolVarP(&flags.GitOpsMode, gitOpsFlagName, "", false, "Creates a git repository for the Dev environment to manage the installation, configuration, upgrade and addition of Apps in Jenkins X all via GitOps")
   382  	cmd.Flags().BoolVarP(&flags.NoGitOpsEnvApply, "no-gitops-env-apply", "", false, "When using GitOps to create the source code for the development environment and installation, don't run 'jx step env apply' to perform the install")
   383  	cmd.Flags().BoolVarP(&flags.NoGitOpsEnvRepo, "no-gitops-env-repo", "", false, "When using GitOps to create the source code for the development environment this flag disables the creation of a git repository for the source code")
   384  	cmd.Flags().BoolVarP(&flags.NoGitOpsVault, "no-gitops-vault", "", false, "When using GitOps to create the source code for the development environment this flag disables the creation of a vault")
   385  	cmd.Flags().BoolVarP(&flags.NoGitOpsEnvSetup, "no-gitops-env-setup", "", false, "When using GitOps to install the development environment this flag skips the post-install setup")
   386  	cmd.Flags().BoolVarP(&flags.Vault, "vault", "", false, "Sets up a Hashicorp Vault for storing secrets during installation (supported only for GKE)")
   387  	cmd.Flags().BoolVarP(&flags.RecreateVaultBucket, "vault-bucket-recreate", "", true, "If the vault bucket already exists delete it then create it empty")
   388  	cmd.Flags().StringVarP(&flags.BuildPackName, "buildpack", "", "", "The name of the build pack to use for the Team")
   389  	cmd.Flags().BoolVarP(&flags.Kaniko, kanikoFlagName, "", false, "Use Kaniko for building docker images")
   390  	cmd.Flags().BoolVarP(&flags.NextGeneration, "ng", "", false, "Use the Next Generation Jenkins X features like Prow, Tekton, No Tiller, Vault, Dev GitOps")
   391  	cmd.Flags().BoolVarP(&flags.StaticJenkins, staticJenkinsFlagName, "", false, "Install a static Jenkins master to use as the pipeline engine. Note this functionality is deprecated in favour of running serverless Tekton builds")
   392  	cmd.Flags().BoolVarP(&flags.LongTermStorage, longTermStorageFlagName, "", false, "Enable the Long Term Storage option to save logs and other assets into a GCS bucket (supported only for GKE)")
   393  	cmd.Flags().StringVarP(&flags.LongTermStorageBucketName, ltsBucketFlagName, "", "", "The bucket to use for Long Term Storage. If the bucket doesn't exist, an attempt will be made to create it, otherwise random naming will be used")
   394  	cmd.Flags().StringVar(&options.ConfigFile, "config-file", "", "Configuration file used for installation")
   395  	cmd.Flags().BoolVar(&options.NoBrew, opts.OptionNoBrew, false, "Disables brew package manager on MacOS when installing binary dependencies")
   396  	cmd.Flags().BoolVar(&options.InstallDependencies, opts.OptionInstallDeps, false, "Enables automatic dependencies installation when required")
   397  	cmd.Flags().BoolVar(&options.SkipAuthSecretsMerge, opts.OptionSkipAuthSecMerge, false, "Skips merging the secrets from local files with the secrets from Kubernetes cluster")
   398  
   399  	bindInstallConfigToFlags(cmd)
   400  	opts.AddGitRepoOptionsArgumentsWithGithubDefault(cmd, &options.GitRepositoryOptions)
   401  	options.HelmValuesConfig.AddExposeControllerValues(cmd, true)
   402  	options.AdminSecretsService.AddAdminSecretsValues(cmd)
   403  	options.InitOptions.AddInitFlags(cmd)
   404  }
   405  
   406  func bindInstallConfigToFlags(cmd *cobra.Command) {
   407  	_ = viper.BindPFlag(installConfigKey(namespaceFlagName), cmd.Flags().Lookup(namespaceFlagName))
   408  	_ = viper.BindPFlag(installConfigKey(kanikoFlagName), cmd.Flags().Lookup(kanikoFlagName))
   409  	_ = viper.BindPFlag(installConfigKey(tektonFlagName), cmd.Flags().Lookup(tektonFlagName))
   410  	_ = viper.BindPFlag(installConfigKey(prowFlagName), cmd.Flags().Lookup(prowFlagName))
   411  	_ = viper.BindPFlag(installConfigKey(gitOpsFlagName), cmd.Flags().Lookup(gitOpsFlagName))
   412  	_ = viper.BindPFlag(installConfigKey("next-generation"), cmd.Flags().Lookup("ng"))
   413  	_ = viper.BindPFlag(installConfigKey(staticJenkinsFlagName), cmd.Flags().Lookup(staticJenkinsFlagName))
   414  }
   415  
   416  func (flags *InstallFlags) AddCloudEnvOptions(cmd *cobra.Command) {
   417  	cmd.Flags().StringVarP(&flags.CloudEnvRepository, "cloud-environment-repo", "", opts.DefaultCloudEnvironmentsURL, "Cloud Environments Git repo")
   418  	cmd.Flags().StringVarP(&flags.VersionsRepository, "versions-repo", "", config.DefaultVersionsURL, "Jenkins X versions Git repo")
   419  	cmd.Flags().StringVarP(&flags.VersionsGitRef, "versions-ref", "", "", "Jenkins X versions Git repository reference (tag, branch, sha etc)")
   420  	cmd.Flags().BoolVarP(&flags.LocalCloudEnvironment, "local-cloud-environment", "", false, "Ignores default cloud-environment-repo and uses current directory ")
   421  }
   422  
   423  // CheckFlags validates & configures install flags
   424  func (options *InstallOptions) CheckFlags() error {
   425  	log.Logger().Debug("checking installation flags")
   426  	flags := &options.Flags
   427  
   428  	if flags.StaticJenkins {
   429  		return fmt.Errorf("option '--static-jenkins' has been removed")
   430  	}
   431  
   432  	if flags.Prow {
   433  		flags.Tekton = true
   434  	}
   435  
   436  	if flags.Tekton {
   437  		flags.Prow = true
   438  		if !options.InitOptions.Flags.NoTiller {
   439  			log.Logger().Warnf("note that if using Serverless Jenkins with Tekton we recommend the extra flag: %s", util.ColorInfo("--no-tiller"))
   440  		}
   441  	}
   442  
   443  	if options.BatchMode && !flags.NextGeneration && !flags.Tekton && !flags.Prow {
   444  		flags.Tekton = true
   445  		flags.Prow = true
   446  		flags.Kaniko = true
   447  		options.InitOptions.Flags.NoTiller = true
   448  	}
   449  
   450  	if flags.NextGeneration {
   451  		flags.GitOpsMode = true
   452  		flags.Vault = true
   453  		flags.Prow = true
   454  		flags.Tekton = true
   455  		flags.Kaniko = true
   456  		options.InitOptions.Flags.NoTiller = true
   457  	}
   458  
   459  	// only kaniko is supported as a builder in tekton
   460  	if flags.Tekton {
   461  		if flags.Provider == cloud.GKE {
   462  			if !flags.Kaniko {
   463  				log.Logger().Warnf("When using tekton, only kaniko is supported as a builder")
   464  			}
   465  			flags.Kaniko = true
   466  		}
   467  	}
   468  
   469  	// check some flags combination for GitOps mode
   470  	if flags.GitOpsMode {
   471  		options.SkipAuthSecretsMerge = true
   472  		flags.DisableSetKubeContext = true
   473  		if !flags.Vault {
   474  			log.Logger().Warnf("GitOps mode requires %s.", util.ColorInfo("vault"))
   475  		}
   476  		initFlags := &options.InitOptions.Flags
   477  		if !initFlags.NoTiller {
   478  			log.Logger().Warnf("GitOps mode requires helm without tiller server. %s flag is automatically set", util.ColorInfo("no-tiller"))
   479  			initFlags.NoTiller = true
   480  		}
   481  	}
   482  
   483  	// If we're using external-dns then remove the namespace subdomain from the URLTemplate
   484  	if options.InitOptions.Flags.ExternalDNS {
   485  		flags.ExposeControllerURLTemplate = `"{{.Service}}-{{.Namespace}}.{{.Domain}}"`
   486  	}
   487  
   488  	// Make sure that the default environment prefix is configured. Typically it is the cluster
   489  	// name when the install command is called from create cluster.
   490  	if flags.DefaultEnvironmentPrefix == "" {
   491  		clusterName := options.installValues[kube.ClusterName]
   492  		if clusterName == "" {
   493  			flags.DefaultEnvironmentPrefix = strings.ToLower(randomdata.SillyName())
   494  		} else {
   495  			flags.DefaultEnvironmentPrefix = clusterName
   496  		}
   497  	}
   498  
   499  	if flags.DockerRegistry == "" {
   500  		dockerReg, err := options.dockerRegistryValue()
   501  		if err != nil {
   502  			log.Logger().Warnf("unable to calculate docker registry values - %s", err)
   503  		}
   504  		flags.DockerRegistry = dockerReg
   505  	}
   506  
   507  	// lets default the docker registry org to the project id
   508  	if flags.DockerRegistryOrg == "" {
   509  		flags.DockerRegistryOrg = options.installValues[kube.ProjectID]
   510  	}
   511  
   512  	log.Logger().Debugf("flags after checking - %+v", flags)
   513  
   514  	if flags.Tekton {
   515  		kind := options.GitRepositoryOptions.ServerKind
   516  		if kind != "" && kind != gits.KindGitHub {
   517  			return fmt.Errorf("Git provider: %s is not yet supported for Tekton.\nYou can work around this with '--static-jenkins'\nFor more details see: https://jenkins-x.io/about/status/", kind)
   518  		}
   519  	}
   520  	return nil
   521  }
   522  
   523  // CheckFeatures - determines if the various features have been enabled
   524  func (options *InstallOptions) CheckFeatures() error {
   525  	if options.Flags.Tekton {
   526  		return features.CheckTektonEnabled()
   527  	}
   528  	return nil
   529  }
   530  
   531  // Run implements this command
   532  func (options *InstallOptions) Run() error {
   533  
   534  	err := options.GetConfiguration(&options)
   535  	if err != nil {
   536  		return errors.Wrap(err, "getting install configuration")
   537  	}
   538  
   539  	err = options.selectJenkinsInstallation()
   540  	if err != nil {
   541  		return errors.Wrap(err, "selecting the Jenkins installation type")
   542  	}
   543  
   544  	// Check the provided flags before starting any installation
   545  	err = options.CheckFlags()
   546  	if err != nil {
   547  		return errors.Wrap(err, "checking the provided flags")
   548  	}
   549  
   550  	configStore := configio.NewFileStore()
   551  
   552  	ns, originalNs, err := options.setupNamespace()
   553  	if err != nil {
   554  		return errors.Wrap(err, "setting up current namespace")
   555  	}
   556  	client, err := options.KubeClient()
   557  	if err != nil {
   558  		return errors.Wrap(err, "creating the kube client")
   559  	}
   560  
   561  	err = options.registerAllCRDs()
   562  	if err != nil {
   563  		return errors.Wrap(err, "registering all CRDs")
   564  	}
   565  
   566  	gitOpsDir, gitOpsEnvDir, err := options.configureGitOpsMode(configStore, ns)
   567  	if err != nil {
   568  		return errors.Wrap(err, "configuring the GitOps mode")
   569  	}
   570  
   571  	options.configureHelm(client, ns)
   572  	err = options.installHelmBinaries()
   573  	if err != nil {
   574  		return errors.Wrap(err, "installing helm binaries")
   575  	}
   576  
   577  	err = options.configureKubectl(ns)
   578  	if err != nil {
   579  		return errors.Wrap(err, "configure the kubectl")
   580  	}
   581  
   582  	err = options.installCloudProviderDependencies()
   583  	if err != nil {
   584  		return errors.Wrap(err, "installing cloud provider dependencies")
   585  	}
   586  
   587  	options.Flags.Provider, err = options.GetCloudProvider(options.Flags.Provider)
   588  	if err != nil {
   589  		return errors.Wrapf(err, "retrieving cloud provider '%s'", options.Flags.Provider)
   590  	}
   591  
   592  	err = options.configureTeamSettings()
   593  	if err != nil {
   594  		return errors.Wrap(err, "configuring the team settings in the dev environment")
   595  	}
   596  
   597  	err = options.configureCloudProviderPreInit(client)
   598  	if err != nil {
   599  		return errors.Wrap(err, "configuring the cloud provider before initializing the platform")
   600  	}
   601  
   602  	err = options.init()
   603  	if err != nil {
   604  		return errors.Wrap(err, "initializing the Jenkins X platform")
   605  	}
   606  
   607  	err = options.configureCloudProivderPostInit(client, ns)
   608  	if err != nil {
   609  		return errors.Wrap(err, "configuring the cloud provider after initializing the platform")
   610  	}
   611  
   612  	ic, err := options.saveIngressConfig()
   613  	if err != nil {
   614  		return errors.Wrap(err, "saving the ingress configuration in a ConfigMap")
   615  	}
   616  
   617  	err = options.configureLongTermStorageBucket()
   618  	if err != nil {
   619  		return errors.Wrap(err, "configuring Long Term Storage")
   620  	}
   621  
   622  	err = options.createSystemVault(client, ns, ic)
   623  	if err != nil {
   624  		return errors.Wrap(err, "creating the system vault")
   625  	}
   626  
   627  	err = options.saveClusterConfig()
   628  	if err != nil {
   629  		return errors.Wrap(err, "saving the cluster configuration in a ConfigMap")
   630  	}
   631  
   632  	err = options.configureGitAuth()
   633  	if err != nil {
   634  		return errors.Wrap(err, "configuring the git auth")
   635  	}
   636  
   637  	err = options.configureDockerRegistry(client, ns)
   638  	if err != nil {
   639  		return errors.Wrap(err, "configuring the docker registry")
   640  	}
   641  
   642  	versionsRepoDir, _, err := options.CloneJXVersionsRepo(options.Flags.VersionsRepository, options.Flags.VersionsGitRef)
   643  	if err != nil {
   644  		return errors.Wrap(err, "cloning the jx versions repo")
   645  	}
   646  
   647  	cloudEnvDir, err := options.CloneJXCloudEnvironmentsRepo()
   648  	if err != nil {
   649  		return errors.Wrap(err, "cloning the jx cloud environments repo")
   650  	}
   651  
   652  	err = options.ConfigureKaniko()
   653  	if err != nil {
   654  		return errors.Wrap(err, "unable to generate the Kaniko configuration")
   655  	}
   656  
   657  	err = options.configureHelmValues(ns)
   658  	if err != nil {
   659  		return errors.Wrap(err, "configuring helm values")
   660  	}
   661  
   662  	if options.Flags.Provider == "" {
   663  		return fmt.Errorf("no Kubernetes provider found to match cloud-environment with")
   664  	}
   665  	providerEnvDir := filepath.Join(cloudEnvDir, fmt.Sprintf("env-%s", strings.ToLower(options.Flags.Provider)))
   666  	valuesFiles, secretsFiles, temporaryFiles, err := options.getHelmValuesFiles(configStore, providerEnvDir)
   667  	if err != nil {
   668  		return errors.Wrap(err, "getting the helm value files")
   669  	}
   670  
   671  	log.Logger().Debugf("Installing Jenkins X platform helm chart from: %s", providerEnvDir)
   672  
   673  	err = options.configureHelmRepo()
   674  	if err != nil {
   675  		return errors.Wrap(err, "configuring the Jenkins X helm repository")
   676  	}
   677  
   678  	err = options.configureProwInTeamSettings()
   679  	if err != nil {
   680  		return errors.Wrap(err, "configuring Prow in team settings")
   681  	}
   682  
   683  	err = options.configureAndInstallProw(ns, gitOpsEnvDir, valuesFiles)
   684  	if err != nil {
   685  		return errors.Wrap(err, "configuring and installing Prow")
   686  	}
   687  
   688  	err = options.verifyTiller(client, ns)
   689  	if err != nil {
   690  		return errors.Wrap(err, "verifying Tiller is running")
   691  	}
   692  
   693  	err = options.configureBuildPackMode()
   694  	if err != nil {
   695  		return errors.Wrap(err, "configuring the build pack mode")
   696  	}
   697  
   698  	log.Logger().Infof("Installing jx into namespace %s", util.ColorInfo(ns))
   699  
   700  	version, err := options.getPlatformVersion(versionsRepoDir, configStore)
   701  	if err != nil {
   702  		return errors.Wrap(err, "getting the platform version")
   703  	}
   704  
   705  	log.Logger().Infof("Installing jenkins-x-platform version: %s", util.ColorInfo(version))
   706  
   707  	if options.Flags.GitOpsMode {
   708  		err := options.installPlatformGitOpsMode(gitOpsEnvDir, gitOpsDir, configStore, kube.DefaultChartMuseumURL,
   709  			platform.JenkinsXPlatformChartName, ns, version, valuesFiles, secretsFiles)
   710  		if err != nil {
   711  			return errors.Wrap(err, "installing the Jenkins X platform in GitOps mode")
   712  		}
   713  	} else {
   714  		err := options.installPlatform(providerEnvDir, platform.JenkinsXPlatformChart, platform.JenkinsXPlatformRelease,
   715  			ns, version, valuesFiles, secretsFiles)
   716  		if err != nil {
   717  			return errors.Wrap(err, "installing the Jenkins X platform")
   718  		}
   719  	}
   720  
   721  	if options.Flags.CleanupTempFiles {
   722  		err := options.cleanupTempFiles(temporaryFiles)
   723  		if err != nil {
   724  			return errors.Wrap(err, "cleaning up the temporary files")
   725  		}
   726  	}
   727  
   728  	err = options.configureImportModeInTeamSettings()
   729  	if err != nil {
   730  		return errors.Wrap(err, "configuring ImportMode in team settings")
   731  	}
   732  
   733  	err = options.configureTillerInDevEnvironment()
   734  	if err != nil {
   735  		return errors.Wrap(err, "configuring Tiller in the dev environment")
   736  	}
   737  
   738  	err = options.configureHelm3(ns)
   739  	if err != nil {
   740  		return errors.Wrap(err, "configuring helm3")
   741  	}
   742  
   743  	err = options.installAddons()
   744  	if err != nil {
   745  		return errors.Wrap(err, "installing the Jenkins X Addons")
   746  	}
   747  
   748  	// Jenkins needs to be configured already here if running in non GitOps mode
   749  	// in order to be able to create the environments
   750  	if !options.Flags.GitOpsMode {
   751  		err = options.configureJenkins(ns)
   752  		if err != nil {
   753  			return errors.Wrap(err, "configuring Jenkins")
   754  		}
   755  	}
   756  
   757  	err = options.createEnvironments(ns)
   758  	if err != nil {
   759  		if strings.Contains(err.Error(), "com.atlassian.bitbucket.project.NoSuchProjectException") {
   760  			log.Logger().Infof("\nProject %s cannot be found. If you are using BitBucket Server, please use "+
   761  				"a project code instead of a project name (for example 'MYPR' instead of 'myproject'). ",
   762  				util.ColorInfo(options.CreateEnvOptions.GitRepositoryOptions.Owner))
   763  			return nil
   764  		}
   765  		return errors.Wrap(err, "creating the environments")
   766  	}
   767  
   768  	err = options.saveChartmuseumAuthConfig()
   769  	if err != nil {
   770  		return errors.Wrap(err, "saving the ChartMuseum auth configuration")
   771  	}
   772  
   773  	if options.Flags.RegisterLocalHelmRepo {
   774  		err = options.RegisterLocalHelmRepo(options.Flags.LocalHelmRepoName, ns)
   775  		if err != nil {
   776  			return errors.Wrapf(err, "registering the local helm repo '%s'", options.Flags.LocalHelmRepoName)
   777  		}
   778  	}
   779  
   780  	gitOpsEnvDir, err = options.generateGitOpsDevEnvironmentConfig(gitOpsDir)
   781  	if err != nil {
   782  		return errors.Wrap(err, "generating the GitOps development environment config")
   783  	}
   784  
   785  	err = options.applyGitOpsDevEnvironmentConfig(gitOpsEnvDir, ns)
   786  	if err != nil {
   787  		return errors.Wrap(err, "applying the GitOps development environment config")
   788  	}
   789  
   790  	err = options.setupGitOpsPostApply(ns)
   791  	if err != nil {
   792  		return errors.Wrap(err, "setting up GitOps post installation")
   793  	}
   794  
   795  	log.Logger().Infof("\nJenkins X installation completed successfully")
   796  
   797  	options.logAdminPassword()
   798  
   799  	options.logNameServers()
   800  
   801  	log.Logger().Infof("Your Kubernetes context is now set to the namespace: %s ", util.ColorInfo(ns))
   802  	log.Logger().Infof("To switch back to your original namespace use: %s", util.ColorInfo("jx namespace "+originalNs))
   803  	log.Logger().Infof("Or to use this context/namespace in just one terminal use: %s", util.ColorInfo("jx shell"))
   804  	log.Logger().Infof("For help on switching contexts see: %s\n", util.ColorInfo("https://jenkins-x.io/developing/kube-context/"))
   805  
   806  	log.Logger().Infof("To import existing projects into Jenkins X:       %s", util.ColorInfo("jx import"))
   807  	log.Logger().Infof("To create a new Spring Boot microservice:       %s", util.ColorInfo("jx create spring -d web -d actuator"))
   808  	log.Logger().Infof("To create a new microservice from a quickstart: %s", util.ColorInfo("jx create quickstart"))
   809  	return nil
   810  }
   811  
   812  func (options *InstallOptions) configureKubectl(namespace string) error {
   813  	if !options.Flags.DisableSetKubeContext {
   814  		context, err := options.GetCommandOutput("", "kubectl", "config", "current-context")
   815  		if err != nil {
   816  			return errors.Wrap(err, "failed to retrieve the current context from kube configuration")
   817  		}
   818  		err = options.RunCommand("kubectl", "config", "set-context", context, "--namespace", namespace)
   819  		if err != nil {
   820  			return errors.Wrapf(err, "failed to set the context '%s' in kube configuration", context)
   821  		}
   822  	}
   823  
   824  	return nil
   825  }
   826  
   827  func (options *InstallOptions) setupNamespace() (string, string, error) {
   828  	_, originalNs, err := options.KubeClientAndNamespace()
   829  	if err != nil {
   830  		return "", "", errors.Wrap(err, "creating kube client")
   831  	}
   832  	ns := options.Flags.Namespace
   833  	if ns == "" {
   834  		ns = originalNs
   835  	}
   836  	options.SetDevNamespace(ns)
   837  
   838  	return ns, originalNs, nil
   839  }
   840  
   841  func (options *InstallOptions) init() error {
   842  	initOpts := &options.InitOptions
   843  	initOpts.Flags.Provider = options.Flags.Provider
   844  	initOpts.Flags.Namespace = options.Flags.Namespace
   845  	initOpts.BatchMode = options.BatchMode
   846  	initOpts.Flags.VersionsRepository = options.Flags.VersionsRepository
   847  	initOpts.Flags.Http = true
   848  	exposeController := options.CreateEnvOptions.HelmValuesConfig.ExposeController
   849  	if exposeController != nil {
   850  		initOpts.Flags.Http = exposeController.Config.HTTP == "true"
   851  	}
   852  	if initOpts.Flags.Domain == "" && options.Flags.Domain != "" {
   853  		initOpts.Flags.Domain = options.Flags.Domain
   854  	}
   855  	if initOpts.Flags.NoTiller {
   856  		initOpts.SetHelm(nil)
   857  	}
   858  	// configure local tiller if this is required
   859  	if !initOpts.Flags.RemoteTiller && !initOpts.Flags.NoTiller {
   860  		err := helm.RestartLocalTiller()
   861  		if err != nil {
   862  			return errors.Wrap(err, "restarting local tiller")
   863  		}
   864  		initOpts.SetHelm(options.Helm())
   865  	}
   866  
   867  	// configure the helm values for expose controller
   868  	if exposeController != nil {
   869  		ecConfig := &exposeController.Config
   870  		if ecConfig.Domain == "" && options.Flags.Domain != "" {
   871  			ecConfig.Domain = options.Flags.Domain
   872  			log.Logger().Infof("set exposeController Config Domain %s", ecConfig.Domain)
   873  		}
   874  		if ecConfig.PathMode == "" && options.Flags.ExposeControllerPathMode != "" {
   875  			ecConfig.PathMode = options.Flags.ExposeControllerPathMode
   876  			log.Logger().Infof("set exposeController Config PathMode %s", ecConfig.PathMode)
   877  		}
   878  		if (ecConfig.URLTemplate == "" && options.Flags.ExposeControllerURLTemplate != "") ||
   879  			(options.Flags.ExposeControllerURLTemplate != "" && options.InitOptions.Flags.ExternalDNS) {
   880  			ecConfig.URLTemplate = options.Flags.ExposeControllerURLTemplate
   881  			log.Logger().Infof("set exposeController Config URLTemplate %s", ecConfig.URLTemplate)
   882  		}
   883  		if isOpenShiftProvider(options.Flags.Provider) {
   884  			ecConfig.Exposer = "Route"
   885  		}
   886  	}
   887  
   888  	err := initOpts.Run()
   889  	if err != nil {
   890  		return errors.Wrap(err, "initializing the Jenkins X platform")
   891  	}
   892  
   893  	// update the domain if was modified during the initialization
   894  	domain := exposeController.Config.Domain
   895  	if domain == "" {
   896  		domain = initOpts.Flags.Domain
   897  	}
   898  	if domain == "" {
   899  		client, err := options.KubeClient()
   900  		if err != nil {
   901  			return errors.Wrap(err, "getting the kubernetes client")
   902  		}
   903  		ingNamespace := initOpts.Flags.IngressNamespace
   904  		ingService := initOpts.Flags.IngressService
   905  		extIP := initOpts.Flags.ExternalIP
   906  		domain, err = options.GetDomain(client, domain,
   907  			options.Flags.Provider,
   908  			ingNamespace,
   909  			ingService,
   910  			extIP)
   911  		if err != nil {
   912  			return errors.Wrapf(err, "getting a domain for ingress service %s/%s", ingNamespace, ingService)
   913  		}
   914  	}
   915  
   916  	// checking if the domain is by any chance empty and bail out
   917  	if domain == "" {
   918  		return fmt.Errorf("the installation cannot proceed with an empty domain. Please provide a domain in the %s option",
   919  			util.ColorInfo("domain"))
   920  	}
   921  
   922  	options.Flags.Domain = domain
   923  	exposeController.Config.Domain = domain
   924  
   925  	return nil
   926  }
   927  
   928  func (options *InstallOptions) getPlatformVersion(cloudEnvDir string,
   929  	configStore configio.ConfigStore) (string, error) {
   930  	version := options.Flags.Version
   931  	var err error
   932  	if version == "" {
   933  		version, err = LoadVersionFromCloudEnvironmentsDir(cloudEnvDir, configStore)
   934  		if err != nil {
   935  			return "", errors.Wrap(err, "failed to load version from cloud environments dir")
   936  		}
   937  	}
   938  	return version, nil
   939  }
   940  
   941  func (options *InstallOptions) installPlatform(providerEnvDir string, jxChart string, jxRelName string,
   942  	namespace string, version string, valuesFiles []string, secretsFiles []string) error {
   943  
   944  	options.Helm().SetCWD(providerEnvDir)
   945  
   946  	timeout := options.Flags.Timeout
   947  	if timeout == "" {
   948  		timeout = opts.DefaultInstallTimeout
   949  	}
   950  
   951  	allValuesFiles := []string{}
   952  	allValuesFiles = append(allValuesFiles, valuesFiles...)
   953  	allValuesFiles = append(allValuesFiles, secretsFiles...)
   954  	for _, f := range allValuesFiles {
   955  		log.Logger().Debugf("Adding values file %s", util.ColorInfo(f))
   956  	}
   957  
   958  	helmOpts := helm.InstallChartOptions{
   959  		ReleaseName: jxRelName,
   960  		Chart:       jxChart,
   961  		Ns:          namespace,
   962  		Version:     version,
   963  		ValueFiles:  allValuesFiles,
   964  		InstallOnly: options.Flags.InstallOnly,
   965  		NoForce:     true,
   966  	}
   967  	err := options.InstallChartWithOptionsAndTimeout(helmOpts, timeout)
   968  
   969  	if err != nil {
   970  		return errors.Wrap(err, "failed to install/upgrade the jenkins-x platform chart")
   971  	}
   972  
   973  	err = options.waitForInstallToBeReady(namespace)
   974  	if err != nil {
   975  		return errors.Wrap(err, "failed to wait for jenkins-x chart installation to be ready")
   976  	}
   977  	log.Logger().Infof("Jenkins X deployments ready in namespace %s", namespace)
   978  	return nil
   979  }
   980  
   981  func (options *InstallOptions) installPlatformGitOpsMode(gitOpsEnvDir string, gitOpsDir string, configStore configio.ConfigStore,
   982  	chartRepository string, chartName string, namespace string, version string, valuesFiles []string, secretsFiles []string) error {
   983  	options.CreateEnvOptions.NoDevNamespaceInit = true
   984  
   985  	chartFile := filepath.Join(gitOpsEnvDir, helm.ChartFileName)
   986  	requirementsFile := filepath.Join(gitOpsEnvDir, helm.RequirementsFileName)
   987  	secretsFile := filepath.Join(gitOpsEnvDir, helm.SecretsFileName)
   988  	valuesFile := filepath.Join(gitOpsEnvDir, helm.ValuesFileName)
   989  
   990  	platformDep := &helm.Dependency{
   991  		Name:       platform.JenkinsXPlatformChartName,
   992  		Version:    version,
   993  		Repository: kube.DefaultChartMuseumURL,
   994  	}
   995  	requirements := &helm.Requirements{
   996  		Dependencies: []*helm.Dependency{platformDep},
   997  	}
   998  
   999  	// lets handle if the requirements.yaml already exists we may have added some initial apps like prow etc
  1000  	exists, err := util.FileExists(requirementsFile)
  1001  	if err != nil {
  1002  		return err
  1003  	}
  1004  	if exists {
  1005  		requirements, err = helm.LoadRequirementsFile(requirementsFile)
  1006  		if err != nil {
  1007  			return errors.Wrapf(err, "failed to load helm requirements file %s", requirementsFile)
  1008  		}
  1009  		requirements.Dependencies = append(requirements.Dependencies, platformDep)
  1010  	}
  1011  	err = helm.SaveFile(requirementsFile, requirements)
  1012  	if err != nil {
  1013  		return errors.Wrapf(err, "failed to save GitOps helm requirements file %s", requirementsFile)
  1014  	}
  1015  
  1016  	err = configStore.Write(chartFile, []byte(GitOpsChartYAML))
  1017  	if err != nil {
  1018  		return errors.Wrapf(err, "failed to save file %s", chartFile)
  1019  	}
  1020  
  1021  	err = helm.CombineValueFilesToFile(secretsFile, secretsFiles, platform.JenkinsXPlatformChartName, nil)
  1022  	if err != nil {
  1023  		return errors.Wrapf(err, "failed to generate %s by combining helm Secret YAML files %s", secretsFile, strings.Join(secretsFiles, ", "))
  1024  	}
  1025  
  1026  	if options.Flags.Vault {
  1027  		err := options.storeSecretYamlFilesInVault(vault.GitOpsSecretsPath, secretsFile)
  1028  		if err != nil {
  1029  			return errors.Wrapf(err, "storing in Vault the secrets files: %s", secretsFile)
  1030  		}
  1031  
  1032  		err = util.DestroyFile(secretsFile)
  1033  		if err != nil {
  1034  			return errors.Wrapf(err, "destroying the secrets file '%s' after storing it in Vault", secretsFile)
  1035  		}
  1036  	}
  1037  
  1038  	extraValues := map[string]interface{}{
  1039  		"postinstalljob": map[string]interface{}{"enabled": "true"},
  1040  	}
  1041  
  1042  	err = options.setValuesFileValue(filepath.Join(gitOpsEnvDir, "jenkins", helm.ValuesFileName), "enabled", !options.Flags.Prow)
  1043  	if err != nil {
  1044  		return err
  1045  	}
  1046  	err = options.setValuesFileValue(filepath.Join(gitOpsEnvDir, "controllerbuild", helm.ValuesFileName), "enabled", options.Flags.Prow)
  1047  	if err != nil {
  1048  		return err
  1049  	}
  1050  	err = options.setValuesFileValue(filepath.Join(gitOpsEnvDir, "controllerworkflow", helm.ValuesFileName), "enabled", !options.Flags.Tekton)
  1051  	if err != nil {
  1052  		return err
  1053  	}
  1054  
  1055  	// lets load any existing values.yaml data as we may have created this via additional apps like Prow
  1056  	exists, err = util.FileExists(valuesFile)
  1057  	if err != nil {
  1058  		return err
  1059  	}
  1060  	if exists {
  1061  		currentValues, err := chartutil.ReadValuesFile(valuesFile)
  1062  		if err != nil {
  1063  			return err
  1064  		}
  1065  		util.CombineMapTrees(extraValues, currentValues)
  1066  	}
  1067  
  1068  	err = helm.CombineValueFilesToFile(valuesFile, valuesFiles, platform.JenkinsXPlatformChartName, extraValues)
  1069  	if err != nil {
  1070  		return errors.Wrapf(err, "failed to generate %s by combining helm value YAML files %s", valuesFile, strings.Join(valuesFiles, ", "))
  1071  	}
  1072  
  1073  	gitIgnore := filepath.Join(gitOpsDir, ".gitignore")
  1074  	err = configStore.Write(gitIgnore, []byte(devGitOpsGitIgnore))
  1075  	if err != nil {
  1076  		return errors.Wrapf(err, "failed to write %s", gitIgnore)
  1077  	}
  1078  
  1079  	readme := filepath.Join(gitOpsDir, "README.md")
  1080  	err = configStore.Write(readme, []byte(devGitOpsReadMe))
  1081  	if err != nil {
  1082  		return errors.Wrapf(err, "failed to write %s", readme)
  1083  	}
  1084  
  1085  	jenkinsFile := filepath.Join(gitOpsDir, "Jenkinsfile")
  1086  	jftTmp := devGitOpsJenkinsfile
  1087  	isProw := options.Flags.Prow
  1088  	if isProw {
  1089  		jftTmp = devGitOpsJenkinsfileProw
  1090  	}
  1091  	text := fmt.Sprintf(jftTmp, namespace)
  1092  	err = configStore.Write(jenkinsFile, []byte(text))
  1093  	if err != nil {
  1094  		return errors.Wrapf(err, "failed to write %s", jenkinsFile)
  1095  	}
  1096  	return nil
  1097  }
  1098  
  1099  func (options *InstallOptions) configureAndInstallProw(namespace string, gitOpsEnvDir string, valuesFiles []string) error {
  1100  	options.SetCurrentNamespace(namespace)
  1101  	if options.Flags.Prow {
  1102  		_, pipelineUser, err := options.GetPipelineGitAuth()
  1103  		if err != nil || pipelineUser == nil {
  1104  			return errors.Wrap(err, "retrieving the pipeline Git Auth")
  1105  		}
  1106  		options.OAUTHToken = pipelineUser.ApiToken
  1107  		err = options.InstallProw(options.Flags.Tekton, options.InitOptions.Flags.ExternalDNS, options.Flags.GitOpsMode, gitOpsEnvDir, pipelineUser.Username, valuesFiles)
  1108  		if err != nil {
  1109  			return errors.Wrap(err, "installing Prow")
  1110  		}
  1111  	}
  1112  	return nil
  1113  }
  1114  
  1115  func (options *InstallOptions) configureHelm3(namespace string) error {
  1116  	initOpts := &options.InitOptions
  1117  	helmBinary := initOpts.HelmBinary()
  1118  	if helmBinary != "helm" {
  1119  		helmOptions := edit.EditHelmBinOptions{}
  1120  		helmOptions.CommonOptions = options.CommonOptions
  1121  		helmOptions.CommonOptions.BatchMode = true
  1122  		helmOptions.CommonOptions.Args = []string{helmBinary}
  1123  		helmOptions.SetDevNamespace(namespace)
  1124  		err := helmOptions.Run()
  1125  		if err != nil {
  1126  			return errors.Wrap(err, "failed to edit the helm options")
  1127  		}
  1128  	}
  1129  	return nil
  1130  }
  1131  
  1132  func (options *InstallOptions) configureHelm(client kubernetes.Interface, namespace string) {
  1133  	initOpts := &options.InitOptions
  1134  	helmBinary := initOpts.HelmBinary()
  1135  	options.Helm().SetHelmBinary(helmBinary)
  1136  	if initOpts.Flags.NoTiller {
  1137  		helmer := options.Helm()
  1138  		helmCli, ok := helmer.(*helm.HelmCLI)
  1139  		if ok && helmCli != nil {
  1140  			helm := helm.NewHelmTemplate(helmCli, helmCli.CWD, client, namespace)
  1141  			options.SetHelm(helm)
  1142  		} else {
  1143  			helmTemplate, ok := helmer.(*helm.HelmTemplate)
  1144  			if ok {
  1145  				options.SetHelm(helmTemplate)
  1146  			} else {
  1147  				log.Logger().Warnf("Helm facade is not a *helm.HelmCLI or *helm.HelmTemplate: %#v", helmer)
  1148  			}
  1149  		}
  1150  	}
  1151  }
  1152  
  1153  func (options *InstallOptions) configureHelmRepo() error {
  1154  	_, err := options.AddHelmBinaryRepoIfMissing(kube.DefaultChartMuseumURL, "jenkins-x", "", "")
  1155  	if err != nil {
  1156  		return errors.Wrap(err, "failed to add the jenkinx-x helm repo")
  1157  	}
  1158  
  1159  	err = options.Helm().UpdateRepo()
  1160  	if err != nil {
  1161  		return errors.Wrap(err, "failed to update the helm repo")
  1162  	}
  1163  	return nil
  1164  }
  1165  
  1166  func (options *InstallOptions) selectJenkinsInstallation() error {
  1167  	if !options.BatchMode {
  1168  		//determine which install type is configured
  1169  		jenkinsInstallOption := ServerlessJenkins
  1170  		log.Logger().Infof(util.QuestionAnswer("Configured Jenkins installation type", jenkinsInstallOption))
  1171  	}
  1172  	return nil
  1173  }
  1174  
  1175  func (options *InstallOptions) configureTillerNamespace() error {
  1176  	helmConfig := &options.CreateEnvOptions.HelmValuesConfig
  1177  	initOpts := &options.InitOptions
  1178  	tillerNameSpace := initOpts.Flags.TillerNamespace
  1179  	if tillerNameSpace != "" {
  1180  		if helmConfig.Jenkins.Servers.Global.EnvVars == nil {
  1181  			helmConfig.Jenkins.Servers.Global.EnvVars = map[string]string{}
  1182  		}
  1183  		helmConfig.Jenkins.Servers.Global.EnvVars["TILLER_NAMESPACE"] = tillerNameSpace
  1184  		err := os.Setenv("TILLER_NAMESPACE", tillerNameSpace)
  1185  		if err != nil {
  1186  			return errors.Wrapf(err, "failed to set env variable TILLER_NAMESPACE to %s", tillerNameSpace)
  1187  		}
  1188  	}
  1189  	return nil
  1190  }
  1191  
  1192  func (options *InstallOptions) configureHelmValues(namespace string) error {
  1193  	helmConfig := &options.CreateEnvOptions.HelmValuesConfig
  1194  
  1195  	domain := helmConfig.ExposeController.Config.Domain
  1196  	if domain != "" && addon.IsAddonEnabled("gitea") {
  1197  		helmConfig.Jenkins.Servers.GetOrCreateFirstGitea().Url = "http://gitea-gitea." + namespace + "." + domain
  1198  	}
  1199  
  1200  	err := options.addGitServersToJenkinsConfig(helmConfig)
  1201  	if err != nil {
  1202  		return errors.Wrap(err, "configuring the Git Servers into Jenkins configuration")
  1203  	}
  1204  
  1205  	err = options.configureTillerNamespace()
  1206  	if err != nil {
  1207  		return errors.Wrap(err, "configuring the tiller namespace")
  1208  	}
  1209  
  1210  	if !options.Flags.GitOpsMode {
  1211  		options.SetDevNamespace(namespace)
  1212  	}
  1213  
  1214  	isProw := options.Flags.Prow
  1215  	if isProw {
  1216  		enableJenkins := false
  1217  		helmConfig.Jenkins.Enabled = &enableJenkins
  1218  		helmConfig.ControllerBuild = &config.EnabledConfig{true}
  1219  		helmConfig.ControllerWorkflow = &config.EnabledConfig{false}
  1220  		if options.Flags.Tekton && options.Flags.Provider == cloud.GKE {
  1221  			helmConfig.DockerRegistryEnabled = &config.EnabledConfig{false}
  1222  		}
  1223  	}
  1224  	return nil
  1225  }
  1226  
  1227  func (options *InstallOptions) getHelmValuesFiles(configStore configio.ConfigStore, providerEnvDir string) ([]string, []string, []string, error) {
  1228  	helmConfig := &options.CreateEnvOptions.HelmValuesConfig
  1229  	cloudEnvironmentValuesLocation := filepath.Join(providerEnvDir, opts.CloudEnvValuesFile)
  1230  	cloudEnvironmentSecretsLocation := filepath.Join(providerEnvDir, opts.CloudEnvSecretsFile)
  1231  
  1232  	valuesFiles := []string{}
  1233  	secretsFiles := []string{}
  1234  	temporaryFiles := []string{}
  1235  
  1236  	adminSecretsFileName, adminSecrets, err := options.getAdminSecrets(configStore,
  1237  		providerEnvDir, cloudEnvironmentSecretsLocation)
  1238  	if err != nil {
  1239  		return valuesFiles, secretsFiles, temporaryFiles,
  1240  			errors.Wrap(err, "creating the admin secrets")
  1241  	}
  1242  
  1243  	dir, err := util.ConfigDir()
  1244  	if err != nil {
  1245  		return valuesFiles, secretsFiles, temporaryFiles,
  1246  			errors.Wrap(err, "creating a temporary config dir for Git credentials")
  1247  	}
  1248  
  1249  	extraValuesFileName := filepath.Join(dir, opts.ExtraValuesFile)
  1250  	err = configStore.WriteObject(extraValuesFileName, helmConfig)
  1251  	if err != nil {
  1252  		return valuesFiles, secretsFiles, temporaryFiles,
  1253  			errors.Wrapf(err, "writing the helm config in the file '%s'", extraValuesFileName)
  1254  	}
  1255  	log.Logger().Debugf("Generated helm values %s", util.ColorInfo(extraValuesFileName))
  1256  
  1257  	err = options.modifySecrets(helmConfig, adminSecrets)
  1258  	if err != nil {
  1259  		return valuesFiles, temporaryFiles, secretsFiles, errors.Wrap(err, "updating the secrets data in Kubernetes cluster")
  1260  	}
  1261  
  1262  	valuesFiles = append(valuesFiles, cloudEnvironmentValuesLocation)
  1263  	valuesFiles, err = helm.AppendMyValues(valuesFiles)
  1264  	if err != nil {
  1265  		return valuesFiles, secretsFiles, temporaryFiles,
  1266  			errors.Wrap(err, "failed to append the myvalues.yaml file")
  1267  	}
  1268  	secretsFiles = append(secretsFiles,
  1269  		[]string{adminSecretsFileName, extraValuesFileName, cloudEnvironmentSecretsLocation}...)
  1270  
  1271  	if options.Flags.Vault {
  1272  		temporaryFiles = append(temporaryFiles, adminSecretsFileName, extraValuesFileName, cloudEnvironmentSecretsLocation)
  1273  	} else {
  1274  		temporaryFiles = append(temporaryFiles, extraValuesFileName, cloudEnvironmentSecretsLocation)
  1275  	}
  1276  
  1277  	return util.FilterFileExists(valuesFiles), util.FilterFileExists(secretsFiles), util.FilterFileExists(temporaryFiles), nil
  1278  }
  1279  
  1280  func (options *InstallOptions) configureGitAuth() error {
  1281  	log.Logger().Infof("Set up a Git username and API token to be able to perform CI/CD")
  1282  	gitUsername := options.GitRepositoryOptions.Username
  1283  	gitServer := options.GitRepositoryOptions.ServerURL
  1284  	gitAPIToken := options.GitRepositoryOptions.ApiToken
  1285  	if gitUsername == "" {
  1286  		gitUsernameEnv := os.Getenv(JX_GIT_USER)
  1287  		if gitUsernameEnv != "" {
  1288  			gitUsername = gitUsernameEnv
  1289  		}
  1290  	}
  1291  
  1292  	if gitAPIToken == "" {
  1293  		gitAPITokenEnv := os.Getenv(JX_GIT_TOKEN)
  1294  		if gitAPITokenEnv != "" {
  1295  			gitAPIToken = gitAPITokenEnv
  1296  		}
  1297  	}
  1298  
  1299  	authConfigSvc, err := options.GitAuthConfigService()
  1300  	if err != nil {
  1301  		return errors.Wrap(err, "creating the git auth config service")
  1302  	}
  1303  
  1304  	authConfig := authConfigSvc.Config()
  1305  	var userAuth *auth.UserAuth
  1306  	if gitUsername != "" && gitAPIToken != "" && gitServer != "" {
  1307  		userAuth = &auth.UserAuth{
  1308  			ApiToken: gitAPIToken,
  1309  			Username: gitUsername,
  1310  		}
  1311  		authConfig.SetUserAuth(gitServer, userAuth)
  1312  	}
  1313  
  1314  	var authServer *auth.AuthServer
  1315  	if gitServer != "" {
  1316  		kind := ""
  1317  		if options.GitRepositoryOptions.ServerKind == "" {
  1318  			kind = gits.SaasGitKind(gitServer)
  1319  		} else {
  1320  			kind = options.GitRepositoryOptions.ServerKind
  1321  		}
  1322  		authServer = authConfig.GetOrCreateServerName(gitServer, "", kind)
  1323  	} else {
  1324  		authServer, err = authConfig.PickServer("Which Git provider:", options.BatchMode, options.GetIOFileHandles())
  1325  		if err != nil {
  1326  			return errors.Wrap(err, "getting the git provider from user")
  1327  		}
  1328  	}
  1329  
  1330  	message := fmt.Sprintf("local Git user for %s server:", authServer.Label())
  1331  	userAuth, err = authConfig.PickServerUserAuth(authServer, message, options.BatchMode, "", options.GetIOFileHandles())
  1332  	if err != nil {
  1333  		return errors.Wrapf(err, "selecting the local user for git server %s", authServer.Label())
  1334  	}
  1335  
  1336  	if userAuth.IsInvalid() {
  1337  		log.Logger().Infof("Creating a local Git user for %s server", authServer.Label())
  1338  		f := func(username string) error {
  1339  			options.Git().PrintCreateRepositoryGenerateAccessToken(authServer, username, options.Out)
  1340  			return nil
  1341  		}
  1342  		defaultUserName := ""
  1343  		err = authConfig.EditUserAuth(authServer.Label(), userAuth, defaultUserName, false, options.BatchMode, f,
  1344  			options.GetIOFileHandles())
  1345  		if err != nil {
  1346  			return errors.Wrapf(err, "creating a user authentication for git server %s", authServer.Label())
  1347  		}
  1348  		if userAuth.IsInvalid() {
  1349  			return fmt.Errorf("invalid user authentication for git server %s", authServer.Label())
  1350  		}
  1351  		authConfig.SetUserAuth(gitServer, userAuth)
  1352  	}
  1353  
  1354  	log.Logger().Infof("Select the CI/CD pipelines Git server and user")
  1355  	var pipelineAuthServer *auth.AuthServer
  1356  	if options.BatchMode {
  1357  		pipelineAuthServer = authServer
  1358  	} else {
  1359  		surveyOpts := survey.WithStdio(options.In, options.Out, options.Err)
  1360  		confirm := &survey.Confirm{
  1361  			Message: fmt.Sprintf("Do you wish to use %s as the pipelines Git server:", authServer.Label()),
  1362  			Default: true,
  1363  		}
  1364  		yes := false
  1365  		err = survey.AskOne(confirm, &yes, nil, surveyOpts)
  1366  		if err != nil {
  1367  			return errors.Wrap(err, "selecting pipelines Git server")
  1368  		}
  1369  		if yes {
  1370  			pipelineAuthServer = authServer
  1371  		} else {
  1372  			pipelineAuthServerURL, err := util.PickValue("Git Service URL:", gits.GitHubURL, true, "",
  1373  				options.GetIOFileHandles())
  1374  			if err != nil {
  1375  				return errors.Wrap(err, "reading the pipelines Git service URL")
  1376  			}
  1377  			pipelineAuthServer, err = authConfig.PickOrCreateServer(gits.GitHubURL, pipelineAuthServerURL,
  1378  				"Which Git Service do you wish to use:",
  1379  				options.BatchMode, options.GetIOFileHandles())
  1380  			if err != nil {
  1381  				return errors.Wrap(err, "selecting the pipelines Git Service")
  1382  			}
  1383  		}
  1384  	}
  1385  
  1386  	// lets default the values from the CLI arguments
  1387  	if options.GitRepositoryOptions.Username != "" {
  1388  		authConfig.PipeLineUsername = options.GitRepositoryOptions.Username
  1389  	}
  1390  	if options.GitRepositoryOptions.ServerURL != "" {
  1391  		authConfig.PipeLineServer = options.GitRepositoryOptions.ServerURL
  1392  	}
  1393  	pipelineUserAuth, err := options.PickPipelineUserAuth(authConfig, authServer)
  1394  	if err != nil {
  1395  		return errors.Wrapf(err, "selecting the pipeline user for git server %s", authServer.Label())
  1396  	}
  1397  	if pipelineUserAuth.IsInvalid() {
  1398  		log.Logger().Infof("Creating a pipelines Git user for %s server", authServer.Label())
  1399  		f := func(username string) error {
  1400  			options.Git().PrintCreateRepositoryGenerateAccessToken(pipelineAuthServer, username, options.Out)
  1401  			return nil
  1402  		}
  1403  		defaultUserName := ""
  1404  		err = authConfig.EditUserAuth(pipelineAuthServer.Label(), pipelineUserAuth, defaultUserName, false, options.BatchMode,
  1405  			f, options.GetIOFileHandles())
  1406  		if err != nil {
  1407  			return errors.Wrapf(err, "creating a pipeline user authentication for git server %s", authServer.Label())
  1408  		}
  1409  		if userAuth.IsInvalid() {
  1410  			return fmt.Errorf("invalid pipeline user authentication for git server %s", authServer.Label())
  1411  		}
  1412  		authConfig.SetUserAuth(pipelineAuthServer.URL, pipelineUserAuth)
  1413  	}
  1414  
  1415  	pipelineAuthServerURL := pipelineAuthServer.URL
  1416  	pipelineAuthUsername := pipelineUserAuth.Username
  1417  
  1418  	log.Logger().Infof("Setting the pipelines Git server %s and user name %s.",
  1419  		util.ColorInfo(pipelineAuthServerURL), util.ColorInfo(pipelineAuthUsername))
  1420  	authConfig.UpdatePipelineServer(pipelineAuthServer, pipelineUserAuth)
  1421  
  1422  	log.Logger().Debugf("Saving the Git authentication configuration")
  1423  	err = authConfigSvc.SaveConfig()
  1424  	if err != nil {
  1425  		return errors.Wrap(err, "saving the Git authentication configuration")
  1426  	}
  1427  
  1428  	editTeamSettingsCallback := func(env *v1.Environment) error {
  1429  		teamSettings := &env.Spec.TeamSettings
  1430  		teamSettings.GitServer = pipelineAuthServerURL
  1431  		teamSettings.PipelineUsername = pipelineAuthUsername
  1432  		teamSettings.Organisation = options.Owner
  1433  		teamSettings.GitPublic = options.GitRepositoryOptions.Public
  1434  		return nil
  1435  	}
  1436  	err = options.ModifyDevEnvironment(editTeamSettingsCallback)
  1437  	if err != nil {
  1438  		return errors.Wrap(err, "updating the team settings into the environment configuration")
  1439  	}
  1440  
  1441  	return nil
  1442  }
  1443  
  1444  func (options *InstallOptions) buildGitRepositoryOptionsForEnvironments() (*gits.GitRepositoryOptions, error) {
  1445  	authConfigSvc, err := options.GitAuthConfigService()
  1446  	if err != nil {
  1447  		return nil, errors.Wrap(err, "creating Git authentication config service")
  1448  	}
  1449  	config := authConfigSvc.Config()
  1450  
  1451  	server := config.CurrentAuthServer()
  1452  	if server == nil {
  1453  		return nil, fmt.Errorf("no current git server set in the configuration")
  1454  	}
  1455  	user := config.CurrentUser(server, false)
  1456  	if user == nil {
  1457  		return nil, fmt.Errorf("no current git user set in configuration for server '%s'", server.Label())
  1458  	}
  1459  
  1460  	org := options.Flags.EnvironmentGitOwner
  1461  	if org == "" {
  1462  		if options.BatchMode {
  1463  			jxClient, _, err := options.JXClientAndDevNamespace()
  1464  			if err != nil {
  1465  				return nil, errors.Wrap(err, "determining the git owner for environments")
  1466  			}
  1467  			org, _ = kube.GetDevEnvGitOwner(jxClient)
  1468  			if org == "" {
  1469  				org = user.Username
  1470  			}
  1471  
  1472  			log.Logger().Infof("Using %s environment git owner in batch mode.", util.ColorInfo(org))
  1473  		} else {
  1474  			provider, err := gits.CreateProvider(server, user, options.Git())
  1475  			if err != nil {
  1476  				return nil, errors.Wrap(err, "creating the Git provider")
  1477  			}
  1478  
  1479  			orgs := gits.GetOrganizations(provider, user.Username)
  1480  			if len(orgs) == 0 {
  1481  				return nil, fmt.Errorf("user '%s' has no organizations", user.Username)
  1482  			}
  1483  
  1484  			surveyOpts := survey.WithStdio(options.In, options.Out, options.Err)
  1485  			sort.Strings(orgs)
  1486  			prompt := &survey.Select{
  1487  				Message: "Select the organization where you want to create the environment repository:",
  1488  				Options: orgs,
  1489  			}
  1490  			err = survey.AskOne(prompt, &org, survey.Required, surveyOpts)
  1491  			if err != nil {
  1492  				return nil, errors.Wrap(err, "selecting the organization for environment repository")
  1493  			}
  1494  		}
  1495  	}
  1496  
  1497  	//Save selected organisation for Environment repos.
  1498  	err = options.ModifyDevEnvironment(func(env *v1.Environment) error {
  1499  		env.Spec.TeamSettings.EnvOrganisation = org
  1500  		return nil
  1501  	})
  1502  	if err != nil {
  1503  		return nil, errors.Wrap(err, "updating the TeamSettings with Environments organisation")
  1504  	}
  1505  
  1506  	return &gits.GitRepositoryOptions{
  1507  		ServerURL: server.URL,
  1508  		Username:  user.Username,
  1509  		ApiToken:  user.ApiToken,
  1510  		Owner:     org,
  1511  		Public:    options.GitRepositoryOptions.Public,
  1512  	}, nil
  1513  }
  1514  
  1515  func (options *InstallOptions) cleanupTempFiles(temporaryFiles []string) error {
  1516  	for _, tempFile := range temporaryFiles {
  1517  		exists, err := util.FileExists(tempFile)
  1518  		if exists && err == nil {
  1519  			err := util.DestroyFile(tempFile)
  1520  			if err != nil {
  1521  				return errors.Wrapf(err, "removing temporary file '%s'", tempFile)
  1522  			}
  1523  		}
  1524  	}
  1525  	return nil
  1526  }
  1527  
  1528  func (options *InstallOptions) verifyTiller(client kubernetes.Interface, namespace string) error {
  1529  	initOpts := &options.InitOptions
  1530  	if !initOpts.Flags.NoTiller {
  1531  		serviceAccountName := "tiller"
  1532  		tillerNamespace := options.InitOptions.Flags.TillerNamespace
  1533  
  1534  		log.Logger().Infof("Waiting for %s pod to be ready, service account name is %s, namespace is %s, tiller namespace is %s",
  1535  			util.ColorInfo("tiller"), util.ColorInfo(serviceAccountName), util.ColorInfo(namespace), util.ColorInfo(tillerNamespace))
  1536  
  1537  		clusterRoleBindingName := serviceAccountName + "-role-binding"
  1538  		role := options.InitOptions.Flags.TillerClusterRole
  1539  
  1540  		log.Logger().Infof("Waiting for cluster role binding to be defined, named %s in namespace %s", util.ColorInfo(clusterRoleBindingName), util.ColorInfo(namespace))
  1541  		err := options.EnsureClusterRoleBinding(clusterRoleBindingName, role, namespace, serviceAccountName)
  1542  		if err != nil {
  1543  			return errors.Wrap(err, "tiller cluster role not defined")
  1544  		}
  1545  		log.Logger().Infof("tiller cluster role defined: %s in namespace %s", util.ColorInfo(role), util.ColorInfo(namespace))
  1546  
  1547  		err = kube.WaitForDeploymentToBeReady(client, "tiller-deploy", tillerNamespace, 10*time.Minute)
  1548  		if err != nil {
  1549  			msg := fmt.Sprintf("tiller pod (tiller-deploy in namespace %s) is not running after 10 minutes", tillerNamespace)
  1550  			return errors.Wrap(err, msg)
  1551  		}
  1552  		log.Logger().Info("tiller pod running")
  1553  	}
  1554  	return nil
  1555  }
  1556  
  1557  func (options *InstallOptions) configureTillerInDevEnvironment() error {
  1558  	initOpts := &options.InitOptions
  1559  	if !initOpts.Flags.RemoteTiller && !initOpts.Flags.NoTiller {
  1560  		callback := func(env *v1.Environment) error {
  1561  			env.Spec.TeamSettings.NoTiller = true
  1562  			log.Logger().Info("Disabling the server side use of tiller in the TeamSettings")
  1563  			return nil
  1564  		}
  1565  		err := options.ModifyDevEnvironment(callback)
  1566  		if err != nil {
  1567  			return err
  1568  		}
  1569  	}
  1570  	return nil
  1571  }
  1572  
  1573  func (options *InstallOptions) configureProwInTeamSettings() error {
  1574  	if options.Flags.Prow {
  1575  		callback := func(env *v1.Environment) error {
  1576  			env.Spec.WebHookEngine = v1.WebHookEngineProw
  1577  			settings := &env.Spec.TeamSettings
  1578  			settings.PromotionEngine = v1.PromotionEngineProw
  1579  			settings.ProwEngine = v1.ProwEngineTypeTekton
  1580  			settings.ImportMode = v1.ImportModeTypeYAML
  1581  			log.Logger().Debugf("Configuring the TeamSettings for Prow with engine %s", string(settings.ProwEngine))
  1582  			return nil
  1583  		}
  1584  		err := options.ModifyDevEnvironment(callback)
  1585  		if err != nil {
  1586  			return err
  1587  		}
  1588  	}
  1589  	return nil
  1590  }
  1591  
  1592  func (options *InstallOptions) configureImportModeInTeamSettings() error {
  1593  	callback := func(env *v1.Environment) error {
  1594  		settings := &env.Spec.TeamSettings
  1595  		if string(settings.ImportMode) == "" {
  1596  			if options.Flags.Tekton {
  1597  				settings.ImportMode = v1.ImportModeTypeYAML
  1598  			} else {
  1599  				settings.ImportMode = v1.ImportModeTypeJenkinsfile
  1600  			}
  1601  		}
  1602  		log.Logger().Infof("Configuring the TeamSettings for ImportMode %s", string(settings.ImportMode))
  1603  		return nil
  1604  	}
  1605  	return options.ModifyDevEnvironment(callback)
  1606  }
  1607  
  1608  func (options *InstallOptions) configureGitOpsMode(configStore configio.ConfigStore, namespace string) (string, string, error) {
  1609  	gitOpsDir := ""
  1610  	gitOpsEnvDir := ""
  1611  	if options.Flags.GitOpsMode {
  1612  		var err error
  1613  		if options.Flags.Dir == "" {
  1614  			options.Flags.Dir, err = util.ConfigDir()
  1615  			if err != nil {
  1616  				return "", "", err
  1617  			}
  1618  		}
  1619  
  1620  		envName := fmt.Sprintf("environment-%s-dev", options.Flags.DefaultEnvironmentPrefix)
  1621  		gitOpsDir = filepath.Join(options.Flags.Dir, envName)
  1622  		gitOpsEnvDir = filepath.Join(gitOpsDir, "env")
  1623  		templatesDir := filepath.Join(gitOpsEnvDir, "templates")
  1624  		err = os.MkdirAll(templatesDir, util.DefaultWritePermissions)
  1625  		if err != nil {
  1626  			return "", "", errors.Wrapf(err, "Failed to make GitOps templates directory %s", templatesDir)
  1627  		}
  1628  
  1629  		options.ModifyDevEnvironmentFn = func(callback func(env *v1.Environment) error) error {
  1630  			defaultEnv := kube.CreateDefaultDevEnvironment(namespace)
  1631  			_, err := gitOpsModifyEnvironment(templatesDir, kube.LabelValueDevEnvironment, defaultEnv, configStore, callback)
  1632  			return err
  1633  		}
  1634  		options.ModifyEnvironmentFn = func(name string, callback func(env *v1.Environment) error) error {
  1635  			defaultEnv := &v1.Environment{}
  1636  			defaultEnv.Labels = map[string]string{}
  1637  			_, err := gitOpsModifyEnvironment(templatesDir, name, defaultEnv, configStore, callback)
  1638  			return err
  1639  		}
  1640  		options.InitOptions.ModifyDevEnvironmentFn = options.ModifyDevEnvironmentFn
  1641  		options.modifyConfigMapCallback = func(name string, callback func(configMap *core_v1.ConfigMap) error) (*core_v1.ConfigMap, error) {
  1642  			return gitOpsModifyConfigMap(templatesDir, name, nil, configStore, callback)
  1643  		}
  1644  		options.modifySecretCallback = func(name string, callback func(secret *core_v1.Secret) error) (*core_v1.Secret, error) {
  1645  			if options.Flags.Vault {
  1646  				_, devNamespace, err := options.KubeClientAndDevNamespace()
  1647  				if err != nil {
  1648  					return nil, errors.Wrap(err, "getting team's dev namesapces")
  1649  				}
  1650  				vaultClient, err := options.SystemVaultClient(devNamespace)
  1651  				if err != nil {
  1652  					return nil, errors.Wrapf(err, "retrieving the system vault client in namespace %s", devNamespace)
  1653  				}
  1654  				vaultConfigStore := configio.NewVaultStore(vaultClient, vault.GitOpsSecretsPath)
  1655  				return gitOpsModifySecret(vault.GitOpsTemplatesPath, name, nil, vaultConfigStore, callback)
  1656  			}
  1657  			return gitOpsModifySecret(templatesDir, name, nil, configStore, callback)
  1658  		}
  1659  	}
  1660  
  1661  	return gitOpsDir, gitOpsEnvDir, nil
  1662  }
  1663  
  1664  func (options *InstallOptions) generateGitOpsDevEnvironmentConfig(gitOpsDir string) (string, error) {
  1665  	if options.Flags.GitOpsMode {
  1666  		log.Logger().Infof("\n\nGenerated the source code for the GitOps development environment at %s", util.ColorInfo(gitOpsDir))
  1667  		log.Logger().Infof("You can apply this to the kubernetes cluster at any time in this directory via: %s\n", util.ColorInfo("jx step env apply"))
  1668  
  1669  		if !options.Flags.NoGitOpsEnvRepo {
  1670  			authConfigSvc, err := options.GitAuthConfigService()
  1671  			if err != nil {
  1672  				return "", errors.Wrap(err, "creating git auth config service")
  1673  			}
  1674  			config := &v1.Environment{
  1675  				Spec: v1.EnvironmentSpec{
  1676  					Label:             "Development",
  1677  					PromotionStrategy: v1.PromotionStrategyTypeNever,
  1678  					Kind:              v1.EnvironmentKindTypeDevelopment,
  1679  				},
  1680  			}
  1681  			config.Name = kube.LabelValueDevEnvironment
  1682  			var devEnv *v1.Environment
  1683  			err = options.ModifyDevEnvironment(func(env *v1.Environment) error {
  1684  				devEnv = env
  1685  				devEnv.Spec.TeamSettings.UseGitOps = true
  1686  				return nil
  1687  			})
  1688  			if err != nil {
  1689  				return "", errors.Wrap(err, "modifying the dev environment configuration")
  1690  			}
  1691  			envDir, err := util.EnvironmentsDir()
  1692  			if err != nil {
  1693  				return "", errors.Wrap(err, "getting the environments directory")
  1694  			}
  1695  			forkEnvGitURL := ""
  1696  			prefix := options.Flags.DefaultEnvironmentPrefix
  1697  
  1698  			git := options.Git()
  1699  			gitRepoOptions, err := options.buildGitRepositoryOptionsForEnvironments()
  1700  			if err != nil || gitRepoOptions == nil {
  1701  				if err == nil {
  1702  					err = errors.New("empty git repository options")
  1703  				}
  1704  				return "", errors.Wrap(err, "building the git repository options for environment")
  1705  			}
  1706  			repo, gitProvider, err := kube.CreateEnvGitRepository(options.BatchMode, authConfigSvc, devEnv, devEnv, config, forkEnvGitURL, envDir,
  1707  				gitRepoOptions, options.CreateEnvOptions.HelmValuesConfig, prefix, git, options.ResolveChartMuseumURL, options.GetIOFileHandles())
  1708  			if err != nil || repo == nil || gitProvider == nil {
  1709  				return "", errors.Wrap(err, "creating git repository for the dev environment source")
  1710  			}
  1711  
  1712  			dir := gitOpsDir
  1713  			err = git.Init(dir)
  1714  			if err != nil {
  1715  				return "", errors.Wrap(err, "initializing the dev environment repository")
  1716  			}
  1717  			err = options.ModifyDevEnvironment(func(env *v1.Environment) error {
  1718  				env.Spec.Source.URL = repo.CloneURL
  1719  				env.Spec.Source.Ref = "master"
  1720  				return nil
  1721  			})
  1722  			if err != nil {
  1723  				return "", errors.Wrap(err, "updating the source in the dev environment")
  1724  			}
  1725  
  1726  			err = git.Add(dir, ".gitignore")
  1727  			if err != nil {
  1728  				return "", errors.Wrap(err, "adding gitignore to the dev environment")
  1729  			}
  1730  			err = git.Add(dir, "*")
  1731  			if err != nil {
  1732  				return "", errors.Wrap(err, "adding all files from dev environment repo to git")
  1733  			}
  1734  			err = options.Git().CommitIfChanges(dir, "Initial import of Dev Environment source")
  1735  			if err != nil {
  1736  				return "", errors.Wrap(err, "committing in git if there are changes")
  1737  			}
  1738  			userAuth := gitProvider.UserAuth()
  1739  			pushGitURL, err := git.CreateAuthenticatedURL(repo.CloneURL, &userAuth)
  1740  			if err != nil {
  1741  				return "", errors.Wrapf(err, "creating push URL for %q", repo.CloneURL)
  1742  			}
  1743  			err = git.SetRemoteURL(dir, "origin", pushGitURL)
  1744  			if err != nil {
  1745  				return "", errors.Wrapf(err, "setting remote origin to %q", pushGitURL)
  1746  			}
  1747  			err = git.PushMaster(dir)
  1748  			if err != nil {
  1749  				return "", errors.Wrapf(err, "pushing master from repository %q", dir)
  1750  			}
  1751  			log.Logger().Infof("Pushed Git repository to %s\n", util.ColorInfo(repo.HTMLURL))
  1752  
  1753  			dir = filepath.Join(envDir, gitRepoOptions.Owner)
  1754  			if _, err := os.Stat(dir); os.IsNotExist(err) {
  1755  				if err := os.MkdirAll(dir, 0755); err != nil {
  1756  					return "", errors.Wrapf(err, "creating directory %q", dir)
  1757  				}
  1758  			}
  1759  			dir = filepath.Join(dir, repo.Name)
  1760  			if err := util.RenameDir(gitOpsDir, dir, true); err != nil {
  1761  				return "", errors.Wrap(err, "renaming dev environment dir")
  1762  			}
  1763  			return filepath.Join(dir, "env"), nil
  1764  		}
  1765  	}
  1766  
  1767  	return "", nil
  1768  }
  1769  
  1770  func (options *InstallOptions) applyGitOpsDevEnvironmentConfig(gitOpsEnvDir string, namespace string) error {
  1771  	if options.Flags.GitOpsMode && !options.Flags.NoGitOpsEnvApply {
  1772  		applyEnv := true
  1773  		if !options.BatchMode {
  1774  			if answer, err := util.Confirm("Would you like to setup the Development Environment from the source code now?", true, "Do you want to apply the development environment helm charts now?", options.GetIOFileHandles()); err != nil {
  1775  				return err
  1776  			} else if !answer {
  1777  				applyEnv = false
  1778  			}
  1779  		}
  1780  
  1781  		if applyEnv {
  1782  			// Reset the secret location cached in memory before creating the dev
  1783  			// environment. The location might have been changed in the cluster configuration.
  1784  			err := options.ResetSecretsLocation()
  1785  			if err != nil {
  1786  				return errors.Wrap(err, "unable to reset the secret location in memory")
  1787  			}
  1788  
  1789  			envApplyOptions := &env.StepEnvApplyOptions{
  1790  				StepEnvOptions: env.StepEnvOptions{
  1791  					StepOptions: step.StepOptions{
  1792  						CommonOptions: options.CommonOptions,
  1793  					},
  1794  				},
  1795  				Dir:         gitOpsEnvDir,
  1796  				Namespace:   namespace,
  1797  				ChangeNs:    true,
  1798  				Vault:       options.Flags.Vault,
  1799  				ReleaseName: "jenkins-x",
  1800  			}
  1801  
  1802  			err = envApplyOptions.Run()
  1803  			if err != nil {
  1804  				return errors.Wrap(err, "applying the dev environment configuration")
  1805  			}
  1806  		}
  1807  	}
  1808  
  1809  	return nil
  1810  }
  1811  
  1812  func (options *InstallOptions) setupGitOpsPostApply(ns string) error {
  1813  	if options.Flags.GitOpsMode && !options.Flags.NoGitOpsEnvSetup {
  1814  		if !options.Flags.Prow {
  1815  			err := options.configureJenkins(ns)
  1816  			if err != nil {
  1817  				return errors.Wrap(err, "configuring Jenkins")
  1818  			}
  1819  		} else {
  1820  			client, devNamespace, err := options.KubeClientAndDevNamespace()
  1821  
  1822  			settings, err := options.TeamSettings()
  1823  			if err != nil {
  1824  				return errors.Wrap(err, "reading the team settings")
  1825  			}
  1826  
  1827  			err = prow.AddDummyApplication(client, devNamespace, settings)
  1828  			if err != nil {
  1829  				return errors.Wrap(err, "adding dummy application")
  1830  			}
  1831  		}
  1832  
  1833  		jxClient, devNs, err := options.JXClientAndDevNamespace()
  1834  		if err != nil {
  1835  			return errors.Wrap(err, "getting jx client and dev namesapce")
  1836  		}
  1837  
  1838  		envs, err := kube.GetPermanentEnvironments(jxClient, devNs)
  1839  		if err != nil {
  1840  			return errors.Wrapf(err, "retrieving the current permanent environments in namespace %q", devNs)
  1841  		}
  1842  		devEnv, err := kube.GetDevEnvironment(jxClient, devNs)
  1843  		if err != nil {
  1844  			return errors.Wrapf(err, "get the dev environment namespace %q", devNs)
  1845  		}
  1846  		if devEnv != nil {
  1847  			envs = append(envs, devEnv)
  1848  		}
  1849  
  1850  		errs := []error{}
  1851  		createEnvOpts := CreateEnvOptions{
  1852  			CreateOptions: createoptions.CreateOptions{
  1853  				CommonOptions: options.CommonOptions,
  1854  			},
  1855  			Prefix: options.Flags.DefaultEnvironmentPrefix,
  1856  			Prow:   options.Flags.Prow,
  1857  		}
  1858  		if options.BatchMode {
  1859  			createEnvOpts.BatchMode = options.BatchMode
  1860  		}
  1861  		for _, env := range envs {
  1862  			err := createEnvOpts.RegisterEnvironment(env, nil, nil)
  1863  			if err != nil {
  1864  				errs = append(errs, errors.Wrapf(err, "registering environment %q", env.GetName()))
  1865  			}
  1866  			log.Logger().Infof("Registered environment %s", util.ColorInfo(env.GetName()))
  1867  		}
  1868  		return errorutil.CombineErrors(errs...)
  1869  	}
  1870  	return nil
  1871  }
  1872  
  1873  func (options *InstallOptions) installHelmBinaries() error {
  1874  	initOpts := &options.InitOptions
  1875  	helmBinary := initOpts.HelmBinary()
  1876  	dependencies := []string{}
  1877  	if !initOpts.Flags.RemoteTiller && !initOpts.Flags.NoTiller {
  1878  		binDir, err := util.JXBinLocation()
  1879  		if err != nil {
  1880  			return errors.Wrap(err, "reading jx bin location")
  1881  		}
  1882  		install, err := packages.ShouldInstallBinary("tiller")
  1883  		if !install && err == nil {
  1884  			confirm := &survey.Confirm{
  1885  				Message: "Uninstalling existing tiller binary:",
  1886  				Default: true,
  1887  			}
  1888  			flag := true
  1889  			err = survey.AskOne(confirm, &flag, nil)
  1890  			if err != nil || flag == false {
  1891  				return errors.New("Existing tiller must be uninstalled first in order to use the jx in tiller less mode")
  1892  			}
  1893  			// Uninstall helm and tiller first to avoid using some older version
  1894  			err = packages.UninstallBinary(binDir, "tiller")
  1895  			if err != nil {
  1896  				return errors.Wrap(err, "uninstalling existing tiller binary")
  1897  			}
  1898  		}
  1899  
  1900  		install, err = packages.ShouldInstallBinary(helmBinary)
  1901  		if !install && err == nil {
  1902  			confirm := &survey.Confirm{
  1903  				Message: "Uninstalling existing helm binary:",
  1904  				Default: true,
  1905  			}
  1906  			flag := true
  1907  			err = survey.AskOne(confirm, &flag, nil)
  1908  			if err != nil || flag == false {
  1909  				return errors.New("Existing helm must be uninstalled first in order to use the jx in tiller less mode")
  1910  			}
  1911  			// Uninstall helm and tiller first to avoid using some older version
  1912  			err = packages.UninstallBinary(binDir, helmBinary)
  1913  			if err != nil {
  1914  				return errors.Wrap(err, "uninstalling existing helm binary")
  1915  			}
  1916  		}
  1917  		dependencies = append(dependencies, "tiller")
  1918  		options.Helm().SetHost(helm.GetTillerAddress())
  1919  	}
  1920  	dependencies = append(dependencies, helmBinary)
  1921  	return options.InstallMissingDependencies(dependencies)
  1922  }
  1923  
  1924  // SetInstallValues sets the install values
  1925  func (options *InstallOptions) SetInstallValues(values map[string]string) {
  1926  	if values != nil {
  1927  		if options.installValues == nil {
  1928  			options.installValues = map[string]string{}
  1929  		}
  1930  		for k, v := range values {
  1931  			options.installValues[k] = v
  1932  		}
  1933  	}
  1934  }
  1935  
  1936  func (options *InstallOptions) configureCloudProviderPreInit(client kubernetes.Interface) error {
  1937  	switch options.Flags.Provider {
  1938  	case cloud.AKS:
  1939  		err := options.CreateClusterAdmin()
  1940  		if err != nil {
  1941  			return errors.Wrap(err, "creating cluster admin for AKS cloud provider")
  1942  		}
  1943  		log.Logger().Info("created role cluster-admin")
  1944  	case cloud.AWS:
  1945  		fallthrough
  1946  	case cloud.EKS:
  1947  		err := options.ensureDefaultStorageClass(client, "gp2", "kubernetes.io/aws-ebs", "gp2")
  1948  		if err != nil {
  1949  			return errors.Wrap(err, "ensuring default storage for EKS/AWS cloud provider")
  1950  		}
  1951  	default:
  1952  		return nil
  1953  	}
  1954  	return nil
  1955  }
  1956  
  1957  func (options *InstallOptions) configureCloudProivderPostInit(client kubernetes.Interface, namespace string) error {
  1958  	switch options.Flags.Provider {
  1959  	case cloud.OPENSHIFT:
  1960  		err := options.enableOpenShiftSCC(namespace)
  1961  		if err != nil {
  1962  			return errors.Wrap(err, "failed to enable the OpenShiftSCC")
  1963  		}
  1964  	case cloud.IKS:
  1965  		_, err := options.AddHelmBinaryRepoIfMissing(DEFAULT_IBMREPO_URL, "ibm", "", "")
  1966  		if err != nil {
  1967  			return errors.Wrap(err, "failed to add the IBM helm repo")
  1968  		}
  1969  		err = options.Helm().UpdateRepo()
  1970  		if err != nil {
  1971  			return errors.Wrap(err, "failed to update the helm repo")
  1972  		}
  1973  		helmOptions := helm.InstallChartOptions{
  1974  			Chart:       "ibm/ibmcloud-block-storage-plugin",
  1975  			ReleaseName: "ibmcloud-block-storage-plugin",
  1976  			NoForce:     true,
  1977  		}
  1978  		err = options.InstallChartWithOptions(helmOptions)
  1979  		if err != nil {
  1980  			return errors.Wrap(err, "failed to install/upgrade the IBM Cloud Block Storage drivers")
  1981  		}
  1982  		return options.changeDefaultStorageClass(client, "ibmc-block-bronze")
  1983  	default:
  1984  		return nil
  1985  	}
  1986  
  1987  	return nil
  1988  }
  1989  
  1990  func (options *InstallOptions) configureDockerRegistry(client kubernetes.Interface, namespace string) error {
  1991  	helmConfig := &options.CreateEnvOptions.HelmValuesConfig
  1992  	dockerRegistryConfig, dockerRegistry, err := options.configureCloudProviderRegistry(client, namespace)
  1993  	if err != nil {
  1994  		return errors.Wrap(err, "configure cloud provider docker registry")
  1995  	}
  1996  	if dockerRegistryConfig != "" {
  1997  		helmConfig.PipelineSecrets.DockerConfig = dockerRegistryConfig
  1998  	}
  1999  	if dockerRegistry != "" {
  2000  		if !options.Flags.Prow {
  2001  			if helmConfig.Jenkins.Servers.Global.EnvVars == nil {
  2002  				helmConfig.Jenkins.Servers.Global.EnvVars = map[string]string{}
  2003  			}
  2004  			helmConfig.Jenkins.Servers.Global.EnvVars["DOCKER_REGISTRY"] = dockerRegistry
  2005  		} else {
  2006  			helmConfig.DockerRegistry = dockerRegistry
  2007  		}
  2008  	}
  2009  	return nil
  2010  }
  2011  
  2012  func (options *InstallOptions) configureCloudProviderRegistry(client kubernetes.Interface, namespace string) (string, string, error) {
  2013  	dockerRegistry, err := options.dockerRegistryValue()
  2014  	if err != nil {
  2015  		return "", "", err
  2016  	}
  2017  	kubeConfig, _, err := options.Kube().LoadConfig()
  2018  	if err != nil {
  2019  		return "", "", err
  2020  	}
  2021  	switch options.Flags.Provider {
  2022  	case cloud.AKS:
  2023  		server := kube.CurrentServer(kubeConfig)
  2024  		azureCLI := aks.NewAzureRunner()
  2025  		resourceGroup, name, cluster, err := azureCLI.GetClusterClient(server)
  2026  		if err != nil {
  2027  			return "", "", errors.Wrap(err, "getting cluster from Azure")
  2028  		}
  2029  		registryID := ""
  2030  		config, dockerRegistry, registryID, err := azureCLI.GetRegistry(options.Flags.AzureRegistrySubscription, resourceGroup, name, dockerRegistry)
  2031  		if err != nil {
  2032  			return "", "", errors.Wrap(err, "getting registry configuration from Azure")
  2033  		}
  2034  		azureCLI.AssignRole(cluster, registryID)
  2035  		log.Logger().Infof("Assign AKS %s a reader role for ACR %s", util.ColorInfo(server), util.ColorInfo(dockerRegistry))
  2036  		return config, dockerRegistry, nil
  2037  	case cloud.IKS:
  2038  		dockerRegistry = iks.GetClusterRegistry(client)
  2039  		config, err := iks.GetRegistryConfigJSON(dockerRegistry)
  2040  		if err != nil {
  2041  			return "", "", errors.Wrap(err, "getting IKS registry configuration")
  2042  		}
  2043  		return config, dockerRegistry, nil
  2044  	case cloud.OPENSHIFT:
  2045  		if dockerRegistry == "docker-registry.default.svc:5000" {
  2046  			config, err := options.enableOpenShiftRegistryPermissions(namespace, dockerRegistry)
  2047  			if err != nil {
  2048  				return "", "", errors.Wrap(err, "enabling OpenShift registry permissions")
  2049  			}
  2050  			return config, dockerRegistry, nil
  2051  		}
  2052  	}
  2053  
  2054  	helmConfig := &options.CreateEnvOptions.HelmValuesConfig
  2055  	return helmConfig.PipelineSecrets.DockerConfig, dockerRegistry, nil
  2056  }
  2057  
  2058  func (options *InstallOptions) registerAllCRDs() error {
  2059  	if !options.GitOpsMode {
  2060  		apisClient, err := options.ApiExtensionsClient()
  2061  		if err != nil {
  2062  			return errors.Wrap(err, "failed to create the API extensions client")
  2063  		}
  2064  		err = kube.RegisterAllCRDs(apisClient)
  2065  		if err != nil {
  2066  			return err
  2067  		}
  2068  	}
  2069  	return nil
  2070  }
  2071  
  2072  func (options *InstallOptions) installCloudProviderDependencies() error {
  2073  	dependencies := []string{}
  2074  	err := options.InstallRequirements(options.Flags.Provider, dependencies...)
  2075  	if err != nil {
  2076  		return errors.Wrap(err, "installing cloud provider dependencies")
  2077  	}
  2078  	return nil
  2079  }
  2080  
  2081  func (options *InstallOptions) getAdminSecrets(configStore configio.ConfigStore, providerEnvDir string, cloudEnvironmentSecretsLocation string) (string, *config.AdminSecretsConfig, error) {
  2082  	cloudEnvironmentSopsLocation := filepath.Join(providerEnvDir, opts.CloudEnvSopsConfigFile)
  2083  	if _, err := os.Stat(providerEnvDir); os.IsNotExist(err) {
  2084  		return "", nil, fmt.Errorf("cloud environment dir %s not found", providerEnvDir)
  2085  	}
  2086  	sopsFileExists, err := util.FileExists(cloudEnvironmentSopsLocation)
  2087  	if err != nil {
  2088  		return "", nil, errors.Wrap(err, "failed to look for "+cloudEnvironmentSopsLocation)
  2089  	}
  2090  
  2091  	adminSecretsServiceInit := false
  2092  
  2093  	if sopsFileExists {
  2094  		log.Logger().Infof("Attempting to decrypt secrets file %s", util.ColorInfo(cloudEnvironmentSecretsLocation))
  2095  		// need to decrypt secrets now
  2096  		err = options.Helm().DecryptSecrets(cloudEnvironmentSecretsLocation)
  2097  		if err != nil {
  2098  			return "", nil, errors.Wrap(err, "failed to decrypt "+cloudEnvironmentSecretsLocation)
  2099  		}
  2100  
  2101  		cloudEnvironmentSecretsDecryptedLocation := filepath.Join(providerEnvDir, opts.CloudEnvSecretsFile+".dec")
  2102  		decryptedSecretsFile, err := util.FileExists(cloudEnvironmentSecretsDecryptedLocation)
  2103  		if err != nil {
  2104  			return "", nil, errors.Wrap(err, "failed to look for "+cloudEnvironmentSecretsDecryptedLocation)
  2105  		}
  2106  
  2107  		if decryptedSecretsFile {
  2108  			log.Logger().Infof("Successfully decrypted %s", util.ColorInfo(cloudEnvironmentSecretsDecryptedLocation))
  2109  			cloudEnvironmentSecretsLocation = cloudEnvironmentSecretsDecryptedLocation
  2110  
  2111  			err = options.AdminSecretsService.NewAdminSecretsConfigFromSecret(cloudEnvironmentSecretsDecryptedLocation)
  2112  			if err != nil {
  2113  				return "", nil, errors.Wrap(err, "failed to create the admin secret config service from the decrypted secrets file")
  2114  			}
  2115  			adminSecretsServiceInit = true
  2116  		}
  2117  	}
  2118  
  2119  	if !adminSecretsServiceInit {
  2120  		err = options.AdminSecretsService.NewAdminSecretsConfig()
  2121  		if err != nil {
  2122  			return "", nil, errors.Wrap(err, "failed to create the admin secret config service")
  2123  		}
  2124  	}
  2125  
  2126  	dir, err := util.ConfigDir()
  2127  	if err != nil {
  2128  		return "", nil, errors.Wrap(err, "creating a temporary config dir for Git credentials")
  2129  	}
  2130  
  2131  	adminSecrets := &options.AdminSecretsService.Secrets
  2132  	adminSecretsFileName := filepath.Join(dir, opts.AdminSecretsFile)
  2133  	err = configStore.WriteObject(adminSecretsFileName, adminSecrets)
  2134  	if err != nil {
  2135  		return "", nil, errors.Wrapf(err, "writing the admin secrets in the secrets file '%s'", adminSecretsFileName)
  2136  	}
  2137  
  2138  	if options.Flags.Vault {
  2139  		// lets make sure the devNamespace hasn't been overwritten to "default"
  2140  		if options.Flags.Namespace != "" {
  2141  			options.SetDevNamespace(options.Flags.Namespace)
  2142  		}
  2143  		err := options.storeAdminCredentialsInVault(&options.AdminSecretsService)
  2144  		if err != nil {
  2145  			return "", nil, errors.Wrapf(err, "storing the admin credentials in vault")
  2146  		}
  2147  	}
  2148  
  2149  	return adminSecretsFileName, adminSecrets, nil
  2150  }
  2151  
  2152  // ConfigureKaniko configures the kaniko SA and secret
  2153  func (options *InstallOptions) ConfigureKaniko() error {
  2154  	if options.Flags.Kaniko {
  2155  		if options.Flags.Provider != cloud.GKE {
  2156  			log.Logger().Infof("we are assuming your IAM roles are setup so that Kaniko can push images to your docker registry\n")
  2157  			return nil
  2158  		}
  2159  
  2160  		serviceAccountDir, err := ioutil.TempDir("", "gke")
  2161  		if err != nil {
  2162  			return errors.Wrap(err, "creating a temporary folder where the service account will be stored")
  2163  		}
  2164  		defer os.RemoveAll(serviceAccountDir) //nolint:errcheck
  2165  
  2166  		clusterName := options.installValues[kube.ClusterName]
  2167  		projectID := options.installValues[kube.ProjectID]
  2168  		if projectID == "" || clusterName == "" {
  2169  			if kubeClient, ns, err := options.KubeClientAndDevNamespace(); err == nil {
  2170  				if data, err := kube.ReadInstallValues(kubeClient, ns); err == nil && data != nil {
  2171  					if projectID == "" {
  2172  						projectID = data[kube.ProjectID]
  2173  					}
  2174  					if clusterName == "" {
  2175  						clusterName = data[kube.ClusterName]
  2176  					}
  2177  				}
  2178  			}
  2179  		}
  2180  		if projectID == "" {
  2181  			projectID, err = options.GetGoogleProjectID("")
  2182  			if err != nil {
  2183  				return errors.Wrap(err, "getting the GCP project ID")
  2184  			}
  2185  		}
  2186  		if clusterName == "" {
  2187  			clusterName, err = options.GetGKEClusterNameFromContext()
  2188  			if err != nil {
  2189  				return errors.Wrap(err, "getting the GKE cluster name from current context")
  2190  			}
  2191  		}
  2192  
  2193  		serviceAccountName := naming.ToValidGCPServiceAccount(fmt.Sprintf("%s-ko", clusterName))
  2194  		log.Logger().Infof("Configuring Kaniko service account %s for project %s", util.ColorInfo(serviceAccountName), util.ColorInfo(projectID))
  2195  		serviceAccountPath, err := options.GCloud().GetOrCreateServiceAccount(serviceAccountName, projectID, serviceAccountDir, gke.KanikoServiceAccountRoles)
  2196  		if err != nil {
  2197  			return errors.Wrap(err, "creating the service account")
  2198  		}
  2199  
  2200  		serviceAccount, err := ioutil.ReadFile(serviceAccountPath)
  2201  		if err != nil {
  2202  			return errors.Wrapf(err, "reading the service account from file '%s'", serviceAccountPath)
  2203  		}
  2204  
  2205  		options.AdminSecretsService.Flags.KanikoSecret = string(serviceAccount)
  2206  
  2207  	}
  2208  	return nil
  2209  }
  2210  
  2211  func (options *InstallOptions) createSystemVault(client kubernetes.Interface, namespace string, ic *kube.IngressConfig) error {
  2212  	if options.Flags.GitOpsMode && !options.Flags.NoGitOpsVault || options.Flags.Vault {
  2213  		if options.Flags.Provider != cloud.GKE && options.Flags.Provider != cloud.EKS && options.Flags.Provider != cloud.AWS {
  2214  			return fmt.Errorf("system vault is not supported for %s provider", options.Flags.Provider)
  2215  		}
  2216  
  2217  		if options.installValues == nil {
  2218  			return errors.New("no install values provided")
  2219  		}
  2220  
  2221  		// Configure the vault flag if only GitOps mode is on
  2222  		options.Flags.Vault = true
  2223  
  2224  		vaultOperatorClient, err := options.VaultOperatorClient()
  2225  		if err != nil {
  2226  			return err
  2227  		}
  2228  
  2229  		systemVaultName, err := kubevault.SystemVaultName(options.Kube())
  2230  		if err != nil {
  2231  			return errors.Wrap(err, "building the system vault name from cluster name")
  2232  		}
  2233  
  2234  		options.installValues[vault.SystemVaultName] = systemVaultName
  2235  
  2236  		if kubevault.FindVault(vaultOperatorClient, systemVaultName, namespace) {
  2237  			log.Logger().Infof("System vault named %s in namespace %s already exists",
  2238  				util.ColorInfo(systemVaultName), util.ColorInfo(namespace))
  2239  		} else {
  2240  			log.Logger().Info("Creating new system vault")
  2241  
  2242  			resolver, err := options.CreateVersionResolver("", "")
  2243  			if err != nil {
  2244  				return errors.Wrap(err, "creating the docker image version resolver")
  2245  			}
  2246  
  2247  			err = options.installOperator(resolver, namespace)
  2248  			if err != nil {
  2249  				return errors.Wrap(err, "installing Vault operator")
  2250  			}
  2251  
  2252  			vaultCreateParam := create.VaultCreationParam{
  2253  				VaultName:            systemVaultName,
  2254  				Namespace:            namespace,
  2255  				ClusterName:          options.installValues[kube.ClusterName],
  2256  				SecretsPathPrefix:    pkgvault.DefaultSecretsPathPrefix,
  2257  				KubeProvider:         options.Flags.Provider,
  2258  				KubeClient:           client,
  2259  				VaultOperatorClient:  vaultOperatorClient,
  2260  				VersionResolver:      *resolver,
  2261  				FileHandles:          options.GetIOFileHandles(),
  2262  				CreateCloudResources: true,
  2263  				Boot:                 false,
  2264  				BatchMode:            true,
  2265  			}
  2266  
  2267  			if options.Flags.Provider == cloud.GKE {
  2268  				gkeParam := &create.GKEParam{
  2269  					ProjectID: options.installValues[kube.ProjectID],
  2270  					Zone:      options.installValues[kube.Zone],
  2271  				}
  2272  				vaultCreateParam.GKE = gkeParam
  2273  			}
  2274  
  2275  			if options.Flags.Provider == cloud.EKS {
  2276  				awsParam, err := options.createAWSParam(options.installValues[kube.Region])
  2277  				if err != nil {
  2278  					return errors.Wrap(err, "unable to create Vault creation parameter from requirements")
  2279  				}
  2280  				vaultCreateParam.AWS = &awsParam
  2281  			}
  2282  
  2283  			vaultCreator := create.NewVaultCreator()
  2284  			err = vaultCreator.CreateOrUpdateVault(vaultCreateParam)
  2285  			if err != nil {
  2286  				return errors.Wrap(err, "unable to create/update Vault")
  2287  			}
  2288  
  2289  			err = options.exposeVault(systemVaultName, namespace, ic)
  2290  			if err != nil {
  2291  				return errors.Wrap(err, "unable to expose Vault")
  2292  			}
  2293  
  2294  			log.Logger().Infof("System vault created named %s in namespace %s.",
  2295  				util.ColorInfo(systemVaultName), util.ColorInfo(namespace))
  2296  		}
  2297  
  2298  		// Make sure that the dev namespace wasn't overwritten
  2299  		options.SetDevNamespace(namespace)
  2300  
  2301  		err = options.SetSecretsLocation(secrets.VaultLocationKind, false)
  2302  		if err != nil {
  2303  			return errors.Wrap(err, "setting the secrets location as vault")
  2304  		}
  2305  	}
  2306  	return nil
  2307  }
  2308  
  2309  func (options *InstallOptions) installOperator(resolver *versionstream.VersionResolver, ns string) error {
  2310  	tag, err := options.vaultOperatorImageTag(resolver)
  2311  	if err != nil {
  2312  		return errors.Wrap(err, "unable to determine Vault operator version")
  2313  	}
  2314  
  2315  	values := []string{
  2316  		"image.repository=" + kubevault.VaultOperatorImage,
  2317  		"image.tag=" + tag,
  2318  	}
  2319  	log.Logger().Infof("Installing %s operator with helm values: %v", util.ColorInfo(kube.DefaultVaultOperatorReleaseName), util.ColorInfo(values))
  2320  
  2321  	helmOptions := helm.InstallChartOptions{
  2322  		Chart:       kube.ChartVaultOperator,
  2323  		ReleaseName: kube.DefaultVaultOperatorReleaseName,
  2324  		Version:     options.Version,
  2325  		Ns:          ns,
  2326  		SetValues:   values,
  2327  	}
  2328  	err = options.InstallChartWithOptions(helmOptions)
  2329  	if err != nil {
  2330  		return errors.Wrap(err, "unable to install vault operator")
  2331  	}
  2332  
  2333  	log.Logger().Infof("Vault operator installed in namespace %s", ns)
  2334  	return nil
  2335  }
  2336  
  2337  // vaultOperatorImageTag lookups the vault operator image tag in the version stream
  2338  func (options *InstallOptions) vaultOperatorImageTag(resolver *versionstream.VersionResolver) (string, error) {
  2339  	fullImage, err := resolver.ResolveDockerImage(kubevault.VaultOperatorImage)
  2340  	if err != nil {
  2341  		return "", errors.Wrapf(err, "looking up the vault-operator %q image into the version stream",
  2342  			kubevault.VaultOperatorImage)
  2343  	}
  2344  	parts := strings.Split(fullImage, ":")
  2345  	if len(parts) != 2 {
  2346  		return "", fmt.Errorf("no tag found for image %q in version stream", kubevault.VaultOperatorImage)
  2347  	}
  2348  	return parts[1], nil
  2349  }
  2350  
  2351  func (options *InstallOptions) createAWSParam(defaultRegion string) (create.AWSParam, error) {
  2352  	if defaultRegion == "" {
  2353  		return create.AWSParam{}, errors.New("unable to find cluster region in requirements")
  2354  	}
  2355  
  2356  	dynamoDBRegion := options.DynamoDBRegion
  2357  	if dynamoDBRegion == "" {
  2358  		dynamoDBRegion = defaultRegion
  2359  		log.Logger().Infof("Region not specified for DynamoDB, defaulting to %s", util.ColorInfo(defaultRegion))
  2360  	}
  2361  
  2362  	kmsRegion := options.KMSRegion
  2363  	if kmsRegion == "" {
  2364  		kmsRegion = defaultRegion
  2365  		log.Logger().Infof("Region not specified for KMS, defaulting to %s", util.ColorInfo(defaultRegion))
  2366  
  2367  	}
  2368  
  2369  	s3Region := options.S3Region
  2370  	if s3Region == "" {
  2371  		s3Region = defaultRegion
  2372  		log.Logger().Infof("Region not specified for S3, defaulting to %s", util.ColorInfo(defaultRegion))
  2373  	}
  2374  
  2375  	awsParam := create.AWSParam{
  2376  		IAMUsername:     options.ProvidedIAMUsername,
  2377  		S3Bucket:        options.S3Bucket,
  2378  		S3Region:        s3Region,
  2379  		S3Prefix:        options.S3Prefix,
  2380  		DynamoDBTable:   options.DynamoDBTable,
  2381  		DynamoDBRegion:  dynamoDBRegion,
  2382  		KMSKeyID:        options.KMSKeyID,
  2383  		KMSRegion:       kmsRegion,
  2384  		AccessKeyID:     options.AccessKeyID,
  2385  		SecretAccessKey: options.SecretAccessKey,
  2386  		AutoCreate:      options.AutoCreate,
  2387  	}
  2388  
  2389  	return awsParam, nil
  2390  }
  2391  
  2392  func (options *InstallOptions) exposeVault(vaultService string, namespace string, ic *kube.IngressConfig) error {
  2393  	client, err := options.KubeClient()
  2394  	if err != nil {
  2395  		return err
  2396  	}
  2397  	svc, err := client.CoreV1().Services(namespace).Get(vaultService, metav1.GetOptions{})
  2398  	if err != nil {
  2399  		return errors.Wrapf(err, "getting the vault service: %s", vaultService)
  2400  	}
  2401  	if svc.Annotations == nil {
  2402  		svc.Annotations = map[string]string{}
  2403  	}
  2404  	if svc.Annotations[kube.AnnotationExpose] == "" {
  2405  		svc.Annotations[kube.AnnotationExpose] = "true"
  2406  		svc.Annotations[kube.AnnotationExposePort] = vault.DefaultVaultPort
  2407  		svc, err = client.CoreV1().Services(namespace).Update(svc)
  2408  		if err != nil {
  2409  			return errors.Wrapf(err, "updating %s service annotations", vaultService)
  2410  		}
  2411  	}
  2412  
  2413  	upgradeIngOpts := &upgrade.UpgradeIngressOptions{
  2414  		CommonOptions:       options.CommonOptions,
  2415  		Namespaces:          []string{namespace},
  2416  		Services:            []string{vaultService},
  2417  		IngressConfig:       *ic,
  2418  		SkipResourcesUpdate: true,
  2419  		WaitForCerts:        true,
  2420  	}
  2421  	return upgradeIngOpts.Run()
  2422  }
  2423  
  2424  func (options *InstallOptions) storeSecretYamlFilesInVault(path string, files ...string) error {
  2425  	_, devNamespace, err := options.KubeClientAndDevNamespace()
  2426  	if err != nil {
  2427  		return errors.Wrap(err, "getting team's dev namespace")
  2428  	}
  2429  	vaultClient, err := options.SystemVaultClient(devNamespace)
  2430  	if err != nil {
  2431  		return errors.Wrapf(err, "retrieving the system vault client in namespace %s", devNamespace)
  2432  	}
  2433  
  2434  	err = vault.WriteYamlFiles(vaultClient, path, files...)
  2435  	if err != nil {
  2436  		return errors.Wrapf(err, "storing in vault the secret YAML files: %s", strings.Join(files, ","))
  2437  	}
  2438  
  2439  	return nil
  2440  }
  2441  
  2442  func (options *InstallOptions) storeAdminCredentialsInVault(svc *config.AdminSecretsService) error {
  2443  	_, devNamespace, err := options.KubeClientAndDevNamespace()
  2444  	if err != nil {
  2445  		return errors.Wrap(err, "getting team's dev namespace")
  2446  	}
  2447  	vaultClient, err := options.SystemVaultClient(devNamespace)
  2448  	if err != nil {
  2449  		return errors.Wrapf(err, "retrieving the system vault client in namespace %s", devNamespace)
  2450  	}
  2451  	secrets := map[vault.AdminSecret]config.BasicAuth{
  2452  		vault.JenkinsAdminSecret:     svc.JenkinsAuth(),
  2453  		vault.IngressAdminSecret:     svc.IngressAuth(),
  2454  		vault.ChartmuseumAdminSecret: svc.ChartMuseumAuth(),
  2455  		vault.GrafanaAdminSecret:     svc.GrafanaAuth(),
  2456  		vault.NexusAdminSecret:       svc.NexusAuth(),
  2457  	}
  2458  	for secretName, secret := range secrets {
  2459  		path := vault.AdminSecretPath(secretName)
  2460  		err := vault.WriteBasicAuth(vaultClient, path, secret)
  2461  		if err != nil {
  2462  			return errors.Wrapf(err, "storing in vault the basic auth credentials for %s", secretName)
  2463  		}
  2464  	}
  2465  	return nil
  2466  }
  2467  
  2468  func (options *InstallOptions) configureBuildPackMode() error {
  2469  	ebp := &edit.EditBuildPackOptions{
  2470  		BuildPackName: options.Flags.BuildPackName,
  2471  	}
  2472  	ebp.CommonOptions = options.CommonOptions
  2473  
  2474  	return ebp.Run()
  2475  }
  2476  
  2477  func (options *InstallOptions) configureLongTermStorageBucket() error {
  2478  
  2479  	if options.IsConfigExplicitlySet("install", longTermStorageFlagName) && !options.Flags.LongTermStorage {
  2480  		return nil
  2481  	}
  2482  
  2483  	if !options.BatchMode && !options.Flags.LongTermStorage {
  2484  		if options.AdvancedMode {
  2485  			surveyOpts := survey.WithStdio(options.In, options.Out, options.Err)
  2486  			confirm := &survey.Confirm{
  2487  				Message: fmt.Sprintf("Would you like to enable long term logs storage?"+
  2488  					" A bucket for provider %s will be created", options.Flags.Provider),
  2489  				Default: true,
  2490  			}
  2491  			err := survey.AskOne(confirm, &options.Flags.LongTermStorage, nil, surveyOpts)
  2492  			if err != nil {
  2493  				return errors.Wrap(err, "asking to enable Long Term Storage")
  2494  			}
  2495  		} else {
  2496  			if options.Flags.Provider == cloud.GKE {
  2497  				options.Flags.LongTermStorage = true
  2498  				log.Logger().Infof(util.QuestionAnswer("Default enabling long term logs storage", util.YesNo(options.Flags.LongTermStorage)))
  2499  			} else {
  2500  				options.Flags.LongTermStorage = false
  2501  				log.Logger().Debugf("Long Term Storage not supported by provider '%s', disabling this option", options.Flags.Provider)
  2502  			}
  2503  		}
  2504  	} else {
  2505  		log.Logger().Infof(util.QuestionAnswer("Configured to use long term logs storage", util.YesNo(options.Flags.LongTermStorage)))
  2506  	}
  2507  
  2508  	if options.Flags.LongTermStorage {
  2509  
  2510  		var bucketURL string
  2511  		switch strings.ToUpper(options.Flags.Provider) {
  2512  		case "GKE":
  2513  			err := options.ensureGKEInstallValuesAreFilled()
  2514  			if err != nil {
  2515  				return errors.Wrap(err, "filling install values with cluster information")
  2516  			}
  2517  			bucketURL, err = gkeStorage.EnableLongTermStorage(options.GCloud(), options.installValues,
  2518  				options.Flags.LongTermStorageBucketName)
  2519  			if err != nil {
  2520  				return errors.Wrap(err, "enabling long term storage on GKE")
  2521  			}
  2522  			break
  2523  		default:
  2524  			return errors.Errorf("long term storage is not yet supported for provider %s", options.Flags.Provider)
  2525  		}
  2526  		return options.assignBucketToTeamStorage(bucketURL)
  2527  	}
  2528  	return nil
  2529  }
  2530  
  2531  func (options *InstallOptions) assignBucketToTeamStorage(bucketURL string) error {
  2532  	//Enable storage of logs into the bucketURL
  2533  	eso := edit.EditStorageOptions{
  2534  		CommonOptions: options.CommonOptions,
  2535  		StorageLocation: v1.StorageLocation{
  2536  			Classifier: "default",
  2537  			BucketURL:  bucketURL,
  2538  		},
  2539  	}
  2540  	infoBucketURL := util.ColorInfo(bucketURL)
  2541  	log.Logger().Debugf("Enabling default storage for current team in the bucket %s", infoBucketURL)
  2542  	err := eso.Run()
  2543  	if err != nil {
  2544  		return errors.Wrapf(err, "there was a problem executing `jx edit -c default --bucket-url=%s",
  2545  			infoBucketURL)
  2546  	}
  2547  
  2548  	eso.StorageLocation.Classifier = "logs"
  2549  	log.Logger().Debugf("Enabling logs storage for current team in the bucket %s", infoBucketURL)
  2550  	//Only GCS seems to be supported atm
  2551  	err = eso.Run()
  2552  	if err != nil {
  2553  		return errors.Wrapf(err, "there was a problem executing `jx edit -c logs --bucket-url=%s",
  2554  			infoBucketURL)
  2555  	}
  2556  
  2557  	return nil
  2558  }
  2559  
  2560  func (options *InstallOptions) ensureGKEInstallValuesAreFilled() error {
  2561  	if options.installValues == nil {
  2562  		options.installValues = make(map[string]string)
  2563  	}
  2564  
  2565  	if options.installValues[kube.ProjectID] == "" {
  2566  		currentProjectID, err := gke.GetCurrentProject()
  2567  		if err != nil {
  2568  			return errors.Wrap(err, "obtaining the current project from GKE context")
  2569  		}
  2570  		options.installValues[kube.ProjectID] = currentProjectID
  2571  	}
  2572  
  2573  	if options.installValues[kube.Zone] == "" {
  2574  		gcpCurrentZone, err := options.GetGoogleZone(options.installValues[kube.ProjectID], "")
  2575  		if err != nil {
  2576  			return errors.Wrap(err, "asking for the zone to create the bucket into")
  2577  		}
  2578  		options.installValues[kube.Zone] = gcpCurrentZone
  2579  	}
  2580  
  2581  	if options.installValues[kube.ClusterName] == "" {
  2582  		clusterName, err := cluster.Name(options.Kube())
  2583  		if err != nil {
  2584  			return errors.Wrap(err, "obtaining the current cluster name")
  2585  		}
  2586  		options.installValues[kube.ClusterName] = clusterName
  2587  	}
  2588  
  2589  	return nil
  2590  }
  2591  
  2592  func (options *InstallOptions) saveIngressConfig() (*kube.IngressConfig, error) {
  2593  	exposeController := options.CreateEnvOptions.HelmValuesConfig.ExposeController
  2594  	tls, err := util.ParseBool(exposeController.Config.TLSAcme)
  2595  	if err != nil {
  2596  		return nil, fmt.Errorf("failed to parse TLS exposecontroller boolean %v", err)
  2597  	}
  2598  	domain := exposeController.Config.Domain
  2599  	ic := kube.IngressConfig{
  2600  		Domain:      domain,
  2601  		TLS:         tls,
  2602  		Exposer:     exposeController.Config.Exposer,
  2603  		UrlTemplate: exposeController.Config.URLTemplate,
  2604  	}
  2605  	// save ingress config details to a configmap
  2606  	_, err = options.saveAsConfigMap(kube.IngressConfigConfigmap, ic)
  2607  	if err != nil {
  2608  		return nil, err
  2609  	}
  2610  	return &ic, nil
  2611  }
  2612  
  2613  func (options *InstallOptions) saveClusterConfig() error {
  2614  	jxInstallConfig := &kube.JXInstallConfig{
  2615  		KubeProvider: options.Flags.Provider,
  2616  	}
  2617  	kubeConfig, _, err := options.Kube().LoadConfig()
  2618  	if err != nil {
  2619  		return errors.Wrap(err, "retrieving the current kube config")
  2620  	}
  2621  	if kubeConfig != nil {
  2622  		kubeConfigContext := kube.CurrentContext(kubeConfig)
  2623  		if kubeConfigContext != nil {
  2624  			server := kube.Server(kubeConfig, kubeConfigContext)
  2625  			certificateAuthorityData := kube.CertificateAuthorityData(kubeConfig, kubeConfigContext)
  2626  			jxInstallConfig.Server = server
  2627  			jxInstallConfig.CA = certificateAuthorityData
  2628  		}
  2629  	}
  2630  
  2631  	if options.installValues == nil {
  2632  		options.installValues = map[string]string{}
  2633  	}
  2634  	installVersionKey := "jx-install-version"
  2635  	if options.installValues[installVersionKey] == "" {
  2636  		options.installValues[installVersionKey] = version2.GetVersion()
  2637  	}
  2638  	var secretsLocation secrets.SecretsLocationKind
  2639  	if options.Flags.Vault {
  2640  		secretsLocation = secrets.VaultLocationKind
  2641  	} else {
  2642  		secretsLocation = secrets.FileSystemLocationKind
  2643  	}
  2644  	options.installValues[secrets.SecretsLocationKey] = string(secretsLocation)
  2645  
  2646  	_, err = options.ModifyConfigMap(kube.ConfigMapNameJXInstallConfig, func(cm *core_v1.ConfigMap) error {
  2647  		if cm.Data == nil {
  2648  			cm.Data = make(map[string]string)
  2649  		}
  2650  		data := util.ToStringMapStringFromStruct(jxInstallConfig)
  2651  		for k, v := range data {
  2652  			cm.Data[k] = v
  2653  		}
  2654  		iv := options.installValues
  2655  		for k, v := range iv {
  2656  			cm.Data[k] = v
  2657  		}
  2658  		return nil
  2659  	})
  2660  	if err != nil {
  2661  		return errors.Wrapf(err, "saving cluster config into config map %q", kube.ConfigMapNameJXInstallConfig)
  2662  	}
  2663  	return nil
  2664  }
  2665  
  2666  func (options *InstallOptions) configureJenkins(namespace string) error {
  2667  	if !options.Flags.Prow {
  2668  		log.Logger().Info("Configure Jenkins API Token")
  2669  		if isOpenShiftProvider(options.Flags.Provider) {
  2670  			options.CreateJenkinsUserOptions.CommonOptions = options.CommonOptions
  2671  			options.CreateJenkinsUserOptions.Password = options.AdminSecretsService.Flags.DefaultAdminPassword
  2672  			options.CreateJenkinsUserOptions.Username = "jenkins-admin"
  2673  			options.CreateJenkinsUserOptions.Verbose = false
  2674  			jenkinsSaToken, err := options.GetCommandOutput("", "oc", "serviceaccounts", "get-token", "jenkins", "-n", namespace)
  2675  			if err != nil {
  2676  				return errors.Wrap(err, "getting token from service account jenkins")
  2677  			}
  2678  			options.CreateJenkinsUserOptions.BearerToken = jenkinsSaToken
  2679  			err = options.CreateJenkinsUserOptions.Run()
  2680  			if err != nil {
  2681  				return errors.Wrap(err, "creating Jenkins API token")
  2682  			}
  2683  		} else {
  2684  			err := options.Retry(3, 2*time.Second, func() (err error) {
  2685  				_, devNamespace, err := options.KubeClientAndDevNamespace()
  2686  				if err != nil {
  2687  					return errors.Wrap(err, "getting team's dev namespace")
  2688  				}
  2689  				options.CreateJenkinsUserOptions.CommonOptions = options.CommonOptions
  2690  				options.CreateJenkinsUserOptions.Namespace = devNamespace
  2691  				options.CreateJenkinsUserOptions.RecreateToken = true
  2692  				options.CreateJenkinsUserOptions.Username = options.AdminSecretsService.Flags.DefaultAdminUsername
  2693  				options.CreateJenkinsUserOptions.Password = options.AdminSecretsService.Flags.DefaultAdminPassword
  2694  				options.CreateJenkinsUserOptions.Verbose = false
  2695  				options.CreateJenkinsUserOptions.RecreateToken = true
  2696  				if options.BatchMode {
  2697  					options.CreateJenkinsUserOptions.BatchMode = true
  2698  				}
  2699  				err = options.CreateJenkinsUserOptions.Run()
  2700  				return
  2701  			})
  2702  			if err != nil {
  2703  				return errors.Wrap(err, "creating Jenkins API token")
  2704  			}
  2705  		}
  2706  
  2707  		err := options.UpdateJenkinsURL([]string{namespace})
  2708  		if err != nil {
  2709  			log.Logger().Warnf("Failed to update the Jenkins external URL: %s", err)
  2710  		}
  2711  	}
  2712  	return nil
  2713  }
  2714  
  2715  func (options *InstallOptions) installAddons() error {
  2716  	if !options.Flags.GitOpsMode {
  2717  		addonConfig, err := addon.LoadAddonsConfig()
  2718  		if err != nil {
  2719  			return errors.Wrap(err, "failed to load the addons configuration")
  2720  		}
  2721  
  2722  		for _, ac := range addonConfig.Addons {
  2723  			if ac.Enabled {
  2724  				err = options.installAddon(ac.Name)
  2725  				if err != nil {
  2726  					return fmt.Errorf("failed to install addon %s: %s", ac.Name, err)
  2727  				}
  2728  			}
  2729  		}
  2730  	}
  2731  	return nil
  2732  }
  2733  
  2734  func (options *InstallOptions) createEnvironments(namespace string) error {
  2735  	if !options.Flags.NoDefaultEnvironments {
  2736  		if options.Flags.GitOpsMode {
  2737  			options.SetDevNamespace(namespace)
  2738  			options.CreateEnvOptions.CommonOptions = options.CommonOptions
  2739  			options.CreateEnvOptions.GitOpsMode = true
  2740  			options.CreateEnvOptions.ModifyDevEnvironmentFn = options.ModifyDevEnvironmentFn
  2741  			options.CreateEnvOptions.ModifyEnvironmentFn = options.ModifyEnvironmentFn
  2742  		}
  2743  
  2744  		log.Logger().Info("Creating default staging and production environments")
  2745  		_, devNamespace, err := options.KubeClientAndDevNamespace()
  2746  		if err != nil {
  2747  			return errors.Wrap(err, "getting team's dev namespace")
  2748  		}
  2749  		gitRepoOptions, err := options.buildGitRepositoryOptionsForEnvironments()
  2750  		if err != nil || gitRepoOptions == nil {
  2751  			return errors.Wrap(err, "building the Git repository options for environments")
  2752  		}
  2753  		options.CreateEnvOptions.GitRepositoryOptions = *gitRepoOptions
  2754  		// lets not fail if environments already exist
  2755  		options.CreateEnvOptions.Update = true
  2756  
  2757  		options.CreateEnvOptions.Prefix = options.Flags.DefaultEnvironmentPrefix
  2758  		options.CreateEnvOptions.Prow = options.Flags.Prow
  2759  		if options.BatchMode {
  2760  			options.CreateEnvOptions.BatchMode = options.BatchMode
  2761  		}
  2762  		options.CreateEnvOptions.Options.Name = "staging"
  2763  		options.CreateEnvOptions.Options.Spec.Label = "Staging"
  2764  		options.CreateEnvOptions.Options.Spec.Order = 100
  2765  		options.CreateEnvOptions.Options.Spec.RemoteCluster = options.Flags.RemoteEnvironments
  2766  		err = options.CreateEnvOptions.Run()
  2767  		if err != nil {
  2768  			return errors.Wrapf(err, "failed to create staging environment in namespace %s", devNamespace)
  2769  		}
  2770  		options.CreateEnvOptions.Options.Name = "production"
  2771  		options.CreateEnvOptions.Options.Spec.Label = "Production"
  2772  		options.CreateEnvOptions.Options.Spec.Order = 200
  2773  		options.CreateEnvOptions.Options.Spec.RemoteCluster = options.Flags.RemoteEnvironments
  2774  		options.CreateEnvOptions.Options.Spec.PromotionStrategy = v1.PromotionStrategyTypeManual
  2775  		options.CreateEnvOptions.PromotionStrategy = string(v1.PromotionStrategyTypeManual)
  2776  
  2777  		err = options.CreateEnvOptions.Run()
  2778  		if err != nil {
  2779  			return errors.Wrapf(err, "failed to create the production environment in namespace %s", devNamespace)
  2780  		}
  2781  	}
  2782  	return nil
  2783  }
  2784  
  2785  func (options *InstallOptions) modifySecrets(helmConfig *config.HelmValuesConfig, adminSecrets *config.AdminSecretsConfig) error {
  2786  	var err error
  2787  	data := make(map[string][]byte)
  2788  	data[opts.ExtraValuesFile], err = yaml.Marshal(helmConfig)
  2789  	if err != nil {
  2790  		return err
  2791  	}
  2792  	data[opts.AdminSecretsFile], err = yaml.Marshal(adminSecrets)
  2793  	if err != nil {
  2794  		return err
  2795  	}
  2796  	_, err = options.ModifySecret(opts.JXInstallConfig, func(secret *core_v1.Secret) error {
  2797  		secret.Data = data
  2798  		return nil
  2799  	})
  2800  	return err
  2801  }
  2802  
  2803  // ModifySecret modifies the Secret either live or via the file system if generating the GitOps source
  2804  func (options *InstallOptions) ModifySecret(name string, callback func(*core_v1.Secret) error) (*core_v1.Secret, error) {
  2805  	if options.modifySecretCallback == nil {
  2806  		options.modifySecretCallback = func(name string, callback func(*core_v1.Secret) error) (*core_v1.Secret, error) {
  2807  			kubeClient, ns, err := options.KubeClientAndDevNamespace()
  2808  			if err != nil {
  2809  				return nil, err
  2810  			}
  2811  			return kube.DefaultModifySecret(kubeClient, ns, name, callback, nil)
  2812  		}
  2813  	}
  2814  	return options.modifySecretCallback(name, callback)
  2815  }
  2816  
  2817  // ModifyConfigMap modifies the ConfigMap either live or via the file system if generating the GitOps source
  2818  func (options *InstallOptions) ModifyConfigMap(name string, callback func(*core_v1.ConfigMap) error) (*core_v1.ConfigMap, error) {
  2819  	if options.modifyConfigMapCallback == nil {
  2820  		options.modifyConfigMapCallback = func(name string, callback func(*core_v1.ConfigMap) error) (*core_v1.ConfigMap, error) {
  2821  			kubeClient, ns, err := options.KubeClientAndDevNamespace()
  2822  			if err != nil {
  2823  				return nil, err
  2824  			}
  2825  			return kube.DefaultModifyConfigMap(kubeClient, ns, name, callback, nil)
  2826  		}
  2827  	}
  2828  	return options.modifyConfigMapCallback(name, callback)
  2829  }
  2830  
  2831  // gitOpsModifyConfigMap provides a helper function to lazily create, modify and save the YAML file in the given directory
  2832  func gitOpsModifyConfigMap(dir string, name string, defaultResource *core_v1.ConfigMap, configStore configio.ConfigStore,
  2833  	callback func(configMap *core_v1.ConfigMap) error) (*core_v1.ConfigMap, error) {
  2834  	answer := core_v1.ConfigMap{}
  2835  	fileName := filepath.Join(dir, name+"-configmap.yaml")
  2836  	exists, err := util.FileExists(fileName)
  2837  	if err != nil {
  2838  		return &answer, errors.Wrapf(err, "Could not check if file exists %s", fileName)
  2839  	}
  2840  	if exists {
  2841  		err = configStore.ReadObject(fileName, &answer)
  2842  		if err != nil {
  2843  			return &answer, errors.Wrapf(err, "Failed to unmarshall YAML file %s", fileName)
  2844  		}
  2845  	} else if defaultResource != nil {
  2846  		answer = *defaultResource
  2847  	} else {
  2848  		answer.Name = name
  2849  	}
  2850  	err = callback(&answer)
  2851  	if err != nil {
  2852  		return &answer, err
  2853  	}
  2854  	if answer.APIVersion == "" {
  2855  		answer.APIVersion = "v1"
  2856  	}
  2857  	if answer.Kind == "" {
  2858  		answer.Kind = "ConfigMap"
  2859  	}
  2860  	if answer.Data == nil {
  2861  		answer.Data = make(map[string]string)
  2862  	}
  2863  	err = configStore.WriteObject(fileName, &answer)
  2864  	if err != nil {
  2865  		return &answer, errors.Wrapf(err, "Could not save file %s", fileName)
  2866  	}
  2867  	return &answer, nil
  2868  }
  2869  
  2870  // gitOpsModifySecret provides a helper function to lazily create, modify and save the YAML file in the given directory
  2871  func gitOpsModifySecret(dir string, name string, defaultResource *core_v1.Secret, configStore configio.ConfigStore,
  2872  	callback func(secret *core_v1.Secret) error) (*core_v1.Secret, error) {
  2873  	answer := core_v1.Secret{}
  2874  	fileName := filepath.Join(dir, name+"-secret.yaml")
  2875  	exists, err := util.FileExists(fileName)
  2876  	if err != nil {
  2877  		return &answer, errors.Wrapf(err, "checking if file exists %s", fileName)
  2878  	}
  2879  	if exists {
  2880  		// lets unmarshall the data
  2881  		err = configStore.ReadObject(fileName, &answer)
  2882  		if err != nil {
  2883  			return &answer, err
  2884  		}
  2885  	} else if defaultResource != nil {
  2886  		answer = *defaultResource
  2887  	} else {
  2888  		answer.Name = name
  2889  	}
  2890  	err = callback(&answer)
  2891  	if err != nil {
  2892  		return &answer, err
  2893  	}
  2894  	if answer.APIVersion == "" {
  2895  		answer.APIVersion = "v1"
  2896  	}
  2897  	if answer.Kind == "" {
  2898  		answer.Kind = "Secret"
  2899  	}
  2900  	err = configStore.WriteObject(fileName, &answer)
  2901  	if err != nil {
  2902  		return &answer, errors.Wrapf(err, "Could not save file %s", fileName)
  2903  	}
  2904  	return &answer, nil
  2905  }
  2906  
  2907  // gitOpsModifyEnvironment provides a helper function to lazily create, modify and save the YAML file in the given directory
  2908  func gitOpsModifyEnvironment(dir string, name string, defaultEnvironment *v1.Environment, configStore configio.ConfigStore,
  2909  	callback func(*v1.Environment) error) (*v1.Environment, error) {
  2910  	answer := v1.Environment{}
  2911  	fileName := filepath.Join(dir, name+"-env.yaml")
  2912  	exists, err := util.FileExists(fileName)
  2913  	if err != nil {
  2914  		return &answer, errors.Wrapf(err, "Could not check if file exists %s", fileName)
  2915  	}
  2916  	if exists {
  2917  		// lets unmarshal the data
  2918  		err := configStore.ReadObject(fileName, &answer)
  2919  		if err != nil {
  2920  			return &answer, err
  2921  		}
  2922  	} else if defaultEnvironment != nil {
  2923  		answer = *defaultEnvironment
  2924  	}
  2925  	err = callback(&answer)
  2926  	if err != nil {
  2927  		return &answer, err
  2928  	}
  2929  	answer.Name = name
  2930  	if answer.APIVersion == "" {
  2931  		answer.APIVersion = jenkinsio.GroupAndVersion
  2932  	}
  2933  	if answer.Kind == "" {
  2934  		answer.Kind = "Environment"
  2935  	}
  2936  	err = configStore.WriteObject(fileName, &answer)
  2937  	if err != nil {
  2938  		return &answer, errors.Wrapf(err, "Could not save file %s", fileName)
  2939  	}
  2940  	return &answer, nil
  2941  }
  2942  
  2943  func isOpenShiftProvider(provider string) bool {
  2944  	switch provider {
  2945  	case cloud.OPENSHIFT:
  2946  		return true
  2947  	default:
  2948  		return false
  2949  	}
  2950  }
  2951  
  2952  func (options *InstallOptions) enableOpenShiftSCC(ns string) error {
  2953  	log.Logger().Infof("Enabling anyuid for the Jenkins service account in namespace %s", ns)
  2954  	err := options.RunCommand("oc", "adm", "policy", "add-scc-to-user", "anyuid", "system:serviceaccount:"+ns+":jenkins")
  2955  	if err != nil {
  2956  		return err
  2957  	}
  2958  	err = options.RunCommand("oc", "adm", "policy", "add-scc-to-user", "hostaccess", "system:serviceaccount:"+ns+":jenkins")
  2959  	if err != nil {
  2960  		return err
  2961  	}
  2962  	err = options.RunCommand("oc", "adm", "policy", "add-scc-to-user", "privileged", "system:serviceaccount:"+ns+":jenkins")
  2963  	if err != nil {
  2964  		return err
  2965  	}
  2966  	// try fix monocular
  2967  	return options.RunCommand("oc", "adm", "policy", "add-scc-to-user", "anyuid", "system:serviceaccount:"+ns+":default")
  2968  }
  2969  
  2970  func (options *InstallOptions) enableOpenShiftRegistryPermissions(ns string, dockerRegistry string) (string, error) {
  2971  	log.Logger().Infof("Enabling permissions for OpenShift registry in namespace %s", ns)
  2972  	// Open the registry so any authenticated user can pull images from the jx namespace
  2973  	err := options.RunCommand("oc", "adm", "policy", "add-role-to-group", "system:image-puller", "system:authenticated", "-n", ns)
  2974  	if err != nil {
  2975  		return "", err
  2976  	}
  2977  	err = options.EnsureServiceAccount(ns, "jenkins-x-registry")
  2978  	if err != nil {
  2979  		return "", err
  2980  	}
  2981  	err = options.RunCommand("oc", "adm", "policy", "add-cluster-role-to-user", "registry-admin", "system:serviceaccount:"+ns+":jenkins-x-registry")
  2982  	if err != nil {
  2983  		return "", err
  2984  	}
  2985  	registryToken, err := options.GetCommandOutput("", "oc", "serviceaccounts", "get-token", "jenkins-x-registry", "-n", ns)
  2986  	if err != nil {
  2987  		return "", err
  2988  	}
  2989  	return `{"auths": {"` + dockerRegistry + `": {"auth": "` + base64.StdEncoding.EncodeToString([]byte("serviceaccount:"+registryToken)) + `"}}}`, nil
  2990  }
  2991  
  2992  func (options *InstallOptions) logAdminPassword() {
  2993  	astrix := `
  2994  
  2995  	********************************************************
  2996  
  2997  	     NOTE: %s
  2998  
  2999  	********************************************************
  3000  
  3001  	`
  3002  	if options.Flags.Vault {
  3003  		log.Logger().Infof(astrix+"\n", fmt.Sprintf("Your admin password is in vault: %s", util.ColorInfo("eval `jx get vault-config` && vault kv get secret/admin/jenkins")))
  3004  	} else {
  3005  		log.Logger().Infof(astrix+"\n", fmt.Sprintf("Your admin password is: %s", util.ColorInfo(options.AdminSecretsService.Flags.DefaultAdminPassword)))
  3006  	}
  3007  }
  3008  
  3009  func (options *InstallOptions) logNameServers() {
  3010  	output := `
  3011  	********************************************************
  3012  
  3013  	    External DNS: %s
  3014  
  3015  	********************************************************
  3016  	`
  3017  	if options.InitOptions.Flags.ExternalDNS {
  3018  		log.Logger().Infof(output, fmt.Sprintf("Please delegate %s via \n\tyour registrar onto the following name servers: \n\t\t%s",
  3019  			util.ColorInfo(options.Flags.Domain), util.ColorInfo(strings.Join(options.CommonOptions.NameServers, "\n\t\t"))))
  3020  	}
  3021  }
  3022  
  3023  // LoadVersionFromCloudEnvironmentsDir lets load the jenkins-x-platform version
  3024  func LoadVersionFromCloudEnvironmentsDir(wrkDir string, configStore configio.ConfigStore) (string, error) {
  3025  	version, err := versionstream.LoadStableVersionNumber(wrkDir, versionstream.KindChart, platform.JenkinsXPlatformChart)
  3026  	if err != nil {
  3027  		return version, errors.Wrapf(err, "failed to load version of chart %s in dir %s", platform.JenkinsXPlatformChart, wrkDir)
  3028  	}
  3029  	return version, nil
  3030  }
  3031  
  3032  // clones the jenkins-x cloud-environments repo to a local working dir
  3033  func (options *InstallOptions) CloneJXCloudEnvironmentsRepo() (string, error) {
  3034  	surveyOpts := survey.WithStdio(options.In, options.Out, options.Err)
  3035  	configDir, err := util.ConfigDir()
  3036  	if err != nil {
  3037  		return "", fmt.Errorf("error determining config dir %v", err)
  3038  	}
  3039  	wrkDir := filepath.Join(configDir, "cloud-environments")
  3040  
  3041  	log.Logger().Debugf("Current configuration dir: %s", configDir)
  3042  	log.Logger().Debugf("options.Flags.CloudEnvRepository: %s", options.Flags.CloudEnvRepository)
  3043  	log.Logger().Debugf("options.Flags.LocalCloudEnvironment: %t", options.Flags.LocalCloudEnvironment)
  3044  
  3045  	if options.Flags.LocalCloudEnvironment {
  3046  		currentDir, err := os.Getwd()
  3047  		if err != nil {
  3048  			return wrkDir, fmt.Errorf("error getting current working directory %v", err)
  3049  		}
  3050  		log.Logger().Infof("Copying local dir %s to %s", currentDir, wrkDir)
  3051  
  3052  		return wrkDir, util.CopyDir(currentDir, wrkDir, true)
  3053  	}
  3054  	if options.Flags.CloudEnvRepository == "" {
  3055  		options.Flags.CloudEnvRepository = opts.DefaultCloudEnvironmentsURL
  3056  	}
  3057  	log.Logger().Debugf("Cloning the Jenkins X cloud environments repo to %s", wrkDir)
  3058  	_, err = git.PlainClone(wrkDir, false, &git.CloneOptions{
  3059  		URL:           options.Flags.CloudEnvRepository,
  3060  		ReferenceName: "refs/heads/master",
  3061  		SingleBranch:  true,
  3062  		Progress:      options.Out,
  3063  	})
  3064  	if err != nil {
  3065  		if err == git.ErrRepositoryAlreadyExists {
  3066  			flag := false
  3067  			if options.BatchMode {
  3068  				flag = true
  3069  			} else if options.AdvancedMode {
  3070  				confirm := &survey.Confirm{
  3071  					Message: "A local Jenkins X cloud environments repository already exists, recreate with latest?",
  3072  					Default: true,
  3073  				}
  3074  				err := survey.AskOne(confirm, &flag, nil, surveyOpts)
  3075  				if err != nil {
  3076  					return wrkDir, err
  3077  				}
  3078  			} else {
  3079  				flag = true
  3080  				log.Logger().Infof(util.QuestionAnswer("A local Jenkins X cloud environments repository already exists, recreating with latest", util.YesNo(flag)))
  3081  			}
  3082  
  3083  			if flag {
  3084  				err := os.RemoveAll(wrkDir)
  3085  				if err != nil {
  3086  					return wrkDir, err
  3087  				}
  3088  
  3089  				return options.CloneJXCloudEnvironmentsRepo()
  3090  			}
  3091  		} else {
  3092  			return wrkDir, err
  3093  		}
  3094  	}
  3095  	return wrkDir, nil
  3096  }
  3097  
  3098  func (options *InstallOptions) waitForInstallToBeReady(ns string) error {
  3099  	client, err := options.KubeClient()
  3100  	if err != nil {
  3101  		return err
  3102  	}
  3103  
  3104  	log.Logger().Warnf("waiting for install to be ready, if this is the first time then it will take a while to download images")
  3105  
  3106  	return kube.WaitForAllDeploymentsToBeReady(client, ns, 30*time.Minute)
  3107  
  3108  }
  3109  
  3110  func (options *InstallOptions) saveChartmuseumAuthConfig() error {
  3111  
  3112  	authConfigSvc, err := options.ChartmuseumAuthConfigService()
  3113  	if err != nil {
  3114  		return err
  3115  	}
  3116  	config := authConfigSvc.Config()
  3117  
  3118  	var server *auth.AuthServer
  3119  	if options.ServerFlags.IsEmpty() {
  3120  		url := ""
  3121  		url, err = options.FindService(kube.ServiceChartMuseum)
  3122  		if err != nil {
  3123  			log.Logger().Warnf("No service called %s could be found so couldn't wire up the local auth file to talk to chart museum", kube.ServiceChartMuseum)
  3124  			return nil
  3125  		}
  3126  		server = config.GetOrCreateServer(url)
  3127  	} else {
  3128  		server, err = options.FindServer(config, &options.ServerFlags, "ChartMuseum server", "Try installing one via: jx create team", false)
  3129  		if err != nil {
  3130  			return err
  3131  		}
  3132  	}
  3133  
  3134  	user := &auth.UserAuth{
  3135  		Username: options.AdminSecretsService.Flags.DefaultAdminUsername,
  3136  		Password: options.AdminSecretsService.Flags.DefaultAdminPassword,
  3137  	}
  3138  
  3139  	server.Users = append(server.Users, user)
  3140  
  3141  	config.CurrentServer = server.URL
  3142  	return authConfigSvc.SaveConfig()
  3143  }
  3144  
  3145  func (options *InstallOptions) installAddon(name string) error {
  3146  	log.Logger().Infof("Installing addon %s", util.ColorInfo(name))
  3147  
  3148  	opts := &CreateAddonOptions{
  3149  		CreateOptions: createoptions.CreateOptions{
  3150  			CommonOptions: options.CommonOptions,
  3151  		},
  3152  		HelmUpdate: true,
  3153  	}
  3154  	if name == "gitea" {
  3155  		opts.ReleaseName = defaultGiteaReleaseName
  3156  		giteaOptions := &CreateAddonGiteaOptions{
  3157  			CreateAddonOptions: *opts,
  3158  			Chart:              kube.ChartGitea,
  3159  		}
  3160  		return giteaOptions.Run()
  3161  	}
  3162  	return opts.CreateAddon(name)
  3163  }
  3164  
  3165  func (options *InstallOptions) addGitServersToJenkinsConfig(helmConfig *config.HelmValuesConfig) error {
  3166  	gitAuthCfg, err := options.GitAuthConfigService()
  3167  	if err != nil {
  3168  		return errors.Wrap(err, "failed to create the git auth config service")
  3169  	}
  3170  	cfg := gitAuthCfg.Config()
  3171  	for _, server := range cfg.Servers {
  3172  		if server.Kind == "github" {
  3173  			u := server.URL
  3174  			if !gits.IsGitHubServerURL(u) {
  3175  				sc := config.JenkinsGithubServersValuesConfig{
  3176  					Name: server.Name,
  3177  					Url:  gits.GitHubEnterpriseApiEndpointURL(u),
  3178  				}
  3179  				helmConfig.Jenkins.Servers.GHE = append(helmConfig.Jenkins.Servers.GHE, sc)
  3180  			}
  3181  		}
  3182  	}
  3183  	return nil
  3184  }
  3185  
  3186  func (options *InstallOptions) ensureDefaultStorageClass(client kubernetes.Interface, name string, provisioner string, typeName string) error {
  3187  	storageClassInterface := client.StorageV1().StorageClasses()
  3188  	storageClasses, err := storageClassInterface.List(metav1.ListOptions{})
  3189  	if err != nil {
  3190  		return err
  3191  	}
  3192  	var foundSc *storagev1.StorageClass
  3193  	for idx, sc := range storageClasses.Items {
  3194  		ann := sc.Annotations
  3195  		if ann != nil && ann[kube.AnnotationIsDefaultStorageClass] == "true" {
  3196  			return nil
  3197  		}
  3198  		if sc.Name == name {
  3199  			foundSc = &storageClasses.Items[idx]
  3200  		}
  3201  	}
  3202  
  3203  	if foundSc != nil {
  3204  		// lets update the storageclass to be default
  3205  		if foundSc.Annotations == nil {
  3206  			foundSc.Annotations = map[string]string{}
  3207  		}
  3208  		foundSc.Annotations[kube.AnnotationIsDefaultStorageClass] = "true"
  3209  
  3210  		log.Logger().Infof("Updating storageclass %s to be the default", util.ColorInfo(name))
  3211  		_, err = storageClassInterface.Update(foundSc)
  3212  		return err
  3213  	}
  3214  
  3215  	// lets create a default storage class
  3216  	reclaimPolicy := core_v1.PersistentVolumeReclaimRetain
  3217  
  3218  	sc := &storagev1.StorageClass{
  3219  		ObjectMeta: metav1.ObjectMeta{
  3220  			Name: name,
  3221  			Annotations: map[string]string{
  3222  				kube.AnnotationIsDefaultStorageClass: "true",
  3223  			},
  3224  		},
  3225  		Provisioner: provisioner,
  3226  		Parameters: map[string]string{
  3227  			"type": typeName,
  3228  		},
  3229  		ReclaimPolicy: &reclaimPolicy,
  3230  		MountOptions:  []string{"debug"},
  3231  	}
  3232  	log.Logger().Infof("Creating default storageclass %s with provisioner %s", util.ColorInfo(name), util.ColorInfo(provisioner))
  3233  	_, err = storageClassInterface.Create(sc)
  3234  	return err
  3235  }
  3236  
  3237  func (options *InstallOptions) changeDefaultStorageClass(client kubernetes.Interface, defaultName string) error {
  3238  	storageClassInterface := client.StorageV1().StorageClasses()
  3239  	storageClasses, err := storageClassInterface.List(metav1.ListOptions{})
  3240  	if err != nil {
  3241  		return err
  3242  	}
  3243  	var foundSc *storagev1.StorageClass
  3244  	for idx, sc := range storageClasses.Items {
  3245  		ann := sc.Annotations
  3246  		foundSc = &storageClasses.Items[idx]
  3247  		if sc.Name == defaultName {
  3248  			if ann == nil {
  3249  				foundSc.Annotations = map[string]string{}
  3250  			}
  3251  			foundSc.Annotations[kube.AnnotationIsDefaultStorageClass] = "true"
  3252  			_, err = storageClassInterface.Update(foundSc)
  3253  		} else {
  3254  			if ann != nil && ann[kube.AnnotationIsDefaultStorageClass] == "true" {
  3255  				foundSc.Annotations[kube.AnnotationIsDefaultStorageClass] = "false"
  3256  				_, err = storageClassInterface.Update(foundSc)
  3257  			}
  3258  		}
  3259  	}
  3260  	return nil
  3261  }
  3262  
  3263  // returns the docker registry string for the given provider
  3264  func (options *InstallOptions) dockerRegistryValue() (string, error) {
  3265  	if options.Flags.DockerRegistry != "" {
  3266  		return options.Flags.DockerRegistry, nil
  3267  	}
  3268  	if options.Flags.Provider == cloud.AWS || options.Flags.Provider == cloud.EKS {
  3269  		return amazon.GetContainerRegistryHost()
  3270  	}
  3271  	if options.Flags.Provider == cloud.OPENSHIFT {
  3272  		return "docker-registry.default.svc:5000", nil
  3273  	}
  3274  	if options.Flags.Provider == cloud.GKE {
  3275  		if options.Flags.Kaniko {
  3276  			return "gcr.io", nil
  3277  		}
  3278  	}
  3279  
  3280  	log.Logger().Debugf("unable to determine the dockerRegistryValue - provider=%s, defaulting to in-cluster registry", options.Flags.Provider)
  3281  
  3282  	return "", nil
  3283  }
  3284  
  3285  func (options *InstallOptions) saveAsConfigMap(name string, config interface{}) (*core_v1.ConfigMap, error) {
  3286  	return options.ModifyConfigMap(name, func(cm *core_v1.ConfigMap) error {
  3287  		data := util.ToStringMapStringFromStruct(config)
  3288  		cm.Data = data
  3289  		return nil
  3290  	})
  3291  }
  3292  
  3293  func (options *InstallOptions) configureTeamSettings() error {
  3294  	initOpts := &options.InitOptions
  3295  	callback := func(env *v1.Environment) error {
  3296  		if env.Spec.TeamSettings.KubeProvider == "" {
  3297  			env.Spec.TeamSettings.KubeProvider = options.Flags.Provider
  3298  			log.Logger().Debugf("Storing the kubernetes provider %s in the TeamSettings", env.Spec.TeamSettings.KubeProvider)
  3299  		}
  3300  
  3301  		if initOpts.Flags.Helm3 {
  3302  			env.Spec.TeamSettings.HelmTemplate = false
  3303  			env.Spec.TeamSettings.HelmBinary = "helm3"
  3304  			log.Logger().Debugf("Enabling helm3 / non template mode in the TeamSettings")
  3305  		} else if initOpts.Flags.NoTiller {
  3306  			env.Spec.TeamSettings.HelmTemplate = true
  3307  			log.Logger().Debugf("Enabling helm template mode in the TeamSettings")
  3308  		}
  3309  
  3310  		if options.Flags.DockerRegistryOrg != "" {
  3311  			env.Spec.TeamSettings.DockerRegistryOrg = options.Flags.DockerRegistryOrg
  3312  			log.Logger().Infof("Setting the docker registry organisation to %s in the TeamSettings", env.Spec.TeamSettings.DockerRegistryOrg)
  3313  		}
  3314  
  3315  		if options.Flags.VersionsRepository != "" {
  3316  			env.Spec.TeamSettings.VersionStreamURL = options.Flags.VersionsRepository
  3317  		}
  3318  
  3319  		if options.Flags.VersionsGitRef != "" {
  3320  			env.Spec.TeamSettings.VersionStreamRef = options.Flags.VersionsGitRef
  3321  		}
  3322  		return nil
  3323  	}
  3324  	err := options.ModifyDevEnvironment(callback)
  3325  	if err != nil {
  3326  		return errors.Wrap(err, "updating the team setttings in the dev environment")
  3327  	}
  3328  	return nil
  3329  }
  3330  
  3331  // setValuesFileValue lazily creates the values.yaml file possibly in a new directory and ensures there is the key in the values with the given value
  3332  func (options *InstallOptions) setValuesFileValue(fileName string, key string, value interface{}) error {
  3333  	dir, _ := filepath.Split(fileName)
  3334  	err := os.MkdirAll(dir, util.DefaultWritePermissions)
  3335  	if err != nil {
  3336  		return err
  3337  	}
  3338  	answerMap := map[string]interface{}{}
  3339  
  3340  	// lets load any previous values if they exist
  3341  	exists, err := util.FileExists(fileName)
  3342  	if err != nil {
  3343  		return err
  3344  	}
  3345  	if exists {
  3346  		answerMap, err = helm.LoadValuesFile(fileName)
  3347  		if err != nil {
  3348  			return err
  3349  		}
  3350  	}
  3351  	answerMap[key] = value
  3352  	answer := chartutil.Values(answerMap)
  3353  	text, err := answer.YAML()
  3354  	if err != nil {
  3355  		return errors.Wrap(err, "Failed to marshal the updated values YAML files back to YAML")
  3356  	}
  3357  	err = ioutil.WriteFile(fileName, []byte(text), util.DefaultWritePermissions)
  3358  	if err != nil {
  3359  		return errors.Wrapf(err, "Failed to save updated helm values YAML file %s", fileName)
  3360  	}
  3361  	return nil
  3362  }
  3363  
  3364  // validateClusterName checks for compliance of a user supplied
  3365  // cluster name against GKE's rules for these names.
  3366  func validateClusterName(clustername string) error {
  3367  	// Check for length greater than 27.
  3368  	if len(clustername) > maxGKEClusterNameLength {
  3369  		err := fmt.Errorf("cluster name %s is greater than the maximum %d characters", clustername, maxGKEClusterNameLength)
  3370  		return err
  3371  	}
  3372  	// Now we need only make sure that clustername is limited to
  3373  	// lowercase alphanumerics and dashes.
  3374  	if util.DisallowedLabelCharacters.MatchString(clustername) {
  3375  		err := fmt.Errorf("cluster name %v contains invalid characters. Permitted are lowercase alphanumerics and `-`", clustername)
  3376  		return err
  3377  	}
  3378  	return nil
  3379  }
  3380  
  3381  func installConfigKey(key string) string {
  3382  	return fmt.Sprintf("install.%s", key)
  3383  }