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

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package main
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"strings"
    10  
    11  	"launchpad.net/gnuflag"
    12  
    13  	"launchpad.net/juju-core/cmd"
    14  	"launchpad.net/juju-core/juju"
    15  	"launchpad.net/juju-core/state/api/params"
    16  )
    17  
    18  // SetCommand updates the configuration of a service.
    19  type SetCommand struct {
    20  	cmd.EnvCommandBase
    21  	ServiceName     string
    22  	SettingsStrings map[string]string
    23  	SettingsYAML    cmd.FileVar
    24  }
    25  
    26  const setDoc = `
    27  Set one or more configuration options for the specified service. See also the
    28  unset command which sets one or more configuration options for a specified
    29  service to their default value. 
    30  `
    31  
    32  func (c *SetCommand) Info() *cmd.Info {
    33  	return &cmd.Info{
    34  		Name:    "set",
    35  		Args:    "<service> name=value ...",
    36  		Purpose: "set service config options",
    37  		Doc:     "Set one or more configuration options for the specified service.",
    38  	}
    39  }
    40  
    41  func (c *SetCommand) SetFlags(f *gnuflag.FlagSet) {
    42  	c.EnvCommandBase.SetFlags(f)
    43  	f.Var(&c.SettingsYAML, "config", "path to yaml-formatted service config")
    44  }
    45  
    46  func (c *SetCommand) Init(args []string) error {
    47  	if len(args) == 0 || len(strings.Split(args[0], "=")) > 1 {
    48  		return errors.New("no service name specified")
    49  	}
    50  	if c.SettingsYAML.Path != "" && len(args) > 1 {
    51  		return errors.New("cannot specify --config when using key=value arguments")
    52  	}
    53  	c.ServiceName = args[0]
    54  	settings, err := parse(args[1:])
    55  	if err != nil {
    56  		return err
    57  	}
    58  	c.SettingsStrings = settings
    59  	return nil
    60  }
    61  
    62  // serviceSet1dot16 does the final ServiceSet step using direct DB access
    63  // compatibility with an API server running 1.16 or older (when ServiceUnset
    64  // was not available). This fallback can be removed when we no longer maintain
    65  // 1.16 compatibility.
    66  // This was copied directly from the code in SetCommand.Run in 1.16
    67  func (c *SetCommand) serviceSet1dot16() error {
    68  	conn, err := juju.NewConnFromName(c.EnvName)
    69  	if err != nil {
    70  		return err
    71  	}
    72  	defer conn.Close()
    73  	service, err := conn.State.Service(c.ServiceName)
    74  	if err != nil {
    75  		return err
    76  	}
    77  	ch, _, err := service.Charm()
    78  	if err != nil {
    79  		return err
    80  	}
    81  	// We don't need the multiple logic here, because that should have
    82  	// already been taken care of by the API code (which *was* in 1.16).
    83  	settings, err := ch.Config().ParseSettingsStrings(c.SettingsStrings)
    84  	if err != nil {
    85  		return err
    86  	}
    87  	return service.UpdateConfigSettings(settings)
    88  }
    89  
    90  // Run updates the configuration of a service.
    91  func (c *SetCommand) Run(ctx *cmd.Context) error {
    92  	api, err := juju.NewAPIClientFromName(c.EnvName)
    93  	if err != nil {
    94  		return err
    95  	}
    96  	defer api.Close()
    97  
    98  	if c.SettingsYAML.Path != "" {
    99  		b, err := c.SettingsYAML.Read(ctx)
   100  		if err != nil {
   101  			return err
   102  		}
   103  		return api.ServiceSetYAML(c.ServiceName, string(b))
   104  	} else if len(c.SettingsStrings) == 0 {
   105  		return nil
   106  	}
   107  	err = api.ServiceSet(c.ServiceName, c.SettingsStrings)
   108  	if params.IsCodeNotImplemented(err) {
   109  		logger.Infof("NewServiceSetForClientAPI not supported by the API server, " +
   110  			"falling back to 1.16 compatibility mode (direct DB access)")
   111  		err = c.serviceSet1dot16()
   112  	}
   113  	return err
   114  }
   115  
   116  // parse parses the option k=v strings into a map of options to be
   117  // updated in the config. Keys with empty values are returned separately
   118  // and should be removed.
   119  func parse(options []string) (map[string]string, error) {
   120  	kv := make(map[string]string)
   121  	for _, o := range options {
   122  		s := strings.SplitN(o, "=", 2)
   123  		if len(s) != 2 || s[0] == "" {
   124  			return nil, fmt.Errorf("invalid option: %q", o)
   125  		}
   126  		kv[s[0]] = s[1]
   127  	}
   128  	return kv, nil
   129  }