func (t *ToolsSuite) TestUnpackToolsContents(c *C) { files := []*testing.TarFile{ testing.NewTarFile("bar", 0755, "bar contents"), testing.NewTarFile("foo", 0755, "foo contents"), } tools := &state.Tools{ URL: "http://foo/bar", Binary: version.MustParseBinary("1.2.3-foo-bar"), } err := environs.UnpackTools(t.dataDir, tools, bytes.NewReader(testing.TarGz(files...))) c.Assert(err, IsNil) assertDirNames(c, t.toolsDir(), []string{"1.2.3-foo-bar"}) t.assertToolsContents(c, tools, files) // Try to unpack the same version of tools again - it should succeed, // leaving the original version around. tools2 := &state.Tools{ URL: "http://arble", Binary: version.MustParseBinary("1.2.3-foo-bar"), } files2 := []*testing.TarFile{ testing.NewTarFile("bar", 0755, "bar2 contents"), testing.NewTarFile("x", 0755, "x contents"), } err = environs.UnpackTools(t.dataDir, tools2, bytes.NewReader(testing.TarGz(files2...))) c.Assert(err, IsNil) assertDirNames(c, t.toolsDir(), []string{"1.2.3-foo-bar"}) t.assertToolsContents(c, tools, files) }
// 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) *state.Tools { // Set up the current version and tools. version.Current = vers tools := s.uploadTools(c, vers) resp, err := http.Get(tools.URL) c.Assert(err, IsNil) defer resp.Body.Close() err = environs.UnpackTools(s.DataDir(), tools, resp.Body) c.Assert(err, IsNil) return tools }
// getTools downloads and unpacks the given tools. func getTools(dataDir string, tools *state.Tools) error { resp, err := http.Get(tools.URL) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return fmt.Errorf("bad http status: %v", resp.Status) } return environs.UnpackTools(dataDir, tools, resp.Body) }
func (t *ToolsSuite) TestUnpackToolsBadData(c *C) { for i, test := range unpackToolsBadDataTests { c.Logf("test %d", i) tools := &state.Tools{ URL: "http://foo/bar", Binary: version.MustParseBinary("1.2.3-foo-bar"), } err := environs.UnpackTools(t.dataDir, tools, bytes.NewReader(test.data)) c.Assert(err, ErrorMatches, test.err) assertDirNames(c, t.toolsDir(), []string{}) } }
func (t *ToolsSuite) TestChangeAgentTools(c *C) { files := []*testing.TarFile{ testing.NewTarFile("jujuc", 0755, "juju executable"), testing.NewTarFile("jujud", 0755, "jujuc executable"), } tools := &state.Tools{ URL: "http://foo/bar1", Binary: version.MustParseBinary("1.2.3-foo-bar"), } err := environs.UnpackTools(t.dataDir, tools, bytes.NewReader(testing.TarGz(files...))) c.Assert(err, IsNil) gotTools, err := environs.ChangeAgentTools(t.dataDir, "testagent", tools.Binary) c.Assert(err, IsNil) c.Assert(*gotTools, Equals, *tools) assertDirNames(c, t.toolsDir(), []string{"1.2.3-foo-bar", "testagent"}) assertDirNames(c, environs.AgentToolsDir(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 := &state.Tools{ URL: "http://foo/bar2", Binary: version.MustParseBinary("1.2.4-foo-bar"), } err = environs.UnpackTools(t.dataDir, tools2, bytes.NewReader(testing.TarGz(files2...))) c.Assert(err, IsNil) gotTools, err = environs.ChangeAgentTools(t.dataDir, "testagent", tools2.Binary) c.Assert(err, IsNil) c.Assert(*gotTools, Equals, *tools2) assertDirNames(c, t.toolsDir(), []string{"1.2.3-foo-bar", "1.2.4-foo-bar", "testagent"}) assertDirNames(c, environs.AgentToolsDir(t.dataDir, "testagent"), []string{"foo", "bar", urlFile}) }
func (*BootstrapSuite) TestBootstrapCommand(c *C) { defer makeFakeHome(c, "brokenenv").restore() err := ioutil.WriteFile(homePath(".juju", "environments.yaml"), []byte(envConfig), 0666) c.Assert(err, IsNil) // normal bootstrap opc, errc := runCommand(new(BootstrapCommand)) c.Check(<-errc, IsNil) c.Check((<-opc).(dummy.OpBootstrap).Env, Equals, "peckham") // Check that the CA certificate and key have been automatically generated // for the environment. _, err = os.Stat(homePath(".juju", "peckham-cert.pem")) c.Assert(err, IsNil) _, err = os.Stat(homePath(".juju", "peckham-private-key.pem")) c.Assert(err, IsNil) // bootstrap with tool uploading - checking that a file // is uploaded should be sufficient, as the detailed semantics // of UploadTools are tested in environs. opc, errc = runCommand(new(BootstrapCommand), "--upload-tools") c.Check(<-errc, IsNil) c.Check((<-opc).(dummy.OpPutFile).Env, Equals, "peckham") c.Check((<-opc).(dummy.OpBootstrap).Env, Equals, "peckham") envs, err := environs.ReadEnvirons("") c.Assert(err, IsNil) env, err := envs.Open("peckham") c.Assert(err, IsNil) tools, err := environs.FindTools(env, version.Current, environs.CompatVersion) c.Assert(err, IsNil) resp, err := http.Get(tools.URL) c.Assert(err, IsNil) defer resp.Body.Close() err = environs.UnpackTools(c.MkDir(), tools, resp.Body) c.Assert(err, IsNil) // bootstrap with broken environment opc, errc = runCommand(new(BootstrapCommand), "-e", "brokenenv") c.Check(<-errc, ErrorMatches, "dummy.Bootstrap is broken") c.Check(<-opc, IsNil) }
func (s *UpgraderSuite) TestUpgrader(c *C) { currentTools := s.primeTools(c, version.MustParseBinary("2.0.0-foo-bar")) // Remove the tools from the storage so that we're sure that the // uploader isn't trying to fetch them. resp, err := http.Get(currentTools.URL) c.Assert(err, IsNil) err = environs.UnpackTools(s.DataDir(), currentTools, resp.Body) c.Assert(err, IsNil) s.removeVersion(c, currentTools.Binary) var ( u *Upgrader upgraderDone <-chan error ) defer func() { if u != nil { c.Assert(u.Stop(), IsNil) } }() uploaded := make(map[version.Number]*state.Tools) for i, test := range upgraderTests { c.Logf("%d. %s; current version: %v", i, test.about, version.Current) for _, v := range test.upload { vers := version.Current vers.Number = version.MustParse(v) tools := s.uploadTools(c, vers) uploaded[vers.Number] = tools } if test.current != "" { version.Current = version.MustParseBinary(test.current) currentTools, err = environs.ReadTools(s.DataDir(), version.Current) c.Assert(err, IsNil) } if u == nil { u = s.startUpgrader(c, currentTools) } if test.propose != "" { s.proposeVersion(c, version.MustParse(test.propose), test.devVersion) s.State.StartSync() } if test.upgradeTo == "" { s.State.StartSync() assertNothingHappens(c, upgraderDone) } else { ug := waitDeath(c, u) tools := uploaded[version.MustParse(test.upgradeTo)] c.Check(ug.NewTools, DeepEquals, tools) c.Check(ug.OldTools.Binary, Equals, version.Current) c.Check(ug.DataDir, Equals, s.DataDir()) c.Check(ug.AgentName, Equals, "testagent") // Check that the upgraded version was really downloaded. data, err := ioutil.ReadFile(filepath.Join(environs.ToolsDir(s.DataDir(), tools.Binary), "jujud")) c.Assert(err, IsNil) c.Assert(string(data), Equals, "jujud contents "+tools.Binary.String()) u, upgraderDone = nil, nil currentTools = tools version.Current = tools.Binary } } }
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") }