Example #1
0
// 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
}
Example #2
0
// 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
}
Example #3
0
// 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)
}
Example #4
0
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)
	}
}
Example #5
0
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, &current.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)
}
Example #6
0
// 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()
}
Example #7
0
// 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
}
Example #8
0
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())
}
Example #9
0
// 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()
		}
	}
}
Example #10
0
// 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)*")
}
Example #11
0
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)
}
Example #12
0
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, &current.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)
}
Example #13
0
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")
}
Example #14
0
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")
}