Example #1
0
// startBootstrapInstance starts the bootstrap instance for this environment.
func (env *azureEnviron) startBootstrapInstance(cons constraints.Value) (instance.Instance, error) {
	// The bootstrap instance gets machine id "0".  This is not related to
	// instance ids or anything in Azure.  Juju assigns the machine ID.
	const machineID = "0"

	// Create an empty bootstrap state file so we can get its URL.
	// It will be updated with the instance id and hardware characteristics after the
	// bootstrap instance is started.
	err := env.Storage().Put(environs.StateFile, strings.NewReader(""), 0)
	if err != nil {
		return nil, fmt.Errorf("cannot create bootstrap state file: %v", err)
	}
	stateFileURL, err := env.Storage().URL(environs.StateFile)
	if err != nil {
		return nil, fmt.Errorf("cannot get URL for bootstrap state file: %v", err)
	}
	machineConfig := environs.NewBootstrapMachineConfig(machineID, stateFileURL)

	logger.Debugf("bootstrapping environment %q", env.Name())
	possibleTools, err := environs.FindBootstrapTools(env, cons)
	if err != nil {
		return nil, err
	}
	inst, err := env.internalStartInstance(cons, possibleTools, machineConfig)
	if err != nil {
		return nil, fmt.Errorf("cannot start bootstrap instance: %v", err)
	}
	return inst, nil
}
Example #2
0
// startBootstrapNode starts the juju bootstrap node for this environment.
func (env *maasEnviron) startBootstrapNode(cons constraints.Value) (instance.Instance, error) {
	// The bootstrap instance gets machine id "0".  This is not related to
	// instance ids or MAAS system ids.  Juju assigns the machine ID.
	const machineID = "0"

	// Create an empty bootstrap state file so we can get its URL.
	// If will be updated with the instance id and hardware characteristics
	// after the bootstrap instance is started.
	reader := strings.NewReader("")
	err := env.Storage().Put(environs.StateFile, reader, int64(0))
	if err != nil {
		return nil, fmt.Errorf("cannot create bootstrap state file: %v", err)
	}
	stateFileURL, err := env.Storage().URL(environs.StateFile)
	if err != nil {
		return nil, fmt.Errorf("cannot create bootstrap state file: %v", err)
	}

	logger.Debugf("bootstrapping environment %q", env.Name())
	possibleTools, err := environs.FindBootstrapTools(env, cons)
	if err != nil {
		return nil, err
	}
	err = environs.CheckToolsSeries(possibleTools, env.Config().DefaultSeries())
	if err != nil {
		return nil, err
	}

	machineConfig := environs.NewBootstrapMachineConfig(machineID, stateFileURL)
	inst, err := env.internalStartInstance(cons, possibleTools, machineConfig)
	if err != nil {
		return nil, fmt.Errorf("cannot start bootstrap instance: %v", err)
	}
	return inst, nil
}
Example #3
0
func (e *environ) Bootstrap(cons constraints.Value) 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")
	}

	possibleTools, err := environs.FindBootstrapTools(e, cons)
	if err != nil {
		return err
	}
	log.Infof("environs/dummy: would pick tools from %s", possibleTools)
	cfg, err := environs.BootstrapConfig(e.Config())
	if err != nil {
		return fmt.Errorf("cannot make bootstrap config: %v", err)
	}

	e.state.mu.Lock()
	defer e.state.mu.Unlock()
	if e.state.bootstrapped {
		return fmt.Errorf("environment is already bootstrapped")
	}
	if e.ecfg().stateServer() {
		// TODO(rog) factor out relevant code from cmd/jujud/bootstrap.go
		// so that we can call it here.

		info := stateInfo()
		st, err := state.Initialize(info, cfg, state.DefaultDialOpts())
		if err != nil {
			panic(err)
		}
		if err := st.SetEnvironConstraints(cons); err != nil {
			panic(err)
		}
		if err := st.SetAdminMongoPassword(utils.PasswordHash(password)); err != nil {
			panic(err)
		}
		_, err = st.AddUser("admin", password)
		if err != nil {
			panic(err)
		}
		e.state.apiServer, err = apiserver.NewServer(st, "localhost:0", []byte(testing.ServerCert), []byte(testing.ServerKey))
		if err != nil {
			panic(err)
		}
		e.state.apiState = st
	}
	e.state.bootstrapped = true
	e.state.ops <- OpBootstrap{Env: e.state.name, Constraints: cons}
	return nil
}
Example #4
0
// TODO(bug 1199847): This work can be shared between providers.
func (e *environ) Bootstrap(cons constraints.Value) error {
	// The bootstrap instance gets machine id "0".  This is not related
	// to instance ids.  Juju assigns the machine ID.
	const machineID = "0"
	log.Infof("environs/openstack: bootstrapping environment %q", e.name)

	if err := environs.VerifyBootstrapInit(e, shortAttempt); err != nil {
		return err
	}

	possibleTools, err := environs.FindBootstrapTools(e, cons)
	if err != nil {
		return err
	}
	err = environs.CheckToolsSeries(possibleTools, e.Config().DefaultSeries())
	if err != nil {
		return err
	}
	// The client's authentication may have been reset by FindBootstrapTools() if the agent-version
	// attribute was updated so we need to re-authenticate. This will be a no-op if already authenticated.
	// An authenticated client is needed for the URL() call below.
	err = e.client.Authenticate()
	if err != nil {
		return err
	}
	stateFileURL, err := environs.CreateStateFile(e.Storage())
	if err != nil {
		return err
	}

	machineConfig := environs.NewBootstrapMachineConfig(machineID, stateFileURL)

	// TODO(wallyworld) - save bootstrap machine metadata
	inst, characteristics, err := e.internalStartInstance(cons, possibleTools, machineConfig)
	if err != nil {
		return fmt.Errorf("cannot start bootstrap instance: %v", err)
	}
	err = environs.SaveState(e.Storage(), &environs.BootstrapState{
		StateInstances:  []instance.Id{inst.Id()},
		Characteristics: []instance.HardwareCharacteristics{*characteristics},
	})
	if err != nil {
		// ignore error on StopInstance because the previous error is
		// more important.
		e.StopInstances([]instance.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 Swift to make sure that only one succeeds.
	// Perhaps consider using SimpleDB for state storage
	// which would enable that possibility.

	return nil
}
Example #5
0
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
}
Example #6
0
// ensureToolsAvailability verifies the tools are available. If no tools are
// found, it will automatically synchronize them.
func (c *BootstrapCommand) ensureToolsAvailability(env environs.Environ, ctx *cmd.Context) error {
	// Capture possible logging while syncing and write it on the screen.
	loggo.RegisterWriter("bootstrap", sync.NewSyncLogWriter(ctx.Stdout, ctx.Stderr), loggo.INFO)
	defer loggo.RemoveWriter("bootstrap")

	// Try to find bootstrap tools.
	_, err := environs.FindBootstrapTools(env, c.Constraints)
	if errors.IsNotFoundError(err) {
		// Not tools available, so synchronize.
		sctx := &sync.SyncContext{
			EnvName: c.EnvName,
			Source:  c.Source,
		}
		if err = syncTools(sctx); err != nil {
			return err
		}
		// Synchronization done, try again.
		_, err = environs.FindBootstrapTools(env, c.Constraints)
	} else if err != nil {
		return err
	}
	return err
}
Example #7
0
// TODO(bug 1199847): Much of this work can be shared between providers.
func (e *environ) Bootstrap(cons constraints.Value) error {
	// The bootstrap instance gets machine id "0".  This is not related to
	// instance ids.  Juju assigns the machine ID.
	const machineID = "0"
	log.Infof("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.
	if err := environs.VerifyBootstrapInit(e, shortAttempt); err != nil {
		return err
	}

	possibleTools, err := environs.FindBootstrapTools(e, cons)
	if err != nil {
		return err
	}
	err = environs.CheckToolsSeries(possibleTools, e.Config().DefaultSeries())
	if err != nil {
		return err
	}
	stateFileURL, err := e.Storage().URL(environs.StateFile)
	if err != nil {
		return fmt.Errorf("cannot create bootstrap state file: %v", err)
	}

	machineConfig := environs.NewBootstrapMachineConfig(machineID, stateFileURL)

	// TODO(wallyworld) - save bootstrap machine metadata
	inst, characteristics, err := e.internalStartInstance(cons, possibleTools, machineConfig)
	if err != nil {
		return fmt.Errorf("cannot start bootstrap instance: %v", err)
	}
	err = environs.SaveState(e.Storage(), &environs.BootstrapState{
		StateInstances:  []instance.Id{inst.Id()},
		Characteristics: []instance.HardwareCharacteristics{*characteristics},
	})
	if err != nil {
		// ignore error on StopInstance because the previous error is
		// more important.
		e.StopInstances([]instance.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
}
Example #8
0
func (s *ToolsSuite) TestFindBootstrapTools(c *C) {
	for i, test := range findBootstrapToolsTests {
		c.Logf("\ntest %d: %s", i, test.info)
		attrs := map[string]interface{}{
			"development":    test.development,
			"default-series": test.defaultSeries,
		}
		if test.agentVersion != version.Zero {
			attrs["agent-version"] = test.agentVersion.String()
		}
		s.Reset(c, attrs)
		version.Current = test.cliVersion
		available := s.uploadPrivate(c, test.available...)
		if len(available) > 0 {
			// These should never be chosen.
			s.uploadPublic(c, vAll...)
		}

		cons := constraints.MustParse(test.constraints)
		actual, err := environs.FindBootstrapTools(s.env, cons)
		if test.err != nil {
			if len(actual) > 0 {
				c.Logf(actual.String())
			}
			c.Check(err, DeepEquals, &errors.NotFoundError{test.err, ""})
			continue
		}
		expect := map[version.Binary]string{}
		unique := map[version.Number]bool{}
		for _, expected := range test.expect {
			expect[expected] = available[expected]
			unique[expected.Number] = true
		}
		c.Check(actual.URLs(), DeepEquals, expect)
		for expectAgentVersion := range unique {
			agentVersion, ok := s.env.Config().AgentVersion()
			c.Check(ok, Equals, true)
			c.Check(agentVersion, Equals, expectAgentVersion)
		}
	}
}