github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/cmd/juju/block/block.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package block 5 6 import ( 7 "fmt" 8 "strings" 9 10 "github.com/juju/cmd" 11 "github.com/juju/errors" 12 "github.com/juju/loggo" 13 14 "github.com/juju/juju/apiserver/params" 15 "github.com/juju/juju/cmd/envcmd" 16 "github.com/juju/juju/environs/config" 17 ) 18 19 var logger = loggo.GetLogger("juju.cmd.juju.block") 20 21 // ProtectionCommand is a super for environment protection commands that block/unblock operations. 22 type ProtectionCommand struct { 23 envcmd.EnvCommandBase 24 operation string 25 desc string 26 } 27 28 // BlockClientAPI defines the client API methods that the protection command uses. 29 type ClientAPI interface { 30 Close() error 31 EnvironmentSet(config map[string]interface{}) error 32 } 33 34 var getBlockClientAPI = func(p *ProtectionCommand) (ClientAPI, error) { 35 return p.NewAPIClient() 36 } 37 38 var ( 39 // blockArgs has all valid operations that can be 40 // supplied to the command. 41 // These operations do not necessarily correspond to juju commands 42 // but are rather juju command groupings. 43 blockArgs = []string{"destroy-environment", "remove-object", "all-changes"} 44 45 // blockArgsFmt has formatted representation of block command valid arguments. 46 blockArgsFmt = fmt.Sprintf(strings.Join(blockArgs, " | ")) 47 48 // blockBaseDoc common block doc 49 blockBaseDoc = ` 50 51 Juju allows to safeguard deployed environments from unintentional damage by preventing 52 execution of operations that could alter environment. 53 54 This is done by blocking certain commands from successful execution. Blocked commands 55 must be manually unblocked to proceed. 56 57 Some comands offer a --force option that can be used to bypass a block. 58 59 Commands that can be %s are grouped based on logical operations as follows: 60 61 destroy-environment includes command: 62 destroy-environment 63 64 remove-object includes termination commands: 65 destroy-environment 66 remove-machine 67 remove-relation 68 remove-service 69 remove-unit 70 71 all-changes includes all alteration commands 72 add-machine 73 add-relation 74 add-unit 75 authorised-keys add 76 authorised-keys delete 77 authorised-keys import 78 deploy 79 destroy-environment 80 ensure-availability 81 expose 82 remove-machine 83 remove-relation 84 remove-service 85 remove-unit 86 resolved 87 retry-provisioning 88 run 89 set 90 set-constraints 91 set-env 92 sync-tools 93 unexpose 94 unset 95 unset-env 96 upgrade-charm 97 upgrade-juju 98 user add 99 user change-password 100 user disable 101 user enable 102 %s 103 ` 104 ) 105 106 // setBlockEnvironmentVariable sets desired environment variable to given value. 107 func (p *ProtectionCommand) setBlockEnvironmentVariable(block bool) error { 108 client, err := getBlockClientAPI(p) 109 if err != nil { 110 return errors.Trace(err) 111 } 112 defer client.Close() 113 attrs := map[string]interface{}{config.BlockKeyPrefix + p.operation: block} 114 return client.EnvironmentSet(attrs) 115 } 116 117 // assignValidOperation verifies that supplied operation is supported. 118 func (p *ProtectionCommand) assignValidOperation(cmd string, args []string) error { 119 if len(args) != 1 { 120 return errors.Trace(errors.Errorf("must specify one of [%v] to %v", blockArgsFmt, cmd)) 121 } 122 var err error 123 p.operation, err = p.obtainValidArgument(args[0]) 124 return err 125 } 126 127 // obtainValidArgument returns polished argument: 128 // it checks that the argument is a supported operation and 129 // forces it into lower case for consistency. 130 func (p *ProtectionCommand) obtainValidArgument(arg string) (string, error) { 131 for _, valid := range blockArgs { 132 if strings.EqualFold(valid, arg) { 133 return strings.ToLower(arg), nil 134 } 135 } 136 return "", errors.Trace(errors.Errorf("%q is not a valid argument: use one of [%v]", arg, blockArgsFmt)) 137 } 138 139 // BlockCommand blocks specified operation. 140 type BlockCommand struct { 141 ProtectionCommand 142 } 143 144 var ( 145 // blockDocEnding - ending of block doc 146 blockDocEnding = ` 147 148 Examples: 149 To prevent the environment from being destroyed: 150 juju block destroy-environment 151 152 To prevent the machines, services, units and relations from being removed: 153 juju block remove-object 154 155 To prevent changes to the environment: 156 juju block all-changes 157 158 See Also: 159 juju help unblock 160 161 ` 162 // blockDoc formatted block doc 163 blockDoc = fmt.Sprintf(blockBaseDoc, "blocked", blockDocEnding) 164 ) 165 166 // Info provides information about command. 167 // Satisfying Command interface. 168 func (c *BlockCommand) Info() *cmd.Info { 169 return &cmd.Info{ 170 Name: "block", 171 Args: blockArgsFmt, 172 Purpose: "block an operation that would alter a running environment", 173 Doc: blockDoc, 174 } 175 } 176 177 // Init initializes the command. 178 // Satisfying Command interface. 179 func (c *BlockCommand) Init(args []string) error { 180 return c.assignValidOperation("block", args) 181 } 182 183 // Run blocks commands from running successfully. 184 // Satisfying Command interface. 185 func (c *BlockCommand) Run(_ *cmd.Context) error { 186 return c.setBlockEnvironmentVariable(true) 187 } 188 189 // Block describes block type 190 type Block int8 191 192 const ( 193 // BlockDestroy describes the block that 194 // blocks destroy- commands 195 BlockDestroy Block = iota 196 197 // BlockRemove describes the block that 198 // blocks remove- commands 199 BlockRemove 200 201 // BlockChange describes the block that 202 // blocks change commands 203 BlockChange 204 ) 205 206 var blockedMessages = map[Block]string{ 207 BlockDestroy: destroyMsg, 208 BlockRemove: removeMsg, 209 BlockChange: changeMsg, 210 } 211 212 // ProcessBlockedError ensures that correct and user-friendly message is 213 // displayed to the user based on the block type. 214 func ProcessBlockedError(err error, block Block) error { 215 if err == nil { 216 return nil 217 } 218 if params.IsCodeOperationBlocked(err) { 219 logger.Errorf(blockedMessages[block]) 220 return cmd.ErrSilent 221 } 222 return err 223 } 224 225 var removeMsg = ` 226 All operations that remove (or delete or terminate) machines, services, units or 227 relations have been blocked for the current environment. 228 To unblock removal, run 229 230 juju unblock remove-object 231 232 ` 233 var destroyMsg = ` 234 destroy-environment operation has been blocked for the current environment. 235 To remove the block run 236 237 juju unblock destroy-environment 238 239 ` 240 var changeMsg = ` 241 All operations that change environment have been blocked for the current environment. 242 To unblock changes, run 243 244 juju unblock all-changes 245 246 `