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

     1  package edit
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	v1 "github.com/jenkins-x/jx-api/pkg/apis/jenkins.io/v1"
     8  	"github.com/jenkins-x/jx-logging/pkg/log"
     9  	"github.com/olli-ai/jx/v2/pkg/cmd/helper"
    10  	"github.com/olli-ai/jx/v2/pkg/util"
    11  	"github.com/spf13/cobra"
    12  
    13  	"github.com/olli-ai/jx/v2/pkg/cmd/opts"
    14  	"github.com/olli-ai/jx/v2/pkg/cmd/templates"
    15  )
    16  
    17  var (
    18  	editDeployKindLong = templates.LongDesc(`
    19  		Edits the deploy kind to use for your project or team
    20  `)
    21  
    22  	editDeployKindExample = templates.Examples(`
    23  		# Edit the deploy kind for your current project and prompts you to pick one of the available kinds
    24  		jx edit deploy
    25  
    26          # to switch to use Knative Serve deployments
    27  		jx edit deploy knative
    28  
    29          # to switch to normal kubernetes deployments
    30  		jx edit deploy default
    31  
    32          # to switch to use canary deployments (requires flagger and its dependencies)
    33  		jx edit deploy --canary
    34  
    35          # to disable canary deployments and don't ask any more questions
    36  		jx edit deploy --canary=false -b
    37  
    38          # to disable canary deployments and confirm if you want to change the deployment kind and HPA
    39  		jx edit deploy --canary=false
    40  
    41  		# Edit the default deploy kind for your team and be prompted for answers
    42  		jx edit deploy --team
    43  
    44  		# Set the default for your team to use knative and canary but no HPA
    45  		jx edit deploy --team knative --canary=true --hpa=false
    46  	`)
    47  
    48  	deployKinds = []string{opts.DeployKindKnative, opts.DeployKindDefault}
    49  
    50  	knativeDeployKey = "knativeDeploy:"
    51  	deployCanaryKey  = "canary:"
    52  	deployHPAKey     = "hpa:"
    53  	enabledKey       = "  enabled:"
    54  )
    55  
    56  // EditDeployKindOptions the options for the create spring command
    57  type EditDeployKindOptions struct {
    58  	EditOptions
    59  
    60  	Kind          string
    61  	DeployOptions v1.DeployOptions
    62  	Dir           string
    63  	Team          bool
    64  }
    65  
    66  // NewCmdEditDeployKind creates a command object for the "create" command
    67  func NewCmdEditDeployKind(commonOpts *opts.CommonOptions) *cobra.Command {
    68  	cmd, _ := NewCmdEditDeployKindAndOption(commonOpts)
    69  	return cmd
    70  }
    71  
    72  // NewCmdEditDeployKindAndOption creates a command object for the "create" command
    73  func NewCmdEditDeployKindAndOption(commonOpts *opts.CommonOptions) (*cobra.Command, *EditDeployKindOptions) {
    74  	options := &EditDeployKindOptions{
    75  		EditOptions: EditOptions{
    76  			CommonOptions: commonOpts,
    77  		},
    78  	}
    79  
    80  	cmd := &cobra.Command{
    81  		Use:     "deploy",
    82  		Short:   "Edits the deploy kind to use for your project or team",
    83  		Long:    editDeployKindLong,
    84  		Example: editDeployKindExample,
    85  		Run: func(cmd *cobra.Command, args []string) {
    86  			options.Cmd = cmd
    87  			options.Args = args
    88  			err := options.Run()
    89  			helper.CheckErr(err)
    90  		},
    91  	}
    92  	cmd.Flags().BoolVarP(&options.Team, "team", "t", false, "Edits the team default")
    93  	cmd.Flags().StringVarP(&options.Kind, opts.OptionKind, "k", "", fmt.Sprintf("The kind to use which should be one of: %s", strings.Join(deployKinds, ", ")))
    94  	cmd.Flags().BoolVarP(&options.DeployOptions.Canary, opts.OptionCanary, "", false, "should we use canary rollouts (progressive delivery). e.g. using a Canary deployment via flagger. Requires the installation of flagger and istio/gloo in your cluster")
    95  	cmd.Flags().BoolVarP(&options.DeployOptions.HPA, opts.OptionHPA, "", false, "should we enable the Horizontal Pod Autoscaler.")
    96  	options.Cmd = cmd
    97  	return cmd, options
    98  }
    99  
   100  // Run implements the command
   101  func (o *EditDeployKindOptions) Run() error {
   102  	settings, err := o.TeamSettings()
   103  	if err != nil {
   104  		return err
   105  	}
   106  	if o.Team {
   107  		teamDeployOptions := settings.GetDeployOptions()
   108  		name, err := o.pickDeployKind(settings.DeployKind)
   109  		if err != nil {
   110  			return err
   111  		}
   112  		canary, err := o.pickProgressiveDelivery(teamDeployOptions.Canary)
   113  		if err != nil {
   114  			return err
   115  		}
   116  		hpa, err := o.pickHPA(teamDeployOptions.HPA)
   117  		if err != nil {
   118  			return err
   119  		}
   120  
   121  		callback := func(env *v1.Environment) error {
   122  			teamSettings := &env.Spec.TeamSettings
   123  			teamSettings.DeployKind = name
   124  
   125  			dopt := &v1.DeployOptions{}
   126  			if !canary && !hpa {
   127  				teamSettings.DeployOptions = nil
   128  			} else {
   129  				dopt = &v1.DeployOptions{Canary: canary, HPA: hpa}
   130  				teamSettings.DeployOptions = dopt
   131  			}
   132  
   133  			log.Logger().Infof("Setting the team deploy to kind: %s with canary: %s and HPA: %s",
   134  				util.ColorInfo(name), util.ColorInfo(toString(dopt.Canary)), util.ColorInfo(toString(dopt.HPA)))
   135  			return nil
   136  		}
   137  		return o.ModifyDevEnvironment(callback)
   138  	}
   139  
   140  	fn := func(text string) (string, error) {
   141  		defaultName, currentDeployOptions := o.FindDefaultDeployKindInValuesYaml(text)
   142  		name := ""
   143  
   144  		name, err := o.pickDeployKind(defaultName)
   145  		if err != nil {
   146  			return name, err
   147  		}
   148  		canary, err := o.pickProgressiveDelivery(currentDeployOptions.Canary)
   149  		if err != nil {
   150  			return name, err
   151  		}
   152  		hpa, err := o.pickHPA(currentDeployOptions.HPA)
   153  		if err != nil {
   154  			return name, err
   155  		}
   156  		return o.setDeployKindInValuesYaml(text, name, canary, hpa)
   157  	}
   158  	return o.ModifyHelmValuesFile(o.Dir, fn)
   159  }
   160  
   161  // FindDefaultDeployKindInValuesYaml finds the deployment values for the given values.yaml text
   162  func (o *EditDeployKindOptions) FindDefaultDeployKindInValuesYaml(yamlText string) (string, v1.DeployOptions) {
   163  	deployOptions := v1.DeployOptions{}
   164  	// lets try find the current setting
   165  	knativeFlag := ""
   166  	mainSection := ""
   167  	lines := strings.Split(yamlText, "\n")
   168  
   169  	for _, line := range lines {
   170  		if strings.HasPrefix(line, "#") {
   171  			continue
   172  		}
   173  		if !strings.HasPrefix(line, " ") && !strings.HasPrefix(line, "\t") {
   174  			idx := strings.Index(line, ":")
   175  			if idx > 0 {
   176  				mainSection = line[0 : idx+1]
   177  			}
   178  		}
   179  		if strings.HasPrefix(line, knativeDeployKey) {
   180  			knativeFlag = strings.TrimSpace(line[len(knativeDeployKey):])
   181  		} else if strings.HasPrefix(line, enabledKey) {
   182  			if mainSection == deployCanaryKey {
   183  				deployOptions.Canary = toBool(strings.TrimSpace(line[len(enabledKey):]))
   184  			} else if mainSection == deployHPAKey {
   185  				deployOptions.HPA = toBool(strings.TrimSpace(line[len(enabledKey):]))
   186  			}
   187  		}
   188  	}
   189  	kind := opts.DeployKindDefault
   190  	if knativeFlag == "true" {
   191  		kind = opts.DeployKindKnative
   192  	}
   193  	return kind, deployOptions
   194  }
   195  
   196  // setDeployKindInValuesYaml sets the `knativeDeployKey` key to true or false based on the deployment kind
   197  func (o *EditDeployKindOptions) setDeployKindInValuesYaml(yamlText string, deployKind string, progressive bool, hpa bool) (string, error) {
   198  	var buffer strings.Builder
   199  
   200  	mainSection := ""
   201  	lines := strings.Split(yamlText, "\n")
   202  	for _, line := range lines {
   203  		if !strings.HasPrefix(line, " ") && !strings.HasPrefix(line, "\t") {
   204  			idx := strings.Index(line, ":")
   205  			if idx > 0 {
   206  				mainSection = line[0 : idx+1]
   207  			}
   208  		}
   209  		if strings.HasPrefix(line, knativeDeployKey) {
   210  			buffer.WriteString(knativeDeployKey)
   211  			buffer.WriteString(" ")
   212  			if deployKind == opts.DeployKindKnative {
   213  				buffer.WriteString("true")
   214  			} else {
   215  				buffer.WriteString("false")
   216  			}
   217  		} else if strings.HasPrefix(line, enabledKey) {
   218  			if mainSection == deployCanaryKey {
   219  				buffer.WriteString(enabledKey)
   220  				buffer.WriteString(" ")
   221  				buffer.WriteString(toString(progressive))
   222  			} else if mainSection == deployHPAKey {
   223  				buffer.WriteString(enabledKey)
   224  				buffer.WriteString(" ")
   225  				buffer.WriteString(toString(hpa))
   226  			} else {
   227  				buffer.WriteString(line)
   228  			}
   229  		} else {
   230  			buffer.WriteString(line)
   231  		}
   232  		buffer.WriteString("\n")
   233  	}
   234  	return buffer.String(), nil
   235  }
   236  
   237  func (o *EditDeployKindOptions) pickDeployKind(defaultName string) (string, error) {
   238  	if defaultName == "" {
   239  		defaultName = opts.DeployKindDefault
   240  	}
   241  	// return the CLI option if specified
   242  	if o.FlagChanged(opts.OptionKind) {
   243  		return o.Kind, nil
   244  	}
   245  	if o.BatchMode {
   246  		return defaultName, nil
   247  	}
   248  	if o.Kind != "" {
   249  		return o.Kind, nil
   250  	}
   251  	args := o.Args
   252  	if len(args) > 0 {
   253  		return args[0], nil
   254  	}
   255  	if util.StringArrayIndex(deployKinds, defaultName) < 0 {
   256  		defaultName = opts.DeployKindDefault
   257  	}
   258  	name, err := util.PickNameWithDefault(deployKinds, "Pick the deployment kind: ", defaultName, "lets you switch between knative serve based deployments and default kubernetes deployments", o.GetIOFileHandles())
   259  	if err != nil {
   260  		return name, err
   261  	}
   262  	if name == "" {
   263  		return name, fmt.Errorf("no kind chosen")
   264  	}
   265  	return name, nil
   266  }
   267  
   268  func (o *EditDeployKindOptions) pickProgressiveDelivery(defaultValue bool) (bool, error) {
   269  	// return the CLI option if specified
   270  	if o.FlagChanged(opts.OptionCanary) {
   271  		return o.DeployOptions.Canary, nil
   272  	}
   273  	if o.BatchMode {
   274  		return defaultValue, nil
   275  	}
   276  	return util.Confirm("Would you like to use Canary Delivery", defaultValue, "Canary delivery lets us use Canary rollouts to incrementally test applications", o.GetIOFileHandles())
   277  }
   278  
   279  func (o *EditDeployKindOptions) pickHPA(defaultValue bool) (bool, error) {
   280  	// return the CLI option if specified
   281  	if o.FlagChanged(opts.OptionHPA) {
   282  		return o.DeployOptions.HPA, nil
   283  	}
   284  	if o.BatchMode {
   285  		return defaultValue, nil
   286  	}
   287  	return util.Confirm("Would you like to use the Horizontal Pod Autoscaler with deployments", defaultValue, "The Horizontal Pod Autoscaler lets you scale your pods up and down automatically", o.GetIOFileHandles())
   288  }
   289  
   290  func toBool(text string) bool {
   291  	return strings.ToLower(text) == "true"
   292  }
   293  
   294  func toString(flag bool) string {
   295  	if flag {
   296  		return "true"
   297  	}
   298  	return "false"
   299  }
   300  
   301  // ToDeployArguments converts the given deploy kind, canary and HPA to CLI arguments we can parse on the command object
   302  func ToDeployArguments(optionsKind string, kind string, canary bool, hpa bool) []string {
   303  	return []string{"--" + optionsKind + "=" + kind, "--" + opts.OptionCanary + "=" + toString(canary), "--" + opts.OptionHPA + "=" + toString(hpa)}
   304  }