Example #1
0
func (p *Provisioner) getAgentTools() (*tools.Tools, error) {
	tools, err := tools.ReadTools(p.dataDir, version.Current)
	if err != nil {
		logger.Errorf("cannot read agent tools from %q", p.dataDir)
		return nil, err
	}
	return tools, nil
}
Example #2
0
func (t *ToolsSuite) TestReadToolsErrors(c *gc.C) {
	vers := version.MustParseBinary("1.2.3-precise-amd64")
	testTools, err := tools.ReadTools(t.dataDir, vers)
	c.Assert(testTools, gc.IsNil)
	c.Assert(err, gc.ErrorMatches, "cannot read URL in tools directory: .*")

	dir := tools.SharedToolsDir(t.dataDir, vers)
	err = os.MkdirAll(dir, 0755)
	c.Assert(err, gc.IsNil)

	err = ioutil.WriteFile(filepath.Join(dir, urlFile), []byte(" \t\n"), 0644)
	c.Assert(err, gc.IsNil)

	testTools, err = tools.ReadTools(t.dataDir, vers)
	c.Assert(testTools, gc.IsNil)
	c.Assert(err, gc.ErrorMatches, "empty URL in tools directory.*")
}
Example #3
0
// assertToolsContents asserts that the directory for the tools
// has the given contents.
func (t *ToolsSuite) assertToolsContents(c *gc.C, testTools *tools.Tools, files []*testing.TarFile) {
	var wantNames []string
	for _, f := range files {
		wantNames = append(wantNames, f.Header.Name)
	}
	wantNames = append(wantNames, urlFile)
	dir := tools.SharedToolsDir(t.dataDir, testTools.Version)
	assertDirNames(c, dir, wantNames)
	assertFileContents(c, dir, urlFile, testTools.URL, 0200)
	for _, f := range files {
		assertFileContents(c, dir, f.Header.Name, f.Contents, 0400)
	}
	gotTools, err := tools.ReadTools(t.dataDir, testTools.Version)
	c.Assert(err, gc.IsNil)
	c.Assert(*gotTools, gc.Equals, *testTools)
}
Example #4
0
func (s *UpgraderSuite) TestUpgraderUpgradesImmediately(c *gc.C) {
	oldTools := s.primeTools(c, version.MustParseBinary("5.4.3-foo-bar"))
	newTools := s.uploadTools(c, version.MustParseBinary("5.4.5-foo-bar"))

	err := statetesting.SetAgentVersion(s.State, newTools.Version.Number)
	c.Assert(err, gc.IsNil)

	// Make the download take a while so that we verify that
	// the download happens before the upgrader checks if
	// it's been stopped.
	dummy.SetStorageDelay(coretesting.ShortWait)

	u := upgrader.New(s.state.Upgrader(), s.machine.Tag(), s.DataDir())
	err = u.Stop()
	c.Assert(err, gc.DeepEquals, &upgrader.UpgradeReadyError{
		AgentName: s.machine.Tag(),
		OldTools:  oldTools,
		NewTools:  newTools,
		DataDir:   s.DataDir(),
	})
	foundTools, err := tools.ReadTools(s.DataDir(), newTools.Version)
	c.Assert(err, gc.IsNil)
	c.Assert(foundTools, gc.DeepEquals, newTools)
}
Example #5
0
func (u *Upgrader) loop() error {
	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.
		logger.Warningf("cannot read current tools: %v", err)
		currentTools = &tools.Tools{
			Version: version.Current,
		}
	}
	err = u.st.SetTools(u.tag, currentTools)
	if err != nil {
		return err
	}
	versionWatcher, err := u.st.WatchAPIVersion(u.tag)
	if err != nil {
		return err
	}
	changes := versionWatcher.Changes()
	defer watcher.Stop(versionWatcher, &u.tomb)
	var retry <-chan time.Time
	// We don't read on the dying channel until we have received the
	// initial event from the API version watcher, thus ensuring
	// that we attempt an upgrade even if other workers are dying
	// all around us.
	var dying <-chan struct{}
	var wantTools *tools.Tools
	for {
		select {
		case _, ok := <-changes:
			if !ok {
				return watcher.MustErr(versionWatcher)
			}
			wantTools, err = u.st.Tools(u.tag)
			if err != nil {
				return err
			}
			logger.Infof("required tools: %v", wantTools.Version)
			dying = u.tomb.Dying()
		case <-retry:
		case <-dying:
			return nil
		}
		if wantTools.Version.Number != currentTools.Version.Number {
			logger.Infof("upgrade required from %v to %v", currentTools.Version, wantTools.Version)
			// The worker cannot be stopped while we're downloading
			// the tools - this means that even if the API is going down
			// repeatedly (causing the agent to be stopped), as long
			// as we have got as far as this, we will still be able to
			// upgrade the agent.
			err := u.fetchTools(wantTools)
			if err == nil {
				return &UpgradeReadyError{
					OldTools:  currentTools,
					NewTools:  wantTools,
					AgentName: u.tag,
					DataDir:   u.dataDir,
				}
			}
			logger.Errorf("failed to fetch tools from %q: %v", wantTools.URL, err)
			retry = retryAfter()
		}
	}
}
Example #6
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")
}