示例#1
0
// Build the setup script that will need to be run on the specified host.
func (init *HostInit) expandScript(s string) (string, error) {
	// replace expansions in the script
	exp := command.NewExpansions(init.Settings.Expansions)
	script, err := exp.ExpandString(s)
	if err != nil {
		return "", fmt.Errorf("expansions error: %v", err)
	}
	return script, err
}
示例#2
0
// Build the setup script that will need to be run on the specified host.
func (init *HostInit) buildSetupScript(h *host.Host) (string, error) {
	// replace expansions in the script
	exp := command.NewExpansions(init.Settings.Expansions)
	setupScript, err := exp.ExpandString(h.Distro.Setup)
	if err != nil {
		return "", fmt.Errorf("expansions error: %v", err)
	}
	return setupScript, err
}
示例#3
0
func TestExpandS3PutParams(t *testing.T) {

	Convey("With an s3 put command and a task config", t, func() {

		var cmd *S3PutCommand
		var conf *model.TaskConfig

		Convey("when expanding the command's params", func() {

			cmd = &S3PutCommand{}
			conf = &model.TaskConfig{
				Expansions: command.NewExpansions(map[string]string{}),
			}

			Convey("all appropriate values should be expanded, if they"+
				" contain expansions", func() {

				cmd.AwsKey = "${aws_key}"
				cmd.AwsSecret = "${aws_secret}"
				cmd.RemoteFile = "${remote_file}"
				cmd.Bucket = "${bucket}"
				cmd.ContentType = "${content_type}"
				cmd.DisplayName = "${display_name}"
				cmd.Visibility = "${visibility}"

				conf.Expansions.Update(
					map[string]string{
						"aws_key":      "key",
						"aws_secret":   "secret",
						"remote_file":  "remote",
						"bucket":       "bck",
						"content_type": "ct",
						"display_name": "file",
						"visibility":   artifact.Private,
					},
				)

				So(cmd.expandParams(conf), ShouldBeNil)
				So(cmd.AwsKey, ShouldEqual, "key")
				So(cmd.AwsSecret, ShouldEqual, "secret")
				So(cmd.RemoteFile, ShouldEqual, "remote")
				So(cmd.Bucket, ShouldEqual, "bck")
				So(cmd.ContentType, ShouldEqual, "ct")
				So(cmd.DisplayName, ShouldEqual, "file")
				So(cmd.Visibility, ShouldEqual, "private")

			})

		})

	})
}
示例#4
0
func populateExpansions(d *distro.Distro, bv *BuildVariant, t *Task) *command.Expansions {
	expansions := command.NewExpansions(map[string]string{})
	expansions.Put("execution", fmt.Sprintf("%v", t.Execution))
	expansions.Put("task_id", t.Id)
	expansions.Put("task_name", t.DisplayName)
	expansions.Put("build_id", t.BuildId)
	expansions.Put("build_variant", t.BuildVariant)
	expansions.Put("workdir", d.WorkDir)
	expansions.Put("revision", t.Revision)
	expansions.Put("project", t.Project)
	expansions.Put("branch_name", t.Project)
	for _, e := range d.Expansions {
		expansions.Put(e.Key, e.Value)
	}
	expansions.Update(bv.Expansions)
	return expansions
}
示例#5
0
func TestExpandS3GetParams(t *testing.T) {

	Convey("With an s3 get command and a task config", t, func() {

		var cmd *S3GetCommand
		var conf *model.TaskConfig

		Convey("when expanding the command's params", func() {

			cmd = &S3GetCommand{}
			conf = &model.TaskConfig{
				Expansions: command.NewExpansions(map[string]string{}),
			}

			Convey("all appropriate values should be expanded, if they"+
				" contain expansions", func() {

				cmd.AwsKey = "${aws_key}"
				cmd.AwsSecret = "${aws_secret}"
				cmd.RemoteFile = "${remote_file}"
				cmd.Bucket = "${bucket}"

				conf.Expansions.Update(
					map[string]string{
						"aws_key":     "key",
						"aws_secret":  "secret",
						"remote_file": "remote",
						"bucket":      "bck",
					},
				)

				So(cmd.expandParams(conf), ShouldBeNil)
				So(cmd.AwsKey, ShouldEqual, "key")
				So(cmd.AwsSecret, ShouldEqual, "secret")
				So(cmd.RemoteFile, ShouldEqual, "remote")
				So(cmd.Bucket, ShouldEqual, "bck")

			})

		})

	})
}
示例#6
0
func populateExpansions(d *distro.Distro, bv *BuildVariant, t *task.Task) *command.Expansions {
	expansions := command.NewExpansions(map[string]string{})
	expansions.Put("execution", fmt.Sprintf("%v", t.Execution))
	expansions.Put("version_id", t.Version)
	expansions.Put("task_id", t.Id)
	expansions.Put("task_name", t.DisplayName)
	expansions.Put("build_id", t.BuildId)
	expansions.Put("build_variant", t.BuildVariant)
	expansions.Put("workdir", d.WorkDir)
	expansions.Put("revision", t.Revision)
	expansions.Put("project", t.Project)
	expansions.Put("branch_name", t.Project)
	if t.Requester == evergreen.PatchVersionRequester {
		expansions.Put("is_patch", "true")
	}
	for _, e := range d.Expansions {
		expansions.Put(e.Key, e.Value)
	}
	expansions.Update(bv.Expansions)
	return expansions
}
示例#7
0
// Creates a copy of `current`, with instances of ${matrixParameterName} in
// top level string and slice of strings fields replaced by the value specified
// in matrixParameterValues
func expandBuildVariantMatrixParameters(project *Project, current BuildVariant,
	matrixParameterValues []MatrixParameterValue) (*BuildVariant, error) {
	// Create a new build variant with the same parameters
	newBv := current
	newBv.Expansions = make(map[string]string)

	// Make sure to copy over expansions
	for k, v := range current.Expansions {
		newBv.Expansions[k] = v
	}

	// Convert parameter value state into a map for use with expansions
	matrixParameterMap := make(map[string]string)
	for i, parameter := range project.BuildVariantMatrix.MatrixParameters {
		matrixParameterMap[parameter.Name] = matrixParameterValues[i].Value
	}
	matrixParameterExpansions := command.NewExpansions(matrixParameterMap)

	// Iterate over all fields
	numFields := reflect.TypeOf(newBv).NumField()
	for fieldIndex := 0; fieldIndex < numFields; fieldIndex++ {
		// Expand matrix parameters in top level string fields
		if reflect.TypeOf(newBv).Field(fieldIndex).Type.Kind() == reflect.String {
			val := reflect.ValueOf(&newBv).Elem().Field(fieldIndex).String()
			val, err := matrixParameterExpansions.ExpandString(val)
			if err != nil {
				return nil, err
			}
			reflect.ValueOf(&newBv).Elem().Field(fieldIndex).SetString(val)
		}

		// Expand matrix parameters in top level slices of strings
		if reflect.TypeOf(newBv).Field(fieldIndex).Type == reflect.SliceOf(reflect.TypeOf("")) {
			slice := reflect.ValueOf(&newBv).Elem().Field(fieldIndex)
			newSlice := []string{}
			for arrayIndex := 0; arrayIndex < slice.Len(); arrayIndex++ {
				// Expand matrix parameters for each individual element of the slice
				val := slice.Index(arrayIndex).String()
				val, err := matrixParameterExpansions.ExpandString(val)
				if err != nil {
					return nil, err
				}
				newSlice = append(newSlice, val)
			}

			reflect.ValueOf(&newBv).Elem().Field(fieldIndex).Set(reflect.ValueOf(newSlice))
		}
	}

	// First, attach all conditional expansions (i.e. expansions associated with
	// a given parameter value)
	for _, value := range matrixParameterValues {
		for k, v := range value.Expansions {
			newBv.Expansions[k] = v
		}
	}

	// Then, expand matrix parameters in all expansions
	for key, expansion := range newBv.Expansions {
		expansion, err := matrixParameterExpansions.ExpandString(expansion)
		if err != nil {
			return nil, err
		}
		newBv.Expansions[key] = expansion
	}

	// Build variant matrix parameter values are stored in the build variant
	buildVariantMatrixParameterValues := make(map[string]string)
	for i, matrixParameter := range project.BuildVariantMatrix.MatrixParameters {
		buildVariantMatrixParameterValues[matrixParameter.Name] =
			matrixParameterValues[i].Value
	}
	newBv.MatrixParameterValues = buildVariantMatrixParameterValues

	return &newBv, nil
}
示例#8
0
// CreateHost spawns a host with the given options.
func (sm Spawn) CreateHost(so Options) (*host.Host, error) {

	// load in the appropriate distro
	d, err := distro.FindOne(distro.ById(so.Distro))
	if err != nil {
		return nil, err
	}

	// get the appropriate cloud manager
	cloudManager, err := providers.GetCloudManager(d.Provider, sm.settings)
	if err != nil {
		return nil, err
	}

	// spawn the host
	h, err := cloudManager.SpawnInstance(d, so.UserName, true)
	if err != nil {
		return nil, err
	}

	// set the expiration time for the host
	expireTime := h.CreationTime.Add(DefaultExpiration)
	err = h.SetExpirationTime(expireTime)
	if err != nil {
		return h, evergreen.Logger.Errorf(slogger.ERROR,
			"error setting expiration on host %v: %v", h.Id, err)
	}

	// set the user data, if applicable
	if so.UserData != "" {
		err = h.SetUserData(so.UserData)
		if err != nil {
			return h, evergreen.Logger.Errorf(slogger.ERROR,
				"Failed setting userData on host %v: %v", h.Id, err)
		}
	}

	// create a hostinit to take care of setting up the host
	init := &hostinit.HostInit{
		Settings: sm.settings,
	}

	// for making sure the host doesn't take too long to spawn
	startTime := time.Now()

	// spin until the host is ready for its setup script to be run
	for {

		// make sure we haven't been spinning for too long
		if time.Now().Sub(startTime) > 15*time.Minute {
			if err := h.SetDecommissioned(); err != nil {
				evergreen.Logger.Logf(slogger.ERROR, "error decommissioning host %v: %v", h.Id, err)
			}
			return nil, fmt.Errorf("host took too long to come up")
		}

		time.Sleep(5000 * time.Millisecond)

		evergreen.Logger.Logf(slogger.INFO, "Checking if host %v is up and ready", h.Id)

		// see if the host is ready for its setup script to be run
		ready, err := init.IsHostReady(h)
		if err != nil {
			if err := h.SetDecommissioned(); err != nil {
				evergreen.Logger.Logf(slogger.ERROR, "error decommissioning host %v: %v", h.Id, err)
			}
			return nil, fmt.Errorf("error checking on host %v; decommissioning to save resources: %v",
				h.Id, err)
		}

		// if the host is ready, move on to running the setup script
		if ready {
			break
		}

	}

	evergreen.Logger.Logf(slogger.INFO, "Host %v is ready for its setup script to be run", h.Id)

	// add any extra user-specified data into the setup script
	if h.Distro.UserData.File != "" {
		userDataCmd := fmt.Sprintf("echo \"%v\" > %v\n",
			strings.Replace(so.UserData, "\"", "\\\"", -1), h.Distro.UserData.File)
		// prepend the setup script to add the userdata file
		if strings.HasPrefix(h.Distro.Setup, "#!") {
			firstLF := strings.Index(h.Distro.Setup, "\n")
			h.Distro.Setup = h.Distro.Setup[0:firstLF+1] + userDataCmd + h.Distro.Setup[firstLF+1:]
		} else {
			h.Distro.Setup = userDataCmd + h.Distro.Setup
		}
	}

	// modify the setup script to add the user's public key
	h.Distro.Setup += fmt.Sprintf("\necho \"\n%v\" >> ~%v/.ssh/authorized_keys\n",
		so.PublicKey, h.Distro.User)

	// replace expansions in the script
	exp := command.NewExpansions(init.Settings.Expansions)
	h.Distro.Setup, err = exp.ExpandString(h.Distro.Setup)
	if err != nil {
		return nil, fmt.Errorf("expansions error: %v", err)
	}

	// provision the host
	err = init.ProvisionHost(h)
	if err != nil {
		return nil, fmt.Errorf("error provisioning host %v: %v", h.Id, err)
	}

	return h, nil
}
示例#9
0
func TestExpandValues(t *testing.T) {

	Convey("When expanding struct values", t, func() {

		expansions := command.NewExpansions(
			map[string]string{
				"exp1": "val1",
			},
		)

		Convey("if the input value is not a pointer to a struct, an error"+
			" should be returned", func() {
			So(ExpandValues("hello", expansions), ShouldNotBeNil)
			So(ExpandValues(struct{}{}, expansions), ShouldNotBeNil)
			So(ExpandValues([]string{"hi"}, expansions), ShouldNotBeNil)
		})

		Convey("if any non-string fields are tagged as expandable, an error"+
			" should be returned", func() {

			type s struct {
				FieldOne string `plugin:"expand"`
				FieldTwo int    `plugin:"expand"`
			}

			So(ExpandValues(&s{}, expansions), ShouldNotBeNil)

		})

		Convey("any fields of the input struct with the appropriate tag should"+
			" be expanded", func() {

			type s struct {
				FieldOne   string
				FieldTwo   string `plugin:"expand"`
				FieldThree string `plugin:"expand,hello"`
			}

			s1 := &s{
				FieldOne:   "hello ${exp1}",
				FieldTwo:   "hi ${exp1}",
				FieldThree: "yo ${exp2|yo}",
			}

			So(ExpandValues(s1, expansions), ShouldBeNil)

			// make sure the appropriate fields were expanded
			So(s1.FieldOne, ShouldEqual, "hello ${exp1}")
			So(s1.FieldTwo, ShouldEqual, "hi val1")
			So(s1.FieldThree, ShouldEqual, "yo yo")

		})

		Convey("any nested structs tagged as expandable should have their"+
			" fields expanded appropriately", func() {

			type inner struct {
				FieldOne string `plugin:"expand"`
			}

			type outer struct {
				FieldOne   string `plugin:"expand"`
				FieldTwo   inner  `plugin:"expand"`
				FieldThree inner
			}

			s := &outer{
				FieldOne: "hello ${exp1}",
				FieldTwo: inner{
					FieldOne: "hi ${exp1}",
				},
				FieldThree: inner{
					FieldOne: "yo ${exp1}",
				},
			}

			So(ExpandValues(s, expansions), ShouldBeNil)

			// make sure all fields, including nested ones, were expanded
			// correctly
			So(s.FieldOne, ShouldEqual, "hello val1")
			So(s.FieldTwo.FieldOne, ShouldEqual, "hi val1")
			So(s.FieldThree.FieldOne, ShouldEqual, "yo ${exp1}")

		})

		Convey("any nested maps tagged as expandable should have their"+
			" fields expanded appropriately", func() {

			type outer struct {
				FieldOne string            `plugin:"expand"`
				FieldTwo map[string]string `plugin:"expand"`
			}

			s := &outer{
				FieldOne: "hello ${exp1}",
				FieldTwo: map[string]string{
					"1":       "hi ${exp1}",
					"${exp1}": "yo ${exp1}",
				},
			}

			So(ExpandValues(s, expansions), ShouldBeNil)

			// make sure all fields, including nested maps, were expanded
			So(s.FieldOne, ShouldEqual, "hello val1")
			So(s.FieldTwo["1"], ShouldEqual, "hi val1")
			So(s.FieldTwo["val1"], ShouldEqual, "yo val1")
		})

		Convey("if the input value is a slice, expansion should work "+
			"for the fields within that slice", func() {

			type simpleStruct struct {
				StructFieldKeyOne string `plugin:"expand"`
				StructFieldKeyTwo string
			}
			type sliceStruct struct {
				SliceFieldKey []*simpleStruct `plugin:"expand"`
			}
			simpleStruct1 := simpleStruct{
				StructFieldKeyOne: "hello ${exp1}",
				StructFieldKeyTwo: "abc${expl}",
			}
			simpleSlice := make([]*simpleStruct, 0)
			simpleSlice = append(simpleSlice, &simpleStruct1)
			sliceStruct1 := sliceStruct{}
			sliceStruct1.SliceFieldKey = simpleSlice
			So(ExpandValues(&sliceStruct1, expansions), ShouldBeNil)

			// make sure the appropriate fields were expanded
			So(simpleStruct1.StructFieldKeyOne, ShouldEqual, "hello val1")
			So(simpleStruct1.StructFieldKeyTwo, ShouldEqual, "abc${expl}")

		})

		Convey("any nested structs/slices tagged as expandable should have "+
			"their fields expanded appropriately", func() {

			type innerStruct struct {
				FieldOne string `plugin:"expand"`
			}

			type innerSlice struct {
				FieldOne string `plugin:"expand"`
			}

			type middle struct {
				FieldOne   string `plugin:"expand"`
				FieldTwo   string
				FieldThree innerStruct
				FieldFour  []*innerSlice
			}

			type outer struct {
				FieldOne string    `plugin:"expand"`
				FieldTwo []*middle `plugin:"expand"`
			}

			innerStructObject := innerStruct{
				FieldOne: "hello ${exp1}",
			}

			innerSliceField := innerSlice{
				FieldOne: "hi ${exp1}",
			}

			innerSliceObject := make([]*innerSlice, 0)
			innerSliceObject = append(innerSliceObject, &innerSliceField)

			middleObjectOne := middle{
				FieldOne: "ab ${exp1}",
				FieldTwo: "abc ${exp1}",
			}

			middleObjectTwo := middle{
				FieldOne:   "abc ${exp1}",
				FieldTwo:   "abc ${exp1}",
				FieldThree: innerStructObject,
				FieldFour:  innerSliceObject,
			}

			middleObject := make([]*middle, 0)
			middleObject = append(middleObject, &middleObjectOne)
			middleObject = append(middleObject, &middleObjectTwo)

			s := &outer{
				FieldOne: "hello ${exp1}",
				FieldTwo: middleObject,
			}

			So(ExpandValues(s, expansions), ShouldBeNil)

			// make sure all fields, including nested ones, were expanded
			// correctly
			So(s.FieldOne, ShouldEqual, "hello val1")
			So(s.FieldTwo[0].FieldOne, ShouldEqual, "ab val1")
			So(s.FieldTwo[1].FieldOne, ShouldEqual, "abc val1")
			So(s.FieldTwo[0].FieldTwo, ShouldEqual, "abc ${exp1}")
			So(s.FieldTwo[1].FieldTwo, ShouldEqual, "abc ${exp1}")
			So(s.FieldTwo[1].FieldThree.FieldOne, ShouldEqual, "hello ${exp1}")
			So(s.FieldTwo[1].FieldFour[0].FieldOne, ShouldEqual, "hi ${exp1}")
		})
	})

	Convey("When expanding map values", t, func() {
		expansions := command.NewExpansions(
			map[string]string{
				"a": "A",
				"b": "B",
				"c": "C",
			},
		)

		Convey("a simple map expands properly", func() {
			testmap := map[string]string{
				"nope": "nothing",
				"${a}": "key",
				"val":  "${b}",
				"${c}": "${a}",
			}
			So(ExpandValues(&testmap, expansions), ShouldBeNil)
			So(testmap["nope"], ShouldEqual, "nothing")
			So(testmap["A"], ShouldEqual, "key")
			So(testmap["val"], ShouldEqual, "B")
			So(testmap["C"], ShouldEqual, "A")
		})

		Convey("a recursive map expands properly", func() {
			testmap := map[string]map[string]string{
				"${a}": {
					"deep": "${c}",
					"no":   "same",
				},
			}
			So(ExpandValues(&testmap, expansions), ShouldBeNil)
			So(len(testmap), ShouldEqual, 1)
			So(len(testmap["A"]), ShouldEqual, 2)
			So(testmap["A"]["no"], ShouldEqual, "same")
			So(testmap["A"]["deep"], ShouldEqual, "C")
		})
	})
}
示例#10
0
func TestLocalJob(t *testing.T) {
	Convey("With an agent command", t, func() {
		Convey("command's stdout/stderr should be captured by logger", func() {
			appender := &evergreen.SliceAppender{[]*slogger.Log{}}
			killChan := make(chan bool)
			testCmd := &AgentCommand{
				ScriptLine:   "echo 'hi stdout!'; echo 'hi stderr!' >&2;",
				StreamLogger: testutil.NewTestLogger(appender),
				KillChan:     killChan,
				Expansions:   command.NewExpansions(map[string]string{}),
			}
			err := testCmd.Run("")
			So(err, ShouldBeNil)
			testCmd.FlushAndWait()
			// 2 lines from the command, plus 2 lines from the Run() func itself
			for _, v := range appender.Messages {
				fmt.Println(v.Message())
			}

			var levelToString = map[slogger.Level]string{
				slogger.ERROR: "hi stderr!",
				slogger.INFO:  "hi stdout!",
			}
			So(len(appender.Messages), ShouldEqual, 4)
			MsgA := appender.Messages[len(appender.Messages)-2]
			MsgB := appender.Messages[len(appender.Messages)-1]
			So(MsgA.Message(), ShouldEndWith, levelToString[MsgA.Level])
			So(MsgB.Message(), ShouldEndWith, levelToString[MsgB.Level])
		})

		Convey("command's stdout/stderr should only print newlines with \\n", func() {
			appender := &evergreen.SliceAppender{[]*slogger.Log{}}
			killChan := make(chan bool)
			newlineTestCmd := &AgentCommand{
				ScriptLine:   "printf 'this is not a newline...'; printf 'this is a newline \n';",
				StreamLogger: testutil.NewTestLogger(appender),
				KillChan:     killChan,
				Expansions:   command.NewExpansions(map[string]string{}),
			}

			err := newlineTestCmd.Run("")
			So(err, ShouldBeNil)
			newlineTestCmd.FlushAndWait()

			// 2 lines from the command, plus 1 lines from the Run() func itself
			for _, v := range appender.Messages {
				fmt.Println(v.Message())
			}
			So(len(appender.Messages), ShouldEqual, 3)
			NewLineMessage := appender.Messages[len(appender.Messages)-1]
			So(NewLineMessage.Message(), ShouldEqual, "this is not a newline...this is a newline ")
		})

	})

	Convey("With a long-running agent command", t, func() {
		appender := &evergreen.SliceAppender{[]*slogger.Log{}}
		killChan := make(chan bool)
		testCmd := &AgentCommand{
			ScriptLine:   "echo 'hi'; sleep 4; echo 'i should not get run'",
			StreamLogger: testutil.NewTestLogger(appender),
			KillChan:     killChan,
			Expansions:   command.NewExpansions(map[string]string{}),
		}

		Convey("using kill channel should abort command right away", func() {

			commandChan := make(chan error)
			go func() {
				err := testCmd.Run("")
				commandChan <- err
			}()

			go func() {
				// after a delay, signal the command to stop
				time.Sleep(1 * time.Second)
				close(killChan)
			}()

			err := <-commandChan
			So(err, ShouldEqual, InterruptedCmdError)
			testCmd.Flush()
			lastMessage := appender.Messages[len(appender.Messages)-1]
			nextLastMessage := appender.Messages[len(appender.Messages)-2]
			So(lastMessage.Message(), ShouldStartWith, "Got kill signal")
			So(nextLastMessage.Message(), ShouldEqual, "hi")
		})

	})

}
示例#11
0
// CreateHost spawns a host with the given options.
func (sm Spawn) CreateHost(so Options, owner *user.DBUser) error {

	// load in the appropriate distro
	d, err := distro.FindOne(distro.ById(so.Distro))
	if err != nil {
		return err
	}
	// add any extra user-specified data into the setup script
	if d.UserData.File != "" {
		userDataCmd := fmt.Sprintf("echo \"%v\" > %v\n",
			strings.Replace(so.UserData, "\"", "\\\"", -1), d.UserData.File)
		// prepend the setup script to add the userdata file
		if strings.HasPrefix(d.Setup, "#!") {
			firstLF := strings.Index(d.Setup, "\n")
			d.Setup = d.Setup[0:firstLF+1] + userDataCmd + d.Setup[firstLF+1:]
		} else {
			d.Setup = userDataCmd + d.Setup
		}
	}

	// modify the setup script to add the user's public key
	d.Setup += fmt.Sprintf("\necho \"\n%v\" >> ~%v/.ssh/authorized_keys\n", so.PublicKey, d.User)

	// replace expansions in the script
	exp := command.NewExpansions(sm.settings.Expansions)
	d.Setup, err = exp.ExpandString(d.Setup)
	if err != nil {
		return fmt.Errorf("expansions error: %v", err)
	}

	// fake out replacing spot instances with on-demand equivalents
	if d.Provider == ec2.SpotProviderName {
		d.Provider = ec2.OnDemandProviderName
	}

	// get the appropriate cloud manager
	cloudManager, err := providers.GetCloudManager(d.Provider, sm.settings)
	if err != nil {
		return err
	}

	// spawn the host
	provisionOptions := &host.ProvisionOptions{
		LoadCLI: true,
		TaskId:  so.TaskId,
		OwnerId: owner.Id,
	}
	expiration := DefaultExpiration
	hostOptions := cloud.HostOptions{
		ProvisionOptions:   provisionOptions,
		UserName:           so.UserName,
		ExpirationDuration: &expiration,
		UserData:           so.UserData,
		UserHost:           true,
	}

	_, err = cloudManager.SpawnInstance(d, hostOptions)
	if err != nil {
		return err
	}

	return nil
}