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 }