github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/model/advancegeneration.go (about)

     1  // Copyright 2019 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package model
     5  
     6  import (
     7  	"github.com/juju/cmd"
     8  	"github.com/juju/errors"
     9  	"github.com/juju/gnuflag"
    10  	"gopkg.in/juju/names.v2"
    11  
    12  	"github.com/juju/juju/api/modelgeneration"
    13  	jujucmd "github.com/juju/juju/cmd"
    14  	"github.com/juju/juju/cmd/modelcmd"
    15  	"github.com/juju/juju/core/model"
    16  )
    17  
    18  const (
    19  	advanceGenerationSummary = "Advances units and/or applications to the next generation."
    20  	advanceGenerationDoc     = `
    21  Users need to be able to roll changes to applications in a safe guided 
    22  processes that controls the flow such that not all units of an HA application 
    23  are hit at once. This also allows some manual canary testing and provides 
    24  control over the flow of changes out to the model. 
    25  
    26  Examples:
    27      juju advance-generation redis
    28      juju advance-generation redis/0
    29      juju advance-generation redis/0 mysql
    30  
    31  See also:
    32      add-generation
    33      cancel-generation
    34      switch-generation
    35  
    36  Aliases:
    37      advance
    38  `
    39  )
    40  
    41  // NewAdvanceGenerationCommand wraps advanceGenerationCommand with sane model settings.
    42  func NewAdvanceGenerationCommand() cmd.Command {
    43  	return modelcmd.Wrap(&advanceGenerationCommand{})
    44  }
    45  
    46  // advanceGenerationCommand is the simplified command for accessing and setting
    47  // attributes related to adding model generations.
    48  type advanceGenerationCommand struct {
    49  	modelcmd.ModelCommandBase
    50  
    51  	api      AdvanceGenerationCommandAPI
    52  	entities []string
    53  }
    54  
    55  // AdvanceGenerationCommandAPI defines an API interface to be used during testing.
    56  //go:generate mockgen -package mocks -destination ./mocks/advancegeneration_mock.go github.com/juju/juju/cmd/juju/model AdvanceGenerationCommandAPI
    57  type AdvanceGenerationCommandAPI interface {
    58  	Close() error
    59  	AdvanceGeneration(string, []string) (bool, error)
    60  }
    61  
    62  // Info implements part of the cmd.Command interface.
    63  func (c *advanceGenerationCommand) Info() *cmd.Info {
    64  	info := &cmd.Info{
    65  		Name:    "advance-generation",
    66  		Aliases: []string{"advance"},
    67  		Purpose: advanceGenerationSummary,
    68  		Doc:     advanceGenerationDoc,
    69  	}
    70  	return jujucmd.Info(info)
    71  }
    72  
    73  // SetFlags implements part of the cmd.Command interface.
    74  func (c *advanceGenerationCommand) SetFlags(f *gnuflag.FlagSet) {
    75  	c.ModelCommandBase.SetFlags(f)
    76  }
    77  
    78  // Init implements part of the cmd.Command interface.
    79  func (c *advanceGenerationCommand) Init(args []string) error {
    80  	if len(args) == 0 {
    81  		return errors.Errorf("unit and/or application names(s) must be specified")
    82  	}
    83  	for _, arg := range args {
    84  		if !names.IsValidApplication(arg) && !names.IsValidUnit(arg) {
    85  			return errors.Errorf("invalid application or unit name %q", arg)
    86  		}
    87  	}
    88  	c.entities = args
    89  	return nil
    90  }
    91  
    92  // getAPI returns the API. This allows passing in a test AdvanceGenerationCommandAPI
    93  // implementation.
    94  func (c *advanceGenerationCommand) getAPI() (AdvanceGenerationCommandAPI, error) {
    95  	if c.api != nil {
    96  		return c.api, nil
    97  	}
    98  	api, err := c.NewAPIRoot()
    99  	if err != nil {
   100  		return nil, errors.Annotate(err, "opening API connection")
   101  	}
   102  	client := modelgeneration.NewClient(api)
   103  	return client, nil
   104  }
   105  
   106  // Run implements the meaty part of the cmd.Command interface.
   107  func (c *advanceGenerationCommand) Run(ctx *cmd.Context) error {
   108  	client, err := c.getAPI()
   109  	if err != nil {
   110  		return err
   111  	}
   112  	defer func() { _ = client.Close() }()
   113  	_, modelDetails, err := c.ModelDetails()
   114  	if err != nil {
   115  		return errors.Annotate(err, "getting model details")
   116  	}
   117  
   118  	completed, err := client.AdvanceGeneration(modelDetails.ModelUUID, c.entities)
   119  	if err != nil {
   120  		return errors.Trace(err)
   121  	}
   122  
   123  	// If the unit advancement caused the generation to be completed,
   124  	// notify the user and set the local store generation back to "current".
   125  	if completed {
   126  		if err = c.SetModelGeneration(model.GenerationCurrent); err == nil {
   127  			_, err = ctx.Stdout.Write([]byte(
   128  				"generation automatically completed; target generation set to \"current\"\n"))
   129  		}
   130  
   131  	}
   132  	return errors.Trace(err)
   133  }