github.com/jenkins-x/jx/v2@v2.1.155/pkg/cmd/boot/boot.go (about) 1 package boot 2 3 import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 8 "github.com/google/uuid" 9 10 "github.com/jenkins-x/jx/v2/pkg/versionstream" 11 12 "github.com/jenkins-x/jx/v2/pkg/boot" 13 v1 "k8s.io/api/core/v1" 14 15 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 16 17 "github.com/jenkins-x/jx-logging/pkg/log" 18 "github.com/jenkins-x/jx/v2/pkg/cmd/helper" 19 "github.com/jenkins-x/jx/v2/pkg/cmd/namespace" 20 "github.com/jenkins-x/jx/v2/pkg/cmd/step/create" 21 "github.com/jenkins-x/jx/v2/pkg/config" 22 "github.com/jenkins-x/jx/v2/pkg/gits" 23 "github.com/jenkins-x/jx/v2/pkg/util" 24 "github.com/pkg/errors" 25 26 "github.com/spf13/cobra" 27 28 "github.com/jenkins-x/jx/v2/pkg/cmd/opts" 29 "github.com/jenkins-x/jx/v2/pkg/cmd/templates" 30 ) 31 32 // BootOptions options for the command 33 type BootOptions struct { 34 *opts.CommonOptions 35 36 Dir string 37 GitURL string 38 GitRef string 39 StartStep string 40 EndStep string 41 HelmLogLevel string 42 43 // The bootstrap URL for the version stream. Once we have a jx-requirements.yml files, we read that 44 VersionStreamURL string 45 // The bootstrap ref for the version stream. Once we have a jx-requirements.yml, we read that 46 VersionStreamRef string 47 48 // RequirementsFile provided by the user to override the default requirements file from repository 49 RequirementsFile string 50 51 AttemptRestore bool 52 53 // UpgradeGit if we want to automatically upgrade this boot clone if there have been changes since the current clone 54 NoUpgradeGit bool 55 } 56 57 var ( 58 bootLong = templates.LongDesc(` 59 Boots up Jenkins X in a Kubernetes cluster using GitOps and a Jenkins X Pipeline 60 61 For more documentation see: [https://jenkins-x.io/docs/getting-started/setup/boot/](https://jenkins-x.io/docs/getting-started/setup/boot/) 62 63 `) 64 65 bootExample = templates.Examples(` 66 # create a kubernetes cluster via Terraform or via jx 67 jx create cluster gke --skip-installation 68 69 # now lets boot up Jenkins X installing/upgrading whatever is needed 70 jx boot 71 72 # if we have already booted and just want to apply some environment changes without 73 # re-applying ingress and so forth we can start at the environment step: 74 jx boot --start-step install-env 75 `) 76 ) 77 78 // NewCmdBoot creates the command 79 func NewCmdBoot(commonOpts *opts.CommonOptions) *cobra.Command { 80 options := &BootOptions{ 81 CommonOptions: commonOpts, 82 } 83 cmd := &cobra.Command{ 84 Use: "boot", 85 Aliases: []string{"bootstrap"}, 86 Short: "Boots up Jenkins X in a Kubernetes cluster using GitOps and a Jenkins X Pipeline", 87 Long: bootLong, 88 Example: bootExample, 89 Run: func(cmd *cobra.Command, args []string) { 90 options.Cmd = cmd 91 options.Args = args 92 err := options.Run() 93 helper.CheckErr(err) 94 }, 95 } 96 97 cmd.Flags().StringVarP(&options.Dir, "dir", "d", ".", "the directory to look for the Jenkins X Pipeline, requirements and charts") 98 cmd.Flags().StringVarP(&options.GitURL, "git-url", "u", "", "override the Git clone URL for the JX Boot source to start from, ignoring the versions stream. Normally specified with git-ref as well") 99 cmd.Flags().StringVarP(&options.GitRef, "git-ref", "", "", "override the Git ref for the JX Boot source to start from, ignoring the versions stream. Normally specified with git-url as well") 100 cmd.Flags().StringVarP(&options.VersionStreamURL, "versions-repo", "", config.DefaultVersionsURL, "the bootstrap URL for the versions repo. Once the boot config is cloned, the repo will be then read from the jx-requirements.yml") 101 cmd.Flags().StringVarP(&options.VersionStreamRef, "versions-ref", "", config.DefaultVersionsRef, "the bootstrap ref for the versions repo. Once the boot config is cloned, the repo will be then read from the jx-requirements.yml") 102 cmd.Flags().StringVarP(&options.StartStep, "start-step", "s", "", "the step in the pipeline to start from") 103 cmd.Flags().StringVarP(&options.EndStep, "end-step", "e", "", "the step in the pipeline to end at") 104 cmd.Flags().StringVarP(&options.HelmLogLevel, "helm-log", "v", "", "sets the helm logging level from 0 to 9. Passed into the helm CLI via the '-v' argument. Useful to diagnose helm related issues") 105 cmd.Flags().StringVarP(&options.RequirementsFile, "requirements", "r", "", "WARNING: this should only be used for the initial boot of a cluster: requirements file which will overwrite the default requirements file") 106 cmd.Flags().BoolVarP(&options.AttemptRestore, "attempt-restore", "a", false, "attempt to boot from an existing dev environment repository") 107 cmd.Flags().BoolVarP(&options.NoUpgradeGit, "no-update-git", "", false, "disables any attempt to update the local git clone if its old") 108 109 return cmd 110 } 111 112 // Run runs this command 113 func (o *BootOptions) Run() error { 114 err := o.verifyClusterConnection() 115 if err != nil { 116 return err 117 } 118 119 o.overrideSteps() 120 121 if o.AttemptRestore { 122 err := o.restoreFromDevEnvRepo() 123 if err != nil { 124 return errors.Wrapf(err, "unable tp restore dev environment repo") 125 } 126 } 127 128 gitURL, gitRef := o.determineGitURLAndRef() 129 if gitURL == "" { 130 return util.MissingOption("git-url") 131 } 132 133 isBootClone, err := existingBootClone(o.Dir) 134 if err != nil { 135 return errors.Wrapf(err, "failed to check if this is an existing boot clone") 136 } 137 138 isGitRepo := o.isGitRepo(o.Dir) 139 if isGitRepo && !isBootClone { 140 return errors.Errorf("trying to execute 'jx boot' from a non requirements repo") 141 } 142 143 if !isBootClone { 144 gitInfo, err := gits.ParseGitURL(gitURL) 145 if err != nil { 146 return errors.Wrapf(err, "failed to parse git URL %s", gitURL) 147 } 148 dir := filepath.Join(o.Dir, gitInfo.Name) 149 cloneDir, err := o.createBootClone(gitURL, gitRef, dir) 150 if err != nil { 151 return errors.Wrapf(err, "unable to clone %s", gitURL) 152 } 153 o.Dir = cloneDir 154 } 155 156 requirements, requirementsFile, err := config.LoadRequirementsConfig(o.Dir, config.DefaultFailOnValidationError) 157 if err != nil { 158 return errors.Wrapf(err, "unable to load %s", config.RequirementsConfigFileName) 159 } 160 161 err = o.ConfigureCommonOptions(requirements) 162 if err != nil { 163 return err 164 } 165 166 // lets report errors parsing this file after the check we are outside of a git clone 167 o.defaultVersionStream(requirements) 168 169 resolver, err := o.CreateVersionResolver(requirements.VersionStream.URL, requirements.VersionStream.Ref) 170 if err != nil { 171 return errors.Wrapf(err, "there was a problem creating a version resolver from versions stream repository %s and ref %s", requirements.VersionStream.URL, requirements.VersionStream.Ref) 172 } 173 174 // lets avoid trying to reset the current git clone to master if using NoUpgradeGit 175 if o.GitRef == "" && !o.NoUpgradeGit { 176 gitRef, err = o.determineGitRef(resolver, requirements, gitURL) 177 if err != nil { 178 return errors.Wrapf(err, "failed to determine git ref") 179 } 180 } 181 182 projectConfig, pipelineFile, err := config.LoadProjectConfig(o.Dir) 183 if err != nil { 184 return err 185 } 186 187 // check if the user has passed in a requirements file to a cluster which has already been provisioned. 188 if o.RequirementsFile != "" { 189 if err := o.checkIfProvidedRequirementsArePossiblyStale(); err != nil { 190 return errors.Wrapf(err, "loading from provided requirements file") 191 } 192 } 193 194 if err := o.overrideRequirements(gitURL); err != nil { 195 return errors.Wrap(err, "overwriting the default requirements") 196 } 197 198 // only update boot if the a GitRef has not been supplied 199 if o.GitRef == "" && !o.NoUpgradeGit { 200 err = o.updateBootCloneIfOutOfDate(gitRef) 201 if err != nil { 202 return err 203 } 204 } 205 206 err = o.verifyRequirements(requirements, requirementsFile) 207 if err != nil { 208 return err 209 } 210 211 log.Logger().Infof("Booting Jenkins X") 212 213 // now lets really boot 214 _, so := create.NewCmdStepCreateTaskAndOption(o.CommonOptions) 215 so.CloneDir = o.Dir 216 so.CloneDir = o.Dir 217 so.InterpretMode = true 218 so.NoReleasePrepare = true 219 so.StartStep = o.StartStep 220 so.EndStep = o.EndStep 221 222 so.AdditionalEnvVars = map[string]string{ 223 "JX_NO_TILLER": "true", 224 boot.ConfigRepoURLEnvVarName: gitURL, 225 boot.ConfigBaseRefEnvVarName: gitRef, 226 boot.VersionsRepoURLEnvVarName: requirements.VersionStream.URL, 227 boot.VersionsRepoBaseRefEnvVarName: requirements.VersionStream.Ref, 228 } 229 if o.NoUpgradeGit { 230 so.AdditionalEnvVars[boot.DisablePushUpdatesToDevEnvironment] = "true" 231 } 232 if requirements.Cluster.HelmMajorVersion == "3" { 233 so.AdditionalEnvVars["JX_HELM3"] = "true" 234 } else { 235 so.AdditionalEnvVars["JX_NO_TILLER"] = "true" 236 } 237 if o.HelmLogLevel != "" { 238 so.AdditionalEnvVars["JX_HELM_VERBOSE"] = o.HelmLogLevel 239 } 240 241 // Set the namespace in the pipeline 242 so.CommonOptions.SetDevNamespace(requirements.Cluster.Namespace) 243 // lets ensure the namespace is set in the jenkins-x.yml file 244 envVars := make([]v1.EnvVar, 0) 245 for _, e := range projectConfig.PipelineConfig.Pipelines.Release.Pipeline.Environment { 246 if e.Name == "DEPLOY_NAMESPACE" { 247 envVars = append(envVars, v1.EnvVar{ 248 Name: "DEPLOY_NAMESPACE", 249 Value: requirements.Cluster.Namespace, 250 }) 251 } else { 252 envVars = append(envVars, e) 253 } 254 } 255 projectConfig.PipelineConfig.Pipelines.Release.Pipeline.Environment = envVars 256 err = projectConfig.SaveConfig(pipelineFile) 257 if err != nil { 258 return errors.Wrapf(err, "setting namespace in jenkins-x.yml") 259 } 260 so.VersionResolver = resolver 261 262 if o.BatchMode { 263 so.AdditionalEnvVars["JX_BATCH_MODE"] = "true" 264 } 265 err = so.Run() 266 if err != nil { 267 return errors.Wrapf(err, "failed to interpret pipeline file %s", pipelineFile) 268 } 269 270 log.Logger().Debugf("Using additional vars: %+v", so.AdditionalEnvVars) 271 272 // lets switch kubernetes context to it so the user can use `jx` commands immediately 273 no := &namespace.NamespaceOptions{} 274 no.CommonOptions = o.CommonOptions 275 no.Args = []string{requirements.Cluster.Namespace} 276 return no.Run() 277 } 278 279 func (o *BootOptions) isGitRepo(dir string) bool { 280 _, _, err := gits.GetGitInfoFromDirectory(dir, o.Git()) 281 if err == nil { 282 return true 283 } 284 return false 285 } 286 287 func (o *BootOptions) determineGitURLAndRef() (string, string) { 288 gitURL, gitRef, err := gits.GetGitInfoFromDirectory(o.Dir, o.Git()) 289 if err != nil { 290 log.Logger().Info("Creating boot config with defaults, as not in an existing boot directory with a git repository.") 291 gitURL = config.DefaultBootRepository 292 gitRef = config.DefaultVersionsRef 293 } 294 295 if o.GitURL != "" { 296 log.Logger().Infof("GitURL provided, overriding the current value: %s with %s", util.ColorInfo(gitURL), util.ColorInfo(o.GitURL)) 297 gitURL = o.GitURL 298 } 299 300 if o.GitRef != "" { 301 log.Logger().Infof("GitRef provided, overriding the current value: %s with %s", util.ColorInfo(gitRef), util.ColorInfo(o.GitRef)) 302 gitRef = o.GitRef 303 } 304 return gitURL, gitRef 305 } 306 307 func (o *BootOptions) createBootClone(bootConfigGitURL string, bootConfigGitRef string, cloneDir string) (string, error) { 308 info := util.ColorInfo 309 log.Logger().Infof("No Jenkins X pipeline file %s or no jx boot requirements file %s found. You are not running this command from inside a "+ 310 "Jenkins X Boot git clone", info(config.ProjectConfigFileName), info(config.RequirementsConfigFileName)) 311 312 if !o.BatchMode { 313 log.Logger().Infof("To continue we will clone %s @ %s to %s", info(bootConfigGitURL), info(bootConfigGitRef), info(cloneDir)) 314 315 help := "A git clone of a Jenkins X Boot source repository is required for 'jx boot'" 316 message := "Do you want to clone the Jenkins X Boot Git repository?" 317 if answer, err := util.Confirm(message, true, help, o.GetIOFileHandles()); err != nil { 318 return "", errors.Wrapf(err, "unable to process user input") 319 } else if !answer { 320 return "", fmt.Errorf("please run this command again inside a git clone from a Jenkins X Boot repository") 321 } 322 } 323 324 bootCloneExists, err := util.DirExists(cloneDir) 325 if err != nil { 326 return "", errors.Wrapf(err, "unable to check whether directory %s exists", cloneDir) 327 } 328 if bootCloneExists { 329 return "", fmt.Errorf("cannot clone git repository to %s as the dir already exists", cloneDir) 330 } 331 332 log.Logger().Infof("Cloning %s @ %s to %s\n", info(bootConfigGitURL), info(bootConfigGitRef), info(cloneDir)) 333 334 err = os.MkdirAll(cloneDir, util.DefaultWritePermissions) 335 if err != nil { 336 return "", errors.Wrapf(err, "failed to create directory: %s", cloneDir) 337 } 338 339 err = o.Git().Clone(bootConfigGitURL, cloneDir) 340 if err != nil { 341 return "", errors.Wrapf(err, "failed to clone git URL %s to directory: %s", bootConfigGitURL, cloneDir) 342 } 343 344 commitish, err := gits.FindTagForVersion(cloneDir, bootConfigGitRef, o.Git()) 345 if err != nil { 346 log.Logger().Debugf(errors.Wrapf(err, "finding tag for %s", bootConfigGitRef).Error()) 347 } 348 349 if commitish != "" { 350 err = o.Git().Reset(cloneDir, commitish, true) 351 if err != nil { 352 return "", errors.Wrapf(err, "setting HEAD to %s", commitish) 353 } 354 } else { 355 log.Logger().Debugf("fetching branch %s", bootConfigGitRef) 356 err = o.Git().FetchBranch(cloneDir, "origin", bootConfigGitRef) 357 if err != nil { 358 return "", errors.Wrapf(err, "fetching branch %s", bootConfigGitRef) 359 } 360 361 branchName := uuid.New().String() 362 363 err = o.Git().CreateBranchFrom(cloneDir, branchName, bootConfigGitRef) 364 if err != nil { 365 return "", errors.Wrapf(err, "create branch %s from %s", branchName, bootConfigGitRef) 366 } 367 368 err = o.Git().Checkout(cloneDir, branchName) 369 if err != nil { 370 return "", errors.Wrapf(err, "checkout branch %s", branchName) 371 } 372 } 373 374 cloneDir, err = filepath.Abs(cloneDir) 375 if err != nil { 376 return "", errors.Wrapf(err, "unable to determine absolute path for %s", cloneDir) 377 } 378 return cloneDir, nil 379 } 380 381 func (o *BootOptions) restoreFromDevEnvRepo() error { 382 url := o.determineDevEnvironmentUrl() 383 if url != "" { 384 cloned, dir, err := o.cloneDevEnvironment(url) 385 if err != nil { 386 return err 387 } 388 if cloned { 389 err = os.Chdir(dir) 390 if err != nil { 391 return errors.Wrapf(err, "failed to change into new directory: %s", dir) 392 } 393 } else { 394 log.Logger().Infof("failed to clone dev environment booting from %s", o.GitURL) 395 } 396 } else { 397 log.Logger().Infof("cannot determine dev environment url booting from %s", o.GitURL) 398 } 399 return nil 400 } 401 402 func (o *BootOptions) determineDevEnvironmentUrl() string { 403 gitProvider := os.Getenv("JX_VALUE_GITPROVIDER") 404 gitOwner := os.Getenv(config.RequirementEnvGitOwner) 405 clusterName := os.Getenv(config.RequirementClusterName) 406 if gitProvider != "" && gitOwner != "" && clusterName != "" { 407 repo := fmt.Sprintf("environment-%s-dev", clusterName) 408 repoName := o.Git().RepoName(gitOwner, repo) 409 url := fmt.Sprintf("https://%s.com/%s", gitProvider, repoName) 410 log.Logger().Infof("dev environment url is %s", url) 411 return url 412 } 413 return "" 414 } 415 416 func (o *BootOptions) cloneDevEnvironment(gitURL string) (bool, string, error) { 417 log.Logger().Infof("dev environment url specified %t ", o.AttemptRestore) 418 gitInfo, err := gits.ParseGitURL(gitURL) 419 if err != nil { 420 return false, "", errors.Wrapf(err, "failed to parse git URL %s", gitURL) 421 } 422 423 repo := gitInfo.Name 424 cloneDir := filepath.Join(o.Dir, repo) 425 426 err = os.MkdirAll(cloneDir, util.DefaultWritePermissions) 427 if err != nil { 428 return false, "", errors.Wrapf(err, "failed to create directory: %s", cloneDir) 429 } 430 431 err = o.Git().Clone(gitURL, cloneDir) 432 if err != nil { 433 log.Logger().Infof("failed to clone git URL %s to directory: %s", gitURL, cloneDir) 434 rmErr := os.RemoveAll(cloneDir) 435 if rmErr != nil { 436 log.Logger().Warnf("Unable to remove dev env directory") 437 } 438 return false, "", nil 439 } 440 441 return true, cloneDir, nil 442 } 443 444 func (o *BootOptions) updateBootCloneIfOutOfDate(gitRef string) error { 445 // Get the tag corresponding to the git ref. 446 commitish, err := gits.FindTagForVersion(o.Dir, gitRef, o.Git()) 447 if err != nil { 448 log.Logger().Debugf(errors.Wrapf(err, "finding tag for %s", gitRef).Error()) 449 commitish = fmt.Sprintf("%s/%s", "origin", gitRef) 450 } 451 452 // Check if the current HEAD is an ancestor of the tag. 453 isAncestor, err := o.Git().IsAncestor(o.Dir, "HEAD", commitish) 454 if err != nil { 455 log.Logger().Debugf(errors.Wrapf(err, "couldn't determine whether HEAD is an ancestor of %s", commitish).Error()) 456 } 457 458 // Get the tag on the current boot clone HEAD, if any. 459 resolved, _, err := o.Git().Describe(o.Dir, true, "HEAD", "0", true) 460 if err != nil { 461 return errors.Wrap(err, "could not describe boot clone HEAD to find its tag with git describe HEAD --abbrev=0 --contains --always") 462 } 463 464 if resolved != commitish { 465 if isAncestor { 466 log.Logger().Infof("Local boot clone is out of date. It is based on %s, but the version stream is using %s. The clone will now be updated to %s.", 467 resolved, commitish, commitish) 468 log.Logger().Info("Stashing any changes made in local boot clone.") 469 470 err = o.Git().StashPush(o.Dir) 471 if err != nil { 472 return errors.WithStack(err) 473 } 474 err = o.Git().Reset(o.Dir, commitish, true) 475 if err != nil { 476 return errors.Wrapf(err, "Could not reset local boot clone to %s", commitish) 477 } 478 479 err = o.Git().StashPop(o.Dir) 480 if err != nil && !gits.IsNoStashEntriesError(err) { // Ignore no stashes as that's just because there was nothing to stash 481 return fmt.Errorf("Could not update local boot clone due to conflicts between local changes and %s.\n"+ 482 "To fix this, resolve the conflicts listed below manually, run 'git reset HEAD', and run 'jx boot' again.\n%s", 483 commitish, gits.GetSimpleIndentedStashPopErrorMessage(err)) 484 } 485 } else { 486 log.Logger().Infof("Current HEAD %s in %s is not an ancestor of %s, the boot config version from the version stream.\n"+ 487 "Proceeding with current HEAD.", resolved, o.Dir, commitish) 488 return nil 489 } 490 } 491 return nil 492 } 493 494 func existingBootClone(dir string) (bool, error) { 495 pipelineExists, err := util.FileExists(filepath.Join(dir, config.ProjectConfigFileName)) 496 if err != nil { 497 return false, err 498 } 499 requirementsExist, err := util.FileExists(filepath.Join(dir, config.RequirementsConfigFileName)) 500 if err != nil { 501 return false, err 502 } 503 return requirementsExist && pipelineExists, nil 504 } 505 506 func (o *BootOptions) checkIfProvidedRequirementsArePossiblyStale() error { 507 _, devEnv := o.GetDevEnv() 508 if devEnv != nil { 509 log.Logger().Warnf("It seems you're passing a requirements file to cluster which has already been provisioned.") 510 log.Logger().Warnf("We recommend you update the %s file at %s, using the updates provided within your local %s file.", 511 config.RequirementsConfigFileName, devEnv.Spec.Source, o.RequirementsFile) 512 return errors.New("attempting to boot cluster using a requirements file which is possibly stale") 513 } 514 return nil 515 } 516 517 func (o *BootOptions) overrideRequirements(defaultBootConfigURL string) error { 518 requirements, requirementsFile, err := config.LoadRequirementsConfig(o.Dir, config.DefaultFailOnValidationError) 519 if err != nil { 520 return errors.Wrapf(err, "loading requirements from dir %q", o.Dir) 521 } 522 523 // overwrite the default requirements with provided requirements 524 if o.RequirementsFile != "" { 525 providedRequirements, err := config.LoadRequirementsConfigFile(o.RequirementsFile, config.DefaultFailOnValidationError) 526 if err != nil { 527 return errors.Wrapf(err, "loading requirements from file %q", o.RequirementsFile) 528 } 529 *requirements = *providedRequirements 530 } 531 532 o.defaultVersionStream(requirements) 533 if requirements.BootConfigURL == "" { 534 requirements.BootConfigURL = defaultBootConfigURL 535 } 536 537 if err := requirements.SaveConfig(requirementsFile); err != nil { 538 return errors.Wrapf(err, "saving the requirements into file %q", requirementsFile) 539 } 540 541 return nil 542 } 543 544 func (o *BootOptions) determineGitRef(resolver *versionstream.VersionResolver, requirements *config.RequirementsConfig, gitURL string) (string, error) { 545 // If the GitRef is not overridden and is set to it's default value then look up the version number 546 log.Logger().Infof("Attempting to resolve version for boot config %s from %s", util.ColorInfo(gitURL), util.ColorInfo(requirements.VersionStream.URL)) 547 gitRef, err := resolver.ResolveGitVersion(gitURL) 548 if err != nil { 549 return "", errors.Wrapf(err, fmt.Sprintf("failed to resolve version for %s in version stream %s", 550 gitURL, requirements.VersionStream.URL)) 551 } 552 if gitRef == "" { 553 log.Logger().Infof("no version for %s found in version stream %s, defaulting to %s", 554 util.ColorInfo(gitURL), util.ColorInfo(requirements.VersionStream.URL), util.ColorInfo("master")) 555 gitRef = "master" 556 } 557 return gitRef, nil 558 } 559 560 func (o *BootOptions) defaultVersionStream(requirements *config.RequirementsConfig) { 561 if requirements.VersionStream.URL == "" && requirements.VersionStream.Ref == "" { 562 requirements.VersionStream.URL = o.VersionStreamURL 563 requirements.VersionStream.Ref = o.VersionStreamRef 564 } 565 // If we still don't have a complete version stream ref then we better set to a default 566 if requirements.VersionStream.URL == "" || requirements.VersionStream.Ref == "" { 567 log.Logger().Warnf("Incomplete version stream reference %s @ %s", requirements.VersionStream.URL, requirements.VersionStream.Ref) 568 o.VersionStreamRef = config.DefaultVersionsRef 569 o.VersionStreamURL = config.DefaultVersionsURL 570 requirements.VersionStream.URL = o.VersionStreamURL 571 requirements.VersionStream.Ref = o.VersionStreamRef 572 log.Logger().Infof("Setting version stream reference to default %s @ %s", requirements.VersionStream.URL, requirements.VersionStream.Ref) 573 } 574 } 575 576 func (o *BootOptions) verifyRequirements(requirements *config.RequirementsConfig, requirementsFile string) error { 577 provider := requirements.Cluster.Provider 578 if provider == "" { 579 return config.MissingRequirement("provider", requirementsFile) 580 } 581 if requirements.Cluster.Namespace == "" { 582 return config.MissingRequirement("namespace", requirementsFile) 583 } 584 return nil 585 } 586 587 func (o *BootOptions) verifyClusterConnection() error { 588 client, ns, err := o.KubeClientAndNamespace() 589 if err != nil { 590 return errors.Wrapf(err, "Unable to get current kube client/namespace You are not currently connected to a cluster, please connect to the cluster that you intend to %s\n"+ 591 "Alternatively create a new cluster using %s", util.ColorInfo("jx boot"), util.ColorInfo("jx create cluster")) 592 } 593 594 _, err = client.CoreV1().Pods(ns).List(metav1.ListOptions{}) 595 if err != nil { 596 return errors.Wrapf(err, "Unable to list pods. You are not currently connected to a cluster, please connect to the cluster that you intend to %s\n"+ 597 "Alternatively create a new cluster using %s", util.ColorInfo("jx boot"), util.ColorInfo("jx create cluster")) 598 } 599 600 return nil 601 } 602 603 func (o *BootOptions) overrideSteps() { 604 if o.StartStep == "" { 605 startStep := os.Getenv("JX_BOOT_START_STEP") 606 if startStep != "" { 607 log.Logger().Debugf("Overriding start-step with env var: '%s'", startStep) 608 o.StartStep = startStep 609 } 610 } 611 612 if o.EndStep == "" { 613 endStep := os.Getenv("JX_BOOT_END_STEP") 614 if endStep != "" { 615 log.Logger().Debugf("Overriding end-step with env var: '%s'", endStep) 616 o.EndStep = endStep 617 } 618 } 619 }