예제 #1
0
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)
}
예제 #2
0
// 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
}
예제 #3
0
// 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)
}
예제 #4
0
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{})
	}
}
예제 #5
0
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})
}
예제 #6
0
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)
}
예제 #7
0
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
		}
	}
}
예제 #8
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")
}