github.com/jenkins-x/jx/v2@v2.1.155/pkg/cmd/step/env/step_env_apply.go (about)

     1  package env
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"path/filepath"
     8  
     9  	"github.com/jenkins-x/jx/v2/pkg/platform"
    10  
    11  	"github.com/jenkins-x/jx/v2/pkg/cmd/opts/step"
    12  
    13  	"github.com/jenkins-x/jx/v2/pkg/cmd/helper"
    14  	"github.com/jenkins-x/jx/v2/pkg/cmd/namespace"
    15  
    16  	"github.com/ghodss/yaml"
    17  	v1 "github.com/jenkins-x/jx-api/pkg/apis/jenkins.io/v1"
    18  	"github.com/jenkins-x/jx-logging/pkg/log"
    19  	"github.com/jenkins-x/jx/v2/pkg/cmd/opts"
    20  	helm_cmd "github.com/jenkins-x/jx/v2/pkg/cmd/step/helm"
    21  	"github.com/jenkins-x/jx/v2/pkg/cmd/templates"
    22  	"github.com/jenkins-x/jx/v2/pkg/helm"
    23  	"github.com/jenkins-x/jx/v2/pkg/kube"
    24  	"github.com/jenkins-x/jx/v2/pkg/util"
    25  	"github.com/pkg/errors"
    26  	"github.com/spf13/cobra"
    27  )
    28  
    29  // StepEnvApplyOptions contains the command line flags
    30  type StepEnvApplyOptions struct {
    31  	StepEnvOptions
    32  
    33  	Namespace          string
    34  	Dir                string
    35  	ReleaseName        string
    36  	Wait               bool
    37  	Force              bool
    38  	DisableHelmVersion bool
    39  	ChangeNs           bool
    40  	Vault              bool
    41  }
    42  
    43  var (
    44  	// stepEnvApplyLong long description
    45  	stepEnvApplyLong = templates.LongDesc(`
    46  		Applies the GitOps source code (by default in the current directory) to the Environment.
    47  
    48  		This command will lazily create an environment, setup Helm and build and apply any helm charts defined in the env/Chart.yaml
    49  `)
    50  
    51  	// StepEnvApplyExample example
    52  	stepEnvApplyExample = templates.Examples(`
    53  		# setup and/or update the helm charts for the environment
    54  		jx step env apply --namespace jx-staging
    55  `)
    56  )
    57  
    58  // NewCmdStepEnvApply registers the command
    59  func NewCmdStepEnvApply(commonOpts *opts.CommonOptions) *cobra.Command {
    60  	options := StepEnvApplyOptions{
    61  		StepEnvOptions: StepEnvOptions{
    62  			StepOptions: step.StepOptions{
    63  				CommonOptions: commonOpts,
    64  			},
    65  		},
    66  	}
    67  	cmd := &cobra.Command{
    68  		Use:     "apply",
    69  		Short:   "Applies the GitOps source code to an environment",
    70  		Aliases: []string{""},
    71  		Long:    stepEnvApplyLong,
    72  		Example: stepEnvApplyExample,
    73  		Run: func(cmd *cobra.Command, args []string) {
    74  			options.Cmd = cmd
    75  			options.Args = args
    76  			err := options.Run()
    77  			helper.CheckErr(err)
    78  		},
    79  	}
    80  	cmd.Flags().StringVarP(&options.Namespace, "namespace", "n", "", "The Kubernetes namespace to apply the helm charts to")
    81  	cmd.Flags().StringVarP(&options.ReleaseName, "name", "r", "", "The name of the release")
    82  	cmd.Flags().StringVarP(&options.Dir, "dir", "d", "", "The directory to look for the environment chart")
    83  	cmd.Flags().BoolVarP(&options.ChangeNs, "change-namespace", "", false, "Set the given namespace as the current namespace in Kubernetes configuration")
    84  	cmd.Flags().BoolVarP(&options.Vault, "vault", "", false, "Environment secrets are stored in vault")
    85  
    86  	// step helm apply flags
    87  	cmd.Flags().BoolVarP(&options.Wait, "wait", "", true, "Wait for Kubernetes readiness probe to confirm deployment")
    88  	cmd.Flags().BoolVarP(&options.Force, "force", "f", true, "Whether to to pass '--force' to helm to help deal with upgrading if a previous promote failed")
    89  	cmd.Flags().BoolVar(&options.DisableHelmVersion, "no-helm-version", false, "Don't set Chart version before applying")
    90  
    91  	return cmd
    92  }
    93  
    94  // Run performs the comamand
    95  func (o *StepEnvApplyOptions) Run() error {
    96  	var err error
    97  	dir := o.Dir
    98  	if dir == "" {
    99  		dir, err = os.Getwd()
   100  		if err != nil {
   101  			return errors.Wrap(err, "getting the working directory")
   102  		}
   103  	}
   104  
   105  	ns, err := o.GetDeployNamespace(o.Namespace)
   106  	if err != nil {
   107  		return err
   108  	}
   109  	kubeClient, err := o.KubeClient()
   110  	if err != nil {
   111  		return err
   112  	}
   113  	o.SetDevNamespace(ns)
   114  
   115  	apisClient, err := o.ApiExtensionsClient()
   116  	if err != nil {
   117  		return errors.Wrap(err, "creating the API extensions client")
   118  	}
   119  	err = kube.RegisterAllCRDs(apisClient)
   120  	if err != nil {
   121  		return errors.Wrap(err, "registering all CRDs")
   122  	}
   123  
   124  	// now lets find the dev environment to know what kind of helmer to use
   125  	chartFile := filepath.Join(dir, helm.ChartFileName)
   126  	exists, err := util.FileExists(chartFile)
   127  	if err != nil {
   128  		return errors.Wrap(err, "checking if file exits")
   129  	}
   130  	if !exists {
   131  		envDir := filepath.Join(dir, "env")
   132  		chartFile2 := filepath.Join(envDir, helm.ChartFileName)
   133  		exists2, err := util.FileExists(chartFile2)
   134  		if exists2 && err == nil {
   135  			dir = envDir
   136  		} else {
   137  			return fmt.Errorf("there is no Environment chart file at %s or %s\nplease try specify the directory containing the Chart.yaml or env/Chart.yaml with --dir", chartFile, chartFile2)
   138  		}
   139  	}
   140  	devEnvFile := filepath.Join(dir, "templates", "dev-env.yaml")
   141  	exists, err = util.FileExists(chartFile)
   142  	if exists && err == nil {
   143  		// lets setup the Helmer based on the current settings
   144  		log.Logger().Infof("Loading the latest Dev Environment configuration from %s", devEnvFile)
   145  
   146  		env := v1.Environment{}
   147  		data, err := ioutil.ReadFile(devEnvFile)
   148  		if err != nil {
   149  			return errors.Wrapf(err, "loading configuration file %s", devEnvFile)
   150  		}
   151  		err = yaml.Unmarshal(data, &env)
   152  		if err != nil {
   153  			return errors.Wrapf(err, "unmarshalling YAML file %s", devEnvFile)
   154  		}
   155  
   156  		teamSettings := &env.Spec.TeamSettings
   157  
   158  		// disable the modify of the Dev Environment lazily...
   159  		o.ModifyDevEnvironmentFn = func(callback func(env *v1.Environment) error) error {
   160  			err = callback(&env)
   161  			return err
   162  		}
   163  
   164  		helm := o.NewHelm(false, teamSettings.HelmBinary, teamSettings.NoTiller, teamSettings.HelmTemplate)
   165  		o.SetHelm(helm)
   166  
   167  		// ensure there's a development namespace setup
   168  		err = kube.EnsureDevNamespaceCreatedWithoutEnvironment(kubeClient, ns)
   169  		if err != nil {
   170  			return errors.Wrapf(err, "creating namespace %s for development environment", ns)
   171  		}
   172  
   173  		if o.ReleaseName == "" {
   174  			o.ReleaseName = platform.JenkinsXPlatformRelease
   175  		}
   176  	} else {
   177  		// ensure there's a development namespace setup
   178  		err = kube.EnsureNamespaceCreated(kubeClient, ns, nil, nil)
   179  		if err != nil {
   180  			return errors.Wrapf(err, "creating  namespace %s for environment", ns)
   181  		}
   182  	}
   183  
   184  	// Change the current namesapce before applying the environment step
   185  	if o.ChangeNs {
   186  		_, currentNs, err := o.KubeClientAndNamespace()
   187  		if err != nil {
   188  			return errors.Wrap(err, "creating the kube client")
   189  		}
   190  		if currentNs != ns {
   191  			nsOptions := &namespace.NamespaceOptions{
   192  				CommonOptions: o.CommonOptions,
   193  			}
   194  			nsOptions.BatchMode = true
   195  			nsOptions.Args = []string{ns}
   196  			err := nsOptions.Run()
   197  			if err != nil {
   198  				log.Logger().Warnf("Failed to set context to namespace %s: %s", ns, err)
   199  			}
   200  			o.ResetClientsAndNamespaces()
   201  		}
   202  	}
   203  
   204  	stepHelmBuild := &helm_cmd.StepHelmBuildOptions{
   205  		StepHelmOptions: helm_cmd.StepHelmOptions{
   206  			StepOptions: step.StepOptions{
   207  				CommonOptions: o.CommonOptions,
   208  			},
   209  			Dir: dir,
   210  		},
   211  	}
   212  	err = stepHelmBuild.Run()
   213  	if err != nil {
   214  		return errors.Wrapf(err, "building helm chart in dir %s", dir)
   215  	}
   216  
   217  	stepApply := &helm_cmd.StepHelmApplyOptions{
   218  		StepHelmOptions:    stepHelmBuild.StepHelmOptions,
   219  		Namespace:          ns,
   220  		ReleaseName:        o.ReleaseName,
   221  		Wait:               o.Wait,
   222  		DisableHelmVersion: o.DisableHelmVersion,
   223  		Force:              o.Force,
   224  		Vault:              o.Vault,
   225  	}
   226  	err = stepApply.Run()
   227  	if err != nil {
   228  		return errors.Wrapf(err, "applying the helm chart in dir %s", dir)
   229  	}
   230  	log.Logger().Infof("Environment applied in namespace %s", util.ColorInfo(ns))
   231  	return nil
   232  }