Example #1
0
// startInstance is the internal version of StartInstance, used by Bootstrap
// as well as via StartInstance itself.
func (e *environ) startInstance(scfg *startInstanceParams) (environs.Instance, error) {
	if scfg.tools == nil {
		var err error
		flags := environs.HighestVersion | environs.CompatVersion
		scfg.tools, err = environs.FindTools(e, version.Current, flags)
		if err != nil {
			return nil, err
		}
	}
	log.Printf("environs/ec2: starting machine %s in %q running tools version %q from %q", scfg.machineId, e.name, scfg.tools.Binary, scfg.tools.URL)
	spec, err := findInstanceSpec(&instanceConstraint{
		series: scfg.tools.Series,
		arch:   scfg.tools.Arch,
		region: e.ecfg().region(),
	})
	if err != nil {
		return nil, fmt.Errorf("cannot find image satisfying constraints: %v", err)
	}
	// TODO quick sanity check that we can access the tools URL?
	userData, err := e.userData(scfg)
	if err != nil {
		return nil, fmt.Errorf("cannot make user data: %v", err)
	}
	groups, err := e.setUpGroups(scfg.machineId)
	if err != nil {
		return nil, fmt.Errorf("cannot set up groups: %v", err)
	}
	var instances *ec2.RunInstancesResp

	for a := shortAttempt.Start(); a.Next(); {
		instances, err = e.ec2().RunInstances(&ec2.RunInstances{
			ImageId:        spec.imageId,
			MinCount:       1,
			MaxCount:       1,
			UserData:       userData,
			InstanceType:   "m1.small",
			SecurityGroups: groups,
		})
		if err == nil || ec2ErrCode(err) != "InvalidGroup.NotFound" {
			break
		}
	}
	if err != nil {
		return nil, fmt.Errorf("cannot run instances: %v", err)
	}
	if len(instances.Instances) != 1 {
		return nil, fmt.Errorf("expected 1 started instance, got %d", len(instances.Instances))
	}
	inst := &instance{e, &instances.Instances[0]}
	log.Printf("environs/ec2: started instance %q", inst.Id())
	return inst, nil
}
Example #2
0
func (e *environ) Bootstrap(uploadTools bool, cert, key []byte) error {
	defer delay()
	if err := e.checkBroken("Bootstrap"); err != nil {
		return err
	}
	password := e.Config().AdminSecret()
	if password == "" {
		return fmt.Errorf("admin-secret is required for bootstrap")
	}
	if _, ok := e.Config().CACert(); !ok {
		return fmt.Errorf("no CA certificate in environment configuration")
	}
	var tools *state.Tools
	var err error
	if uploadTools {
		tools, err = environs.PutTools(e.Storage(), nil)
		if err != nil {
			return err
		}
	} else {
		flags := environs.HighestVersion | environs.CompatVersion
		tools, err = environs.FindTools(e, version.Current, flags)
		if err != nil {
			return err
		}
	}
	e.state.mu.Lock()
	defer e.state.mu.Unlock()
	e.state.ops <- OpBootstrap{Env: e.state.name}
	if e.state.bootstrapped {
		return fmt.Errorf("environment is already bootstrapped")
	}
	if e.ecfg().stateServer() {
		info := stateInfo()
		cfg, err := environs.BootstrapConfig(&providerInstance, e.ecfg().Config, tools)
		if err != nil {
			return fmt.Errorf("cannot make bootstrap config: %v", err)
		}
		st, err := state.Initialize(info, cfg)
		if err != nil {
			panic(err)
		}
		if err := st.SetAdminMongoPassword(trivial.PasswordHash(password)); err != nil {
			return err
		}
		if err := st.Close(); err != nil {
			panic(err)
		}
	}
	e.state.bootstrapped = true
	return nil
}
Example #3
0
// startInstance is the internal version of StartInstance, used by Bootstrap
// as well as via StartInstance itself.
func (e *environ) startInstance(scfg *startInstanceParams) (environs.Instance, error) {
	if scfg.tools == nil {
		var err error
		flags := environs.HighestVersion | environs.CompatVersion
		scfg.tools, err = environs.FindTools(e, version.Current, flags)
		if err != nil {
			return nil, err
		}
	}

	log.Printf("environs/openstack: starting machine %s in %q running tools version %q from %q",
		scfg.machineId, e.name, scfg.tools.Binary, scfg.tools.URL)
	// TODO(wallyworld) - implement spec lookup
	if strings.Contains(scfg.tools.Series, "unknown") || strings.Contains(scfg.tools.Series, "unknown") {
		return nil, fmt.Errorf("cannot find image for unknown series or architecture")
	}
	userData, err := e.userData(scfg)
	if err != nil {
		return nil, fmt.Errorf("cannot make user data: %v", err)
	}
	log.Debugf("environs/openstack: openstack user data: %q", userData)
	groups, err := e.setUpGroups(scfg.machineId)
	if err != nil {
		return nil, fmt.Errorf("cannot set up groups: %v", err)
	}
	var groupNames = make([]nova.SecurityGroupName, len(groups))
	for i, g := range groups {
		groupNames[i] = nova.SecurityGroupName{g.Name}
	}

	var server *nova.Entity
	for a := shortAttempt.Start(); a.Next(); {
		server, err = e.nova().RunServer(nova.RunServerOpts{
			Name: state.MachineEntityName(scfg.machineId),
			// TODO(wallyworld) - do not use hard coded image
			FlavorId:           defaultFlavorId,
			ImageId:            defaultImageId,
			UserData:           userData,
			SecurityGroupNames: groupNames,
		})
		if err == nil || !gooseerrors.IsNotFound(err) {
			break
		}
	}
	if err != nil {
		return nil, fmt.Errorf("cannot run instance: %v", err)
	}
	inst := &instance{e, server}
	log.Printf("environs/openstack: started instance %q", inst.Id())
	return inst, nil
}
Example #4
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)
}
Example #5
0
func (t *ToolsSuite) TestFindTools(c *C) {
	for i, tt := range findToolsTests {
		c.Logf("test %d", i)
		putNames(c, t.env, tt.contents, tt.publicContents)
		vers := version.Binary{
			Number: tt.version,
			Series: version.Current.Series,
			Arch:   version.Current.Arch,
		}
		tools, err := environs.FindTools(t.env, vers, tt.flags)
		if tt.err != "" {
			c.Assert(err, ErrorMatches, tt.err)
		} else {
			c.Assert(err, IsNil)
			assertURLContents(c, tools.URL, tt.expect)
		}
		t.env.Destroy(nil)
	}
}
Example #6
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")
}
Example #7
0
func (e *environ) Bootstrap(uploadTools bool, cert, key []byte) error {
	password := e.Config().AdminSecret()
	if password == "" {
		return fmt.Errorf("admin-secret is required for bootstrap")
	}
	log.Printf("environs/ec2: bootstrapping environment %q", e.name)
	// If the state file exists, it might actually have just been
	// removed by Destroy, and eventual consistency has not caught
	// up yet, so we retry to verify if that is happening.
	var err error
	for a := shortAttempt.Start(); a.Next(); {
		_, err = e.loadState()
		if err != nil {
			break
		}
	}
	if err == nil {
		return fmt.Errorf("environment is already bootstrapped")
	}
	if _, notFound := err.(*environs.NotFoundError); !notFound {
		return fmt.Errorf("cannot query old bootstrap state: %v", err)
	}
	var tools *state.Tools
	if uploadTools {
		tools, err = environs.PutTools(e.Storage(), nil)
		if err != nil {
			return fmt.Errorf("cannot upload tools: %v", err)
		}
	} else {
		flags := environs.HighestVersion | environs.CompatVersion
		v := version.Current
		v.Series = e.Config().DefaultSeries()
		tools, err = environs.FindTools(e, v, flags)
		if err != nil {
			return fmt.Errorf("cannot find tools: %v", err)
		}
	}
	config, err := environs.BootstrapConfig(providerInstance, e.Config(), tools)
	if err != nil {
		return fmt.Errorf("unable to determine inital configuration: %v", err)
	}
	caCert, hasCert := e.Config().CACert()
	if !hasCert {
		return fmt.Errorf("no CA certificate in environment configuration")
	}
	info := &state.Info{
		Password: trivial.PasswordHash(password),
		CACert:   caCert,
	}
	inst, err := e.startInstance(&startInstanceParams{
		machineId:       "0",
		info:            info,
		tools:           tools,
		stateServer:     true,
		config:          config,
		stateServerCert: cert,
		stateServerKey:  key,
	})
	if err != nil {
		return fmt.Errorf("cannot start bootstrap instance: %v", err)
	}
	err = e.saveState(&bootstrapState{
		StateInstances: []state.InstanceId{inst.Id()},
	})
	if err != nil {
		// ignore error on StopInstance because the previous error is
		// more important.
		e.StopInstances([]environs.Instance{inst})
		return fmt.Errorf("cannot save state: %v", err)
	}
	// TODO make safe in the case of racing Bootstraps
	// If two Bootstraps are called concurrently, there's
	// no way to use S3 to make sure that only one succeeds.
	// Perhaps consider using SimpleDB for state storage
	// which would enable that possibility.

	return nil
}