func (c *UpgradeJujuCommand) Init(args []string) error { if c.vers != "" { vers, err := version.Parse(c.vers) if err != nil { return err } if vers.Major != version.Current.Major { return fmt.Errorf("cannot upgrade to version incompatible with CLI") } if c.UploadTools && vers.Build != 0 { // TODO(fwereade): when we start taking versions from actual built // code, we should disable --version when used with --upload-tools. // For now, it's the only way to experiment with version upgrade // behaviour live, so the only restriction is that Build cannot // be used (because its value needs to be chosen internally so as // not to collide with existing tools). return fmt.Errorf("cannot specify build number when uploading tools") } c.Version = vers } if len(c.Series) > 0 && !c.UploadTools { return fmt.Errorf("--series requires --upload-tools") } return cmd.CheckEmpty(args) }
func (suite) TestComparison(c *C) { for i, test := range cmpTests { c.Logf("test %d", i) v1, err := version.Parse(test.v1) c.Assert(err, IsNil) v2, err := version.Parse(test.v2) c.Assert(err, IsNil) less := v1.Less(v2) gt := v2.Less(v1) c.Check(less, Equals, test.less) if test.eq { c.Check(gt, Equals, false) } else { c.Check(gt, Equals, !test.less) } } }
// Validate ensures that config is a valid configuration. If old is not nil, // it holds the previous environment configuration for consideration when // validating changes. func Validate(cfg, old *Config) error { // Check if there are any required fields that are empty. for _, attr := range []string{"name", "type", "default-series", "authorized-keys"} { if cfg.asString(attr) == "" { return fmt.Errorf("empty %s in environment configuration", attr) } } if strings.ContainsAny(cfg.asString("name"), "/\\") { return fmt.Errorf("environment name contains unsafe characters") } // Check that the agent version parses ok if set explicitly; otherwise leave // it alone. if v, ok := cfg.m["agent-version"].(string); ok { if _, err := version.Parse(v); err != nil { return fmt.Errorf("invalid agent version in environment configuration: %q", v) } } // Check firewall mode. firewallMode := cfg.FirewallMode() switch firewallMode { case FwDefault, FwInstance, FwGlobal: // Valid mode. default: return fmt.Errorf("invalid firewall mode in environment configuration: %q", firewallMode) } // Check the immutable config values. These can't change if old != nil { for _, attr := range []string{"type", "name", "firewall-mode"} { oldValue := old.asString(attr) newValue := cfg.asString(attr) if oldValue != newValue { return fmt.Errorf("cannot change %s from %q to %q", attr, oldValue, newValue) } } oldStatePort := old.StatePort() newStatePort := cfg.StatePort() if oldStatePort != newStatePort { return fmt.Errorf("cannot change state-port from %d to %d", oldStatePort, newStatePort) } oldAPIPort := old.APIPort() newAPIPort := cfg.APIPort() if oldAPIPort != newAPIPort { return fmt.Errorf("cannot change api-port from %d to %d", oldAPIPort, newAPIPort) } if _, oldFound := old.AgentVersion(); oldFound { if _, newFound := cfg.AgentVersion(); !newFound { return fmt.Errorf("cannot clear agent-version") } } } return nil }
// AgentVersion returns the proposed version number for the agent tools, // and whether it has been set. Once an environment is bootstrapped, this // must always be valid. func (c *Config) AgentVersion() (version.Number, bool) { if v, ok := c.m["agent-version"].(string); ok { n, err := version.Parse(v) if err != nil { panic(err) // We should have checked it earlier. } return n, true } return version.Zero, false }
// AgentVersion returns the proposed version number for the agent tools. // It returns the zero version if unset. func (c *Config) AgentVersion() version.Number { v, ok := c.m["agent-version"].(string) if !ok { return version.Number{} } n, err := version.Parse(v) if err != nil { panic(err) // We should have checked it earlier. } return n }
func (suite) TestParse(c *C) { for i, test := range parseTests { c.Logf("test %d", i) got, err := version.Parse(test.v) if test.err != "" { c.Assert(err, ErrorMatches, test.err) } else { c.Assert(err, IsNil) c.Assert(got, Equals, test.expect) c.Check(got.IsDev(), Equals, test.dev) c.Check(got.String(), Equals, test.v) } } }
func (c *UpgradeJujuCommand) Init(f *gnuflag.FlagSet, args []string) error { addEnvironFlags(&c.EnvName, f) var vers string f.BoolVar(&c.UploadTools, "upload-tools", false, "upload local version of tools") f.StringVar(&vers, "version", "", "version to upgrade to (defaults to highest available version with the current major version number)") f.BoolVar(&c.BumpVersion, "bump-version", false, "upload the tools with a higher build number if necessary, and use that version (overrides --version)") f.BoolVar(&c.Development, "dev", false, "allow development versions to be chosen") if err := f.Parse(true, args); err != nil { return err } if vers != "" { var err error c.Version, err = version.Parse(vers) if err != nil { return err } if c.Version == (version.Number{}) { return fmt.Errorf("cannot upgrade to version 0.0.0") } } return cmd.CheckEmpty(f.Args()) }
// New returns a new configuration. Fields that are common to all // environment providers are verified. The "authorized-keys-path" key // is translated into "authorized-keys" by loading the content from // respective file. Similarly, "ca-cert-path" and "ca-private-key-path" // are translated into the "ca-cert" and "ca-private-key" values. If // not specified, authorized SSH keys and CA details will be read from: // // ~/.ssh/id_dsa.pub // ~/.ssh/id_rsa.pub // ~/.ssh/identity.pub // ~/.juju/<name>-cert.pem // ~/.juju/<name>-private-key.pem // // The required keys (after any files have been read) are "name", // "type" and "authorized-keys", all of type string. Additional keys // recognised are "agent-version" and "development", of types string // and bool respectively. func New(attrs map[string]interface{}) (*Config, error) { m, err := checker.Coerce(attrs, nil) if err != nil { return nil, err } c := &Config{ m: m.(map[string]interface{}), t: make(map[string]interface{}), } name := c.m["name"].(string) if name == "" { return nil, fmt.Errorf("empty name in environment configuration") } if strings.ContainsAny(name, "/\\") { return nil, fmt.Errorf("environment name contains unsafe characters") } if c.m["default-series"].(string) == "" { c.m["default-series"] = version.Current.Series } // Load authorized-keys-path into authorized-keys if necessary. path := c.m["authorized-keys-path"].(string) keys := c.m["authorized-keys"].(string) if path != "" || keys == "" { c.m["authorized-keys"], err = readAuthorizedKeys(path) if err != nil { return nil, err } } delete(c.m, "authorized-keys-path") caCert, err := maybeReadFile(c.m, "ca-cert", name+"-cert.pem") if err != nil { return nil, err } caKey, err := maybeReadFile(c.m, "ca-private-key", name+"-private-key.pem") if err != nil { return nil, err } if caCert != nil || caKey != nil { if err := verifyKeyPair(caCert, caKey); err != nil { return nil, fmt.Errorf("bad CA certificate/key in configuration: %v", err) } } // Check if there are any required fields that are empty. for _, attr := range []string{"type", "default-series", "authorized-keys"} { if s, _ := c.m[attr].(string); s == "" { return nil, fmt.Errorf("empty %s in environment configuration", attr) } } // Check that the agent version parses ok if set. if v, ok := c.m["agent-version"].(string); ok { if _, err := version.Parse(v); err != nil { return nil, fmt.Errorf("invalid agent version in environment configuration: %q", v) } } // Check firewall mode. firewallMode := FirewallMode(c.m["firewall-mode"].(string)) switch firewallMode { case FwDefault, FwInstance, FwGlobal: // Valid mode. default: return nil, fmt.Errorf("invalid firewall mode in environment configuration: %q", firewallMode) } // Copy unknown attributes onto the type-specific map. for k, v := range attrs { if _, ok := fields[k]; !ok { c.t[k] = v } } return c, nil }
func (test configTest) check(c *C, h fakeHome) { cfg, err := config.New(test.attrs) if test.err != "" { c.Check(cfg, IsNil) c.Assert(err, ErrorMatches, test.err) return } c.Assert(err, IsNil) typ, _ := test.attrs["type"].(string) name, _ := test.attrs["name"].(string) c.Assert(cfg.Type(), Equals, typ) c.Assert(cfg.Name(), Equals, name) if s := test.attrs["agent-version"]; s != nil { vers, err := version.Parse(s.(string)) c.Assert(err, IsNil) c.Assert(cfg.AgentVersion(), Equals, vers) } else { c.Assert(cfg.AgentVersion(), Equals, version.Number{}) } dev, _ := test.attrs["development"].(bool) c.Assert(cfg.Development(), Equals, dev) if series, _ := test.attrs["default-series"].(string); series != "" { c.Assert(cfg.DefaultSeries(), Equals, series) } else { c.Assert(cfg.DefaultSeries(), Equals, version.Current.Series) } if m, _ := test.attrs["firewall-mode"].(string); m != "" { c.Assert(cfg.FirewallMode(), Equals, config.FirewallMode(m)) } if secret, _ := test.attrs["admin-secret"].(string); secret != "" { c.Assert(cfg.AdminSecret(), Equals, secret) } if path, _ := test.attrs["authorized-keys-path"].(string); path != "" { c.Assert(cfg.AuthorizedKeys(), Equals, h.fileContents(c, path)) c.Assert(cfg.AllAttrs()["authorized-keys-path"], Equals, nil) } else if keys, _ := test.attrs["authorized-keys"].(string); keys != "" { c.Assert(cfg.AuthorizedKeys(), Equals, keys) } else { // Content of all the files that are read by default. want := "dsa\nrsa\nidentity\n" c.Assert(cfg.AuthorizedKeys(), Equals, want) } cert, certPresent := cfg.CACert() if path, _ := test.attrs["ca-cert-path"].(string); path != "" { c.Assert(certPresent, Equals, true) c.Assert(string(cert), Equals, h.fileContents(c, path)) } else if v, ok := test.attrs["ca-cert"].(string); v != "" { c.Assert(certPresent, Equals, true) c.Assert(string(cert), Equals, v) } else if ok { c.Check(cert, HasLen, 0) c.Assert(certPresent, Equals, false) } else if h.fileExists(".juju/my-name-cert.pem") { c.Assert(certPresent, Equals, true) c.Assert(string(cert), Equals, h.fileContents(c, "my-name-cert.pem")) } else { c.Check(cert, HasLen, 0) c.Assert(certPresent, Equals, false) } key, keyPresent := cfg.CAPrivateKey() if path, _ := test.attrs["ca-private-key-path"].(string); path != "" { c.Assert(keyPresent, Equals, true) c.Assert(string(key), Equals, h.fileContents(c, path)) } else if v, ok := test.attrs["ca-private-key"].(string); v != "" { c.Assert(keyPresent, Equals, true) c.Assert(string(key), Equals, v) } else if ok { c.Check(key, HasLen, 0) c.Assert(keyPresent, Equals, false) } else if h.fileExists(".juju/my-name-private-key.pem") { c.Assert(keyPresent, Equals, true) c.Assert(string(key), Equals, h.fileContents(c, "my-name-private-key.pem")) } else { c.Check(key, HasLen, 0) c.Assert(keyPresent, Equals, false) } if v, ok := test.attrs["ssl-hostname-verification"]; ok { c.Assert(cfg.SSLHostnameVerification(), Equals, v) } }