func (t *ToolsSuite) TestUnpackToolsContents(c *gc.C) { files := []*testing.TarFile{ testing.NewTarFile("bar", 0755, "bar contents"), testing.NewTarFile("foo", 0755, "foo contents"), } testTools := &tools.Tools{ URL: "http://foo/bar", Version: version.MustParseBinary("1.2.3-foo-bar"), } err := tools.UnpackTools(t.dataDir, testTools, bytes.NewReader(testing.TarGz(files...))) c.Assert(err, gc.IsNil) assertDirNames(c, t.toolsDir(), []string{"1.2.3-foo-bar"}) t.assertToolsContents(c, testTools, files) // Try to unpack the same version of tools again - it should succeed, // leaving the original version around. tools2 := &tools.Tools{ URL: "http://arble", Version: version.MustParseBinary("1.2.3-foo-bar"), } files2 := []*testing.TarFile{ testing.NewTarFile("bar", 0755, "bar2 contents"), testing.NewTarFile("x", 0755, "x contents"), } err = tools.UnpackTools(t.dataDir, tools2, bytes.NewReader(testing.TarGz(files2...))) c.Assert(err, gc.IsNil) assertDirNames(c, t.toolsDir(), []string{"1.2.3-foo-bar"}) t.assertToolsContents(c, testTools, files) }
func (env *localEnviron) setupLocalMachineAgent(cons constraints.Value) error { dataDir := env.config.rootDir() toolList, err := environs.FindBootstrapTools(env, cons) if err != nil { return err } // ensure we have at least one valid tools if len(toolList) == 0 { return fmt.Errorf("No bootstrap tools found") } // unpack the first tools into the agent dir. agentTools := toolList[0] logger.Debugf("tools: %#v", agentTools) // brutally abuse our knowledge of storage to directly open the file toolsUrl, err := url.Parse(agentTools.URL) toolsLocation := filepath.Join(env.config.storageDir(), toolsUrl.Path) logger.Infof("tools location: %v", toolsLocation) toolsFile, err := os.Open(toolsLocation) defer toolsFile.Close() // Again, brutally abuse our knowledge here. // The tools that FindBootstrapTools has returned us are based on the // default series in the config. However we are running potentially on a // different series. When the machine agent is started, it will be // looking based on the current series, so we need to override the series // returned in the tools to be the current series. agentTools.Version.Series = version.CurrentSeries() err = tools.UnpackTools(dataDir, agentTools, toolsFile) machineId := "0" // Always machine 0 tag := names.MachineTag(machineId) toolsDir := tools.SharedToolsDir(dataDir, agentTools.Version) logDir := env.config.logDir() logConfig := "--debug" // TODO(thumper): specify loggo config machineEnvironment := map[string]string{ "USER": env.config.user, "HOME": os.Getenv("HOME"), osenv.JujuProviderType: env.config.Type(), osenv.JujuStorageDir: env.config.storageDir(), osenv.JujuStorageAddr: env.config.storageAddr(), osenv.JujuSharedStorageDir: env.config.sharedStorageDir(), osenv.JujuSharedStorageAddr: env.config.sharedStorageAddr(), } agent := upstart.MachineAgentUpstartService( env.machineAgentServiceName(), toolsDir, dataDir, logDir, tag, machineId, logConfig, machineEnvironment) agent.InitDir = upstartScriptLocation logger.Infof("installing service %s to %s", env.machineAgentServiceName(), agent.InitDir) if err := agent.Install(); err != nil { logger.Errorf("could not install machine agent service: %v", err) return err } return nil }
// primeTools sets up the current version of the tools to vers and // makes sure that they're available JujuConnSuite's DataDir. func (s *agentSuite) primeTools(c *C, vers version.Binary) *tools.Tools { err := os.RemoveAll(filepath.Join(s.DataDir(), "tools")) c.Assert(err, IsNil) version.Current = vers agentTools := s.uploadTools(c, vers) resp, err := http.Get(agentTools.URL) c.Assert(err, IsNil) defer resp.Body.Close() err = tools.UnpackTools(s.DataDir(), agentTools, resp.Body) c.Assert(err, IsNil) return agentTools }
func (t *ToolsSuite) TestUnpackToolsBadData(c *gc.C) { for i, test := range unpackToolsBadDataTests { c.Logf("test %d", i) testTools := &tools.Tools{ URL: "http://foo/bar", Version: version.MustParseBinary("1.2.3-foo-bar"), } err := tools.UnpackTools(t.dataDir, testTools, bytes.NewReader(test.data)) c.Assert(err, gc.ErrorMatches, test.err) assertDirNames(c, t.toolsDir(), []string{}) } }
func (t *ToolsSuite) TestChangeAgentTools(c *gc.C) { files := []*testing.TarFile{ testing.NewTarFile("jujuc", 0755, "juju executable"), testing.NewTarFile("jujud", 0755, "jujuc executable"), } testTools := &tools.Tools{ URL: "http://foo/bar1", Version: version.MustParseBinary("1.2.3-foo-bar"), } err := tools.UnpackTools(t.dataDir, testTools, bytes.NewReader(testing.TarGz(files...))) c.Assert(err, gc.IsNil) gotTools, err := tools.ChangeAgentTools(t.dataDir, "testagent", testTools.Version) c.Assert(err, gc.IsNil) c.Assert(*gotTools, gc.Equals, *testTools) assertDirNames(c, t.toolsDir(), []string{"1.2.3-foo-bar", "testagent"}) assertDirNames(c, tools.ToolsDir(t.dataDir, "testagent"), []string{"jujuc", "jujud", urlFile}) // Upgrade again to check that the link replacement logic works ok. files2 := []*testing.TarFile{ testing.NewTarFile("foo", 0755, "foo content"), testing.NewTarFile("bar", 0755, "bar content"), } tools2 := &tools.Tools{ URL: "http://foo/bar2", Version: version.MustParseBinary("1.2.4-foo-bar"), } err = tools.UnpackTools(t.dataDir, tools2, bytes.NewReader(testing.TarGz(files2...))) c.Assert(err, gc.IsNil) gotTools, err = tools.ChangeAgentTools(t.dataDir, "testagent", tools2.Version) c.Assert(err, gc.IsNil) c.Assert(*gotTools, gc.Equals, *tools2) assertDirNames(c, t.toolsDir(), []string{"1.2.3-foo-bar", "1.2.4-foo-bar", "testagent"}) assertDirNames(c, tools.ToolsDir(t.dataDir, "testagent"), []string{"foo", "bar", urlFile}) }
func (u *Upgrader) fetchTools(agentTools *tools.Tools) error { logger.Infof("fetching tools from %q", agentTools.URL) resp, err := http.Get(agentTools.URL) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return fmt.Errorf("bad HTTP response: %v", resp.Status) } err = tools.UnpackTools(u.dataDir, agentTools, resp.Body) if err != nil { return fmt.Errorf("cannot unpack tools: %v", err) } return nil }
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") }