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 }
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.*") }
// 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) }
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) }
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() } } }
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") }