github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/cmd/juju/environment.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package main
     5  
     6  import (
     7  	"fmt"
     8  	"strings"
     9  
    10  	"launchpad.net/gnuflag"
    11  
    12  	"launchpad.net/juju-core/cmd"
    13  	"launchpad.net/juju-core/juju"
    14  	"launchpad.net/juju-core/state/api/params"
    15  )
    16  
    17  // GetEnvironmentCommand is able to output either the entire environment or
    18  // the requested value in a format of the user's choosing.
    19  type GetEnvironmentCommand struct {
    20  	cmd.EnvCommandBase
    21  	key string
    22  	out cmd.Output
    23  }
    24  
    25  const getEnvHelpDoc = `
    26  If no extra args passed on the command line, all configuration keys and values
    27  for the environment are output using the selected formatter.
    28  
    29  A single environment value can be output by adding the environment key name to
    30  the end of the command line.
    31  
    32  Example:
    33    
    34    juju get-environment default-series  (returns the default series for the environment)
    35  `
    36  
    37  func (c *GetEnvironmentCommand) Info() *cmd.Info {
    38  	return &cmd.Info{
    39  		Name:    "get-environment",
    40  		Args:    "[<environment key>]",
    41  		Purpose: "view environment values",
    42  		Doc:     strings.TrimSpace(getEnvHelpDoc),
    43  		Aliases: []string{"get-env"},
    44  	}
    45  }
    46  
    47  func (c *GetEnvironmentCommand) SetFlags(f *gnuflag.FlagSet) {
    48  	c.EnvCommandBase.SetFlags(f)
    49  	c.out.AddFlags(f, "smart", cmd.DefaultFormatters)
    50  }
    51  
    52  func (c *GetEnvironmentCommand) Init(args []string) (err error) {
    53  	c.key, err = cmd.ZeroOrOneArgs(args)
    54  	return
    55  }
    56  
    57  // environmentGet1dot16 runs matches client.EnvironmentGet using a direct DB
    58  // connection to maintain compatibility with an API server running 1.16 or
    59  // older (when EnvironmentGet was not available). This fallback can be removed
    60  // when we no longer maintain 1.16 compatibility.
    61  func (c *GetEnvironmentCommand) environmentGet1dot16() (map[string]interface{}, error) {
    62  	conn, err := juju.NewConnFromName(c.EnvName)
    63  	if err != nil {
    64  		return nil, err
    65  	}
    66  	defer conn.Close()
    67  
    68  	// Get the existing environment config from the state.
    69  	config, err := conn.State.EnvironConfig()
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  	attrs := config.AllAttrs()
    74  	return attrs, nil
    75  }
    76  
    77  func (c *GetEnvironmentCommand) Run(ctx *cmd.Context) error {
    78  	client, err := juju.NewAPIClientFromName(c.EnvName)
    79  	if err != nil {
    80  		return err
    81  	}
    82  	defer client.Close()
    83  
    84  	attrs, err := client.EnvironmentGet()
    85  	if params.IsCodeNotImplemented(err) {
    86  		logger.Infof("EnvironmentGet not supported by the API server, " +
    87  			"falling back to 1.16 compatibility mode (direct DB access)")
    88  		attrs, err = c.environmentGet1dot16()
    89  	}
    90  	if err != nil {
    91  		return err
    92  	}
    93  
    94  	if c.key != "" {
    95  		if value, found := attrs[c.key]; found {
    96  			return c.out.Write(ctx, value)
    97  		}
    98  		return fmt.Errorf("Key %q not found in %q environment.", c.key, attrs["name"])
    99  	}
   100  	// If key is empty, write out the whole lot.
   101  	return c.out.Write(ctx, attrs)
   102  }
   103  
   104  type attributes map[string]interface{}
   105  
   106  // SetEnvironment
   107  type SetEnvironmentCommand struct {
   108  	cmd.EnvCommandBase
   109  	values attributes
   110  }
   111  
   112  const setEnvHelpDoc = `
   113  Updates the environment of a running Juju instance.  Multiple key/value pairs
   114  can be passed on as command line arguments.
   115  `
   116  
   117  func (c *SetEnvironmentCommand) Info() *cmd.Info {
   118  	return &cmd.Info{
   119  		Name:    "set-environment",
   120  		Args:    "key=[value] ...",
   121  		Purpose: "replace environment values",
   122  		Doc:     strings.TrimSpace(setEnvHelpDoc),
   123  		Aliases: []string{"set-env"},
   124  	}
   125  }
   126  
   127  // SetFlags handled entirely by cmd.EnvCommandBase
   128  
   129  func (c *SetEnvironmentCommand) Init(args []string) (err error) {
   130  	if len(args) == 0 {
   131  		return fmt.Errorf("No key, value pairs specified")
   132  	}
   133  	// TODO(thumper) look to have a common library of functions for dealing
   134  	// with key=value pairs.
   135  	c.values = make(attributes)
   136  	for i, arg := range args {
   137  		bits := strings.SplitN(arg, "=", 2)
   138  		if len(bits) < 2 {
   139  			return fmt.Errorf(`Missing "=" in arg %d: %q`, i+1, arg)
   140  		}
   141  		key := bits[0]
   142  		if key == "agent-version" {
   143  			return fmt.Errorf("agent-version must be set via upgrade-juju")
   144  		}
   145  		if _, exists := c.values[key]; exists {
   146  			return fmt.Errorf(`Key %q specified more than once`, key)
   147  		}
   148  		c.values[key] = bits[1]
   149  	}
   150  	return nil
   151  }
   152  
   153  // run1dot16 runs matches client.EnvironmentSet using a direct DB
   154  // connection to maintain compatibility with an API server running 1.16 or
   155  // older (when EnvironmentSet was not available). This fallback can be removed
   156  // when we no longer maintain 1.16 compatibility.
   157  // This content was copied from SetEnvironmentCommand.Run in 1.16
   158  func (c *SetEnvironmentCommand) run1dot16() error {
   159  	conn, err := juju.NewConnFromName(c.EnvName)
   160  	if err != nil {
   161  		return err
   162  	}
   163  	defer conn.Close()
   164  
   165  	// Here is the magic around setting the attributes:
   166  	// TODO(thumper): get this magic under test somewhere, and update other call-sites to use it.
   167  	// Get the existing environment config from the state.
   168  	oldConfig, err := conn.State.EnvironConfig()
   169  	if err != nil {
   170  		return err
   171  	}
   172  	// Apply the attributes specified for the command to the state config.
   173  	newConfig, err := oldConfig.Apply(c.values)
   174  	if err != nil {
   175  		return err
   176  	}
   177  	// Now validate this new config against the existing config via the provider.
   178  	provider := conn.Environ.Provider()
   179  	newProviderConfig, err := provider.Validate(newConfig, oldConfig)
   180  	if err != nil {
   181  		return err
   182  	}
   183  	// Now try to apply the new validated config.
   184  	return conn.State.SetEnvironConfig(newProviderConfig, oldConfig)
   185  }
   186  
   187  func (c *SetEnvironmentCommand) Run(ctx *cmd.Context) error {
   188  	client, err := juju.NewAPIClientFromName(c.EnvName)
   189  	if err != nil {
   190  		return err
   191  	}
   192  	defer client.Close()
   193  
   194  	err = client.EnvironmentSet(c.values)
   195  	if params.IsCodeNotImplemented(err) {
   196  		logger.Infof("EnvironmentSet not supported by the API server, " +
   197  			"falling back to 1.16 compatibility mode (direct DB access)")
   198  		err = c.run1dot16()
   199  	}
   200  	return err
   201  }