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  `