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 }