// Find the Tools necessary for the given agents func (u *UpgraderAPI) Tools(args params.Entities) (params.AgentToolsResults, error) { tools := make([]params.AgentToolsResult, len(args.Entities)) result := params.AgentToolsResults{Tools: tools} if len(args.Entities) == 0 { return result, nil } for i, entity := range args.Entities { tools[i].AgentTools.Tag = entity.Tag } // For now, all agents get the same proposed version cfg, err := u.st.EnvironConfig() if err != nil { return result, err } agentVersion, ok := cfg.AgentVersion() if !ok { // TODO: What error do we give here? return result, common.ErrBadRequest } env, err := environs.New(cfg) if err != nil { return result, err } for i, entity := range args.Entities { agentTools, err := u.oneAgentTools(entity, agentVersion, env) if err == nil { tools[i].AgentTools = agentTools } tools[i].Error = common.ServerError(err) } return result, nil }
// Tools finds the Tools necessary for the given agents. func (u *UpgraderAPI) Tools(args params.Entities) (params.AgentToolsResults, error) { results := make([]params.AgentToolsResult, len(args.Entities)) if len(args.Entities) == 0 { return params.AgentToolsResults{}, nil } // For now, all agents get the same proposed version cfg, err := u.st.EnvironConfig() if err != nil { return params.AgentToolsResults{}, err } agentVersion, ok := cfg.AgentVersion() if !ok { return params.AgentToolsResults{}, errors.New("agent version not set in environment config") } env, err := environs.New(cfg) if err != nil { return params.AgentToolsResults{}, err } for i, entity := range args.Entities { agentTools, err := u.oneAgentTools(entity.Tag, agentVersion, env) if err == nil { results[i].Tools = agentTools } results[i].Error = common.ServerError(err) } return params.AgentToolsResults{results}, nil }
// If the environment is configured not to require a public IP address for nodes, // bootstrapping and starting an instance should occur without any attempt to // allocate a public address. func (s *localServerSuite) TestStartInstanceWithoutPublicIP(c *C) { cleanup := s.srv.Service.Nova.RegisterControlPoint( "addFloatingIP", func(sc hook.ServiceControl, args ...interface{}) error { return fmt.Errorf("add floating IP should not have been called") }, ) defer cleanup() cleanup = s.srv.Service.Nova.RegisterControlPoint( "addServerFloatingIP", func(sc hook.ServiceControl, args ...interface{}) error { return fmt.Errorf("add server floating IP should not have been called") }, ) defer cleanup() cfg, err := s.Env.Config().Apply(map[string]interface{}{ "use-floating-ip": false, }) c.Assert(err, IsNil) env, err := environs.New(cfg) c.Assert(err, IsNil) err = environs.Bootstrap(env, constraints.Value{}) c.Assert(err, IsNil) inst, _ := testing.StartInstance(c, env, "100") err = s.Env.StopInstances([]instance.Instance{inst}) c.Assert(err, IsNil) }
func (*ConfigSuite) TestFirewallMode(c *C) { for _, test := range firewallModeTests { c.Logf("test firewall mode %q", test.configFirewallMode) cfgMap := map[string]interface{}{ "name": "only", "type": "dummy", "state-server": true, "authorized-keys": "none", "ca-cert": testing.CACert, "ca-private-key": "", } if test.configFirewallMode != "" { cfgMap["firewall-mode"] = test.configFirewallMode } cfg, err := config.New(cfgMap) if err != nil { c.Assert(err, ErrorMatches, test.errorMsg) continue } env, err := environs.New(cfg) if err != nil { c.Assert(err, ErrorMatches, test.errorMsg) continue } firewallMode := env.Config().FirewallMode() c.Assert(firewallMode, Equals, test.firewallMode) } }
func (t *LiveTests) TestBootstrapWithDefaultSeries(c *C) { if !t.HasProvisioner { c.Skip("HasProvisioner is false; cannot test deployment") } current := version.Current other := current other.Series = "precise" if current == other { other.Series = "quantal" } cfg := t.Env.Config() cfg, err := cfg.Apply(map[string]interface{}{"default-series": other.Series}) c.Assert(err, IsNil) env, err := environs.New(cfg) c.Assert(err, IsNil) dummyenv, err := environs.NewFromAttrs(map[string]interface{}{ "type": "dummy", "name": "dummy storage", "secret": "pizza", "state-server": false, }) c.Assert(err, IsNil) defer dummyenv.Destroy(nil) currentPath := environs.ToolsStoragePath(current) otherPath := environs.ToolsStoragePath(other) envStorage := env.Storage() dummyStorage := dummyenv.Storage() defer envStorage.Remove(otherPath) _, err = environs.PutTools(dummyStorage, ¤t.Number) c.Assert(err, IsNil) // This will only work while cross-compiling across releases is safe, // which depends on external elements. Tends to be safe for the last // few releases, but we may have to refactor some day. err = storageCopy(dummyStorage, currentPath, envStorage, otherPath) c.Assert(err, IsNil) err = environs.Bootstrap(env, false, panicWrite) c.Assert(err, IsNil) defer env.Destroy(nil) conn, err := juju.NewConn(env) c.Assert(err, IsNil) defer conn.Close() // Wait for machine agent to come up on the bootstrap // machine and ensure it deployed the proper series. m0, err := conn.State.Machine("0") c.Assert(err, IsNil) mw0 := newMachineToolWaiter(m0) defer mw0.Stop() waitAgentTools(c, mw0, other) }
// getEnvironStateInfo returns the state and API connection // information from the state and the environment. // // TODO(dimitern): Remove this once we have a way to get state/API // public addresses from state. // BUG(lp:1205371): This is temporary, until the Addresser worker // lands and we can take the addresses of all machines with // JobManageState. func (d *DeployerAPI) getEnvironStateInfo() (*state.Info, *api.Info, error) { cfg, err := d.st.EnvironConfig() if err != nil { return nil, nil, err } env, err := environs.New(cfg) if err != nil { return nil, nil, err } return env.StateInfo() }
// NewConnFromState returns a Conn that uses an Environ // made by reading the environment configuration. // The resulting Conn uses the given State - closing // it will close that State. func NewConnFromState(st *state.State) (*Conn, error) { cfg, err := st.EnvironConfig() if err != nil { return nil, err } environ, err := environs.New(cfg) if err != nil { return nil, err } return &Conn{ Environ: environ, State: st, }, nil }
func (suite) TestConfigRoundTrip(c *C) { cfg, err := config.New(map[string]interface{}{ "name": "bladaam", "type": "dummy", "state-server": false, "authorized-keys": "i-am-a-key", "ca-cert": testing.CACert, "ca-private-key": "", }) c.Assert(err, IsNil) provider, err := environs.Provider(cfg.Type()) c.Assert(err, IsNil) cfg, err = provider.Validate(cfg, nil) c.Assert(err, IsNil) env, err := environs.New(cfg) c.Assert(err, IsNil) c.Assert(cfg.AllAttrs(), DeepEquals, env.Config().AllAttrs()) }
// WaitForEnviron waits for an valid environment to arrive from // the given watcher. It terminates with tomb.ErrDying if // it receives a value on dying. func WaitForEnviron(w *state.EnvironConfigWatcher, dying <-chan struct{}) (environs.Environ, error) { for { select { case <-dying: return nil, tomb.ErrDying case config, ok := <-w.Changes(): if !ok { return nil, watcher.MustErr(w) } environ, err := environs.New(config) if err == nil { return environ, nil } log.Errorf("worker: loaded invalid environment configuration: %v", err) loadedInvalid() } } }
// If the bootstrap node is configured to require a public IP address, // bootstrapping fails if an address cannot be allocated. func (s *localServerSuite) TestBootstrapFailsWhenPublicIPError(c *C) { cleanup := s.srv.Service.Nova.RegisterControlPoint( "addFloatingIP", func(sc hook.ServiceControl, args ...interface{}) error { return fmt.Errorf("failed on purpose") }, ) defer cleanup() // Create a config that matches s.Config but with use-floating-ip set to true cfg, err := s.Env.Config().Apply(map[string]interface{}{ "use-floating-ip": true, }) c.Assert(err, IsNil) env, err := environs.New(cfg) c.Assert(err, IsNil) err = environs.Bootstrap(env, constraints.Value{}) c.Assert(err, ErrorMatches, "(.|\n)*cannot allocate a public IP as needed(.|\n)*") }
func (*ConfigSuite) TestSecretAttrs(c *C) { cfg, err := config.New(map[string]interface{}{ "name": "only", // must match the name in environs_test.go "type": "dummy", "state-server": true, "authorized-keys": "i-am-a-key", "ca-cert": testing.CACert, "ca-private-key": "", }) c.Assert(err, IsNil) env, err := environs.New(cfg) c.Assert(err, IsNil) expected := map[string]interface{}{ "secret": "pork", } actual, err := env.Provider().SecretAttrs(cfg) c.Assert(err, IsNil) c.Assert(expected, DeepEquals, actual) }
func (t *LiveTests) TestBootstrapWithDefaultSeries(c *C) { if !t.HasProvisioner { c.Skip("HasProvisioner is false; cannot test deployment") } current := version.Current other := current other.Series = "quantal" if current == other { other.Series = "precise" } cfg := t.Env.Config() cfg, err := cfg.Apply(map[string]interface{}{"default-series": other.Series}) c.Assert(err, IsNil) env, err := environs.New(cfg) c.Assert(err, IsNil) dummyenv, err := environs.NewFromAttrs(map[string]interface{}{ "type": "dummy", "name": "dummy storage", "secret": "pizza", "state-server": false, "ca-cert": coretesting.CACert, "ca-private-key": coretesting.CAKey, }) c.Assert(err, IsNil) defer dummyenv.Destroy(nil) // BUG: We destroy the environment, then write to its storage. // This is bogus, strictly speaking, but it works on // existing providers for the time being and means // this test does not fail when the environment is // already bootstrapped. t.Destroy(c) currentName := tools.StorageName(current) otherName := tools.StorageName(other) envStorage := env.Storage() dummyStorage := dummyenv.Storage() defer envStorage.Remove(otherName) _, err = tools.Upload(dummyStorage, ¤t.Number) c.Assert(err, IsNil) // This will only work while cross-compiling across releases is safe, // which depends on external elements. Tends to be safe for the last // few releases, but we may have to refactor some day. err = storageCopy(dummyStorage, currentName, envStorage, otherName) c.Assert(err, IsNil) err = environs.Bootstrap(env, constraints.Value{}) c.Assert(err, IsNil) defer env.Destroy(nil) conn, err := juju.NewConn(env) c.Assert(err, IsNil) defer conn.Close() // Wait for machine agent to come up on the bootstrap // machine and ensure it deployed the proper series. m0, err := conn.State.Machine("0") c.Assert(err, IsNil) mw0 := newMachineToolWaiter(m0) defer mw0.Stop() waitAgentTools(c, mw0, other) }
func (u *Upgrader) run() error { // Let the state know the version that is currently running. currentTools, err := tools.ReadTools(u.dataDir, version.Current) if err != nil { // Don't abort everything because we can't find the tools directory. // The problem should sort itself out as we will immediately // download some more tools and upgrade. log.Warningf("upgrader cannot read current tools: %v", err) currentTools = &tools.Tools{ Binary: version.Current, } } err = u.agentState.SetAgentTools(currentTools) if err != nil { return err } // TODO(fwereade): this whole package should be ignorant of environs, // so it shouldn't be watching environ config (and it shouldn't be // looking in storage): we should be able to find out what to download // from state, exactly as we do for charms. w := u.st.WatchEnvironConfig() defer watcher.Stop(w, &u.tomb) // Rather than using worker.WaitForEnviron, invalid environments are // managed explicitly so that all configuration changes are observed // by the loop below. var environ environs.Environ // TODO(rog) retry downloads when they fail. var ( download *downloader.Download downloadTools *tools.Tools downloadDone <-chan downloader.Status ) // If we're killed early on (probably as a result of some other // task dying) we allow ourselves some time to try to connect to // the state and download a new version. We return to normal // undelayed behaviour when: // 1) We find there's no upgrade to do. // 2) A download fails. tomb := delayedTomb(&u.tomb, upgraderKillDelay) noDelay := func() { if tomb != &u.tomb { tomb.Kill(nil) tomb = &u.tomb } } for { // We wait for the tools to change while we're downloading // so that if something goes wrong (for instance a bad URL // hangs up) another change to the proposed tools can // potentially fix things. select { case cfg, ok := <-w.Changes(): if !ok { return watcher.MustErr(w) } var err error if environ == nil { environ, err = environs.New(cfg) if err != nil { log.Errorf("upgrader loaded invalid initial environment configuration: %v", err) break } } else { err = environ.SetConfig(cfg) if err != nil { log.Warningf("upgrader loaded invalid environment configuration: %v", err) // continue on, because the version number is still significant. } } proposed, ok := cfg.AgentVersion() if !ok { // This shouldn't be possible; but if it happens it's no reason // to kill this task. Just wait for the config to change again. continue } if download != nil { // There's a download in progress, stop it if we need to. if downloadTools.Number == proposed { // We are already downloading the requested tools. break } // Tools changed. We need to stop and restart. download.Stop() download, downloadTools, downloadDone = nil, nil, nil } // TODO: major version upgrades. if proposed.Major != version.Current.Major { log.Errorf("major version upgrades are not supported yet") noDelay() break } if proposed == version.Current.Number { noDelay() break } required := version.Binary{ Number: proposed, Series: version.Current.Series, Arch: version.Current.Arch, } if tools, err := tools.ReadTools(u.dataDir, required); err == nil { // The exact tools have already been downloaded, so use them. return u.upgradeReady(currentTools, tools) } tools, err := environs.FindExactTools(environ, required) if err != nil { log.Errorf("upgrader error finding tools for %v: %v", required, err) if !errors.IsNotFoundError(err) { return err } noDelay() // TODO(rog): poll until tools become available. break } log.Infof("upgrader downloading %q", tools.URL) download = downloader.New(tools.URL, "") downloadTools = tools downloadDone = download.Done() case status := <-downloadDone: newTools := downloadTools download, downloadTools, downloadDone = nil, nil, nil if status.Err != nil { log.Errorf("upgrader download of %v failed: %v", newTools.Binary, status.Err) noDelay() break } err := tools.UnpackTools(u.dataDir, newTools, status.File) status.File.Close() if err := os.Remove(status.File.Name()); err != nil { log.Warningf("upgrader cannot remove temporary download file: %v", err) } if err != nil { log.Errorf("upgrader cannot unpack %v tools: %v", newTools.Binary, err) noDelay() break } return u.upgradeReady(currentTools, newTools) case <-tomb.Dying(): if download != nil { return fmt.Errorf("upgrader aborted download of %q", downloadTools.URL) } return nil } } panic("not reached") }
func (u *Upgrader) run() error { // Let the state know the version that is currently running. currentTools, err := environs.ReadTools(u.dataDir, version.Current) if err != nil { // Don't abort everything because we can't find the tools directory. // The problem should sort itself out as we will immediately // download some more tools and upgrade. log.Printf("cmd/jujud: upgrader cannot read current tools: %v", err) currentTools = &state.Tools{ Binary: version.Current, } } err = u.agentState.SetAgentTools(currentTools) if err != nil { return err } w := u.st.WatchEnvironConfig() defer watcher.Stop(w, &u.tomb) // Rather than using worker.WaitForEnviron, invalid environments are // managed explicitly so that all configuration changes are observed // by the loop below. var environ environs.Environ // TODO(rog) retry downloads when they fail. var ( download *downloader.Download downloadTools *state.Tools downloadDone <-chan downloader.Status ) // If we're killed early on (probably as a result of some other // task dying) we allow ourselves some time to try to connect to // the state and download a new version. We return to normal // undelayed behaviour when: // 1) We find there's no upgrade to do. // 2) A download fails. tomb := delayedTomb(&u.tomb, upgraderKillDelay) noDelay := func() { if tomb != &u.tomb { tomb.Kill(nil) tomb = &u.tomb } } for { // We wait for the tools to change while we're downloading // so that if something goes wrong (for instance a bad URL // hangs up) another change to the proposed tools can // potentially fix things. select { case cfg, ok := <-w.Changes(): if !ok { return watcher.MustErr(w) } var err error if environ == nil { environ, err = environs.New(cfg) if err != nil { log.Printf("cmd/jujud: upgrader loaded invalid initial environment configuration: %v", err) break } } else { err = environ.SetConfig(cfg) if err != nil { log.Printf("cmd/jujud: upgrader loaded invalid environment configuration: %v", err) // continue on, because the version number is still significant. } } vers := cfg.AgentVersion() if download != nil { // There's a download in progress, stop it if we need to. if vers == downloadTools.Number { // We are already downloading the requested tools. break } // Tools changed. We need to stop and restart. download.Stop() download, downloadTools, downloadDone = nil, nil, nil } // Ignore the proposed tools if we're already running the // proposed version. if vers == version.Current.Number { noDelay() break } binary := version.Current binary.Number = vers if tools, err := environs.ReadTools(u.dataDir, binary); err == nil { // The tools have already been downloaded, so use them. return u.upgradeReady(currentTools, tools) } flags := environs.CompatVersion if cfg.Development() { flags |= environs.DevVersion } tools, err := environs.FindTools(environ, binary, flags) if err != nil { log.Printf("cmd/jujud: upgrader error finding tools for %v: %v", binary, err) noDelay() // TODO(rog): poll until tools become available. break } if tools.Binary != binary { if tools.Number == version.Current.Number { // TODO(rog): poll until tools become available. log.Printf("cmd/jujud: upgrader: version %v requested but found only current version: %v", binary, tools.Number) noDelay() break } log.Printf("cmd/jujud: upgrader cannot find exact tools match for %s; using %s instead", binary, tools.Binary) } log.Printf("cmd/jujud: upgrader downloading %q", tools.URL) download = downloader.New(tools.URL, "") downloadTools = tools downloadDone = download.Done() case status := <-downloadDone: tools := downloadTools download, downloadTools, downloadDone = nil, nil, nil if status.Err != nil { log.Printf("cmd/jujud: upgrader download of %v failed: %v", tools.Binary, status.Err) noDelay() break } err := environs.UnpackTools(u.dataDir, tools, status.File) status.File.Close() if err := os.Remove(status.File.Name()); err != nil { log.Printf("cmd/jujud: upgrader cannot remove temporary download file: %v", err) } if err != nil { log.Printf("cmd/jujud: upgrader cannot unpack %v tools: %v", tools.Binary, err) noDelay() break } return u.upgradeReady(currentTools, tools) case <-tomb.Dying(): if download != nil { return fmt.Errorf("upgrader aborted download of %q", downloadTools.URL) } return nil } } panic("not reached") }