// SpawnInstance creates and starts a new Docker container func (dockerMgr *DockerManager) SpawnInstance(d *distro.Distro, hostOpts cloud.HostOptions) (*host.Host, error) { var err error if d.Provider != ProviderName { return nil, fmt.Errorf("Can't spawn instance of %v for distro %v: provider is %v", ProviderName, d.Id, d.Provider) } // Initialize client dockerClient, settings, err := generateClient(d) if err != nil { return nil, err } // Create HostConfig structure hostConfig := &docker.HostConfig{} err = populateHostConfig(hostConfig, d) if err != nil { evergreen.Logger.Logf(slogger.ERROR, "Unable to populate docker host config for host '%s': %v", settings.HostIp, err) return nil, err } // Build container containerName := "docker-" + bson.NewObjectId().Hex() newContainer, err := dockerClient.CreateContainer( docker.CreateContainerOptions{ Name: containerName, Config: &docker.Config{ Cmd: []string{"/usr/sbin/sshd", "-D"}, ExposedPorts: map[docker.Port]struct{}{ SSHDPort: {}, }, Image: settings.ImageId, }, HostConfig: hostConfig, }, ) if err != nil { evergreen.Logger.Logf(slogger.ERROR, "Docker create container API call failed for host '%s': %v", settings.HostIp, err) return nil, err } // Start container err = dockerClient.StartContainer(newContainer.ID, nil) if err != nil { // Clean up err2 := dockerClient.RemoveContainer( docker.RemoveContainerOptions{ ID: newContainer.ID, Force: true, }, ) if err2 != nil { err = fmt.Errorf("%v. And was unable to clean up container %v: %v", err, newContainer.ID, err2) } evergreen.Logger.Logf(slogger.ERROR, "Docker start container API call failed for host '%s': %v", settings.HostIp, err) return nil, err } // Retrieve container details newContainer, err = dockerClient.InspectContainer(newContainer.ID) if err != nil { evergreen.Logger.Logf(slogger.ERROR, "Docker inspect container API call failed for host '%s': %v", settings.HostIp, err) return nil, err } hostPort, err := retrieveOpenPortBinding(newContainer) if err != nil { evergreen.Logger.Logf(slogger.ERROR, "Error with docker container '%v': %v", newContainer.ID, err) return nil, err } hostStr := fmt.Sprintf("%s:%s", settings.BindIp, hostPort) // Add host info to db instanceName := "container-" + fmt.Sprintf("%d", rand.New(rand.NewSource(time.Now().UnixNano())).Int()) intentHost := cloud.NewIntent(*d, instanceName, ProviderName, hostOpts) intentHost.Host = hostStr err = intentHost.Insert() if err != nil { return nil, evergreen.Logger.Errorf(slogger.ERROR, "Failed to insert new host '%s': %v", intentHost.Id, err) } evergreen.Logger.Logf(slogger.DEBUG, "Successfully inserted new host '%v' for distro '%v'", intentHost.Id, d.Id) return intentHost, nil }
func (cloudManager *EC2SpotManager) SpawnInstance(d *distro.Distro, hostOpts cloud.HostOptions) (*host.Host, error) { if d.Provider != SpotProviderName { return nil, fmt.Errorf("Can't spawn instance of %v for distro %v: provider is %v", SpotProviderName, d.Id, d.Provider) } ec2Handle := getUSEast(*cloudManager.awsCredentials) //Decode and validate the ProviderSettings into the ec2-specific ones. ec2Settings := &EC2SpotSettings{} if err := mapstructure.Decode(d.ProviderSettings, ec2Settings); err != nil { return nil, fmt.Errorf("Error decoding params for distro %v: %v", d.Id, err) } if err := ec2Settings.Validate(); err != nil { return nil, fmt.Errorf("Invalid EC2 spot settings in distro %v: %v", d.Id, err) } blockDevices, err := makeBlockDeviceMappings(ec2Settings.MountPoints) if err != nil { return nil, err } instanceName := generateName(d.Id) intentHost := cloud.NewIntent(*d, instanceName, SpotProviderName, hostOpts) intentHost.InstanceType = ec2Settings.InstanceType // record this 'intent host' if err := intentHost.Insert(); err != nil { return nil, evergreen.Logger.Errorf(slogger.ERROR, "Could not insert intent "+ "host “%v”: %v", intentHost.Id, err) } evergreen.Logger.Logf(slogger.DEBUG, "Successfully inserted intent host “%v” "+ "for distro “%v” to signal cloud instance spawn intent", instanceName, d.Id) spotRequest := &ec2.RequestSpotInstances{ SpotPrice: fmt.Sprintf("%v", ec2Settings.BidPrice), InstanceCount: 1, ImageId: ec2Settings.AMI, KeyName: ec2Settings.KeyName, InstanceType: ec2Settings.InstanceType, SecurityGroups: ec2.SecurityGroupNames(ec2Settings.SecurityGroup), BlockDevices: blockDevices, } // if the spot instance is a vpc then set the appropriate fields if ec2Settings.IsVpc { spotRequest.SecurityGroups = ec2.SecurityGroupIds(ec2Settings.SecurityGroup) spotRequest.AssociatePublicIpAddress = true spotRequest.SubnetId = ec2Settings.SubnetId } spotResp, err := ec2Handle.RequestSpotInstances(spotRequest) if err != nil { //Remove the intent host if the API call failed if err := intentHost.Remove(); err != nil { evergreen.Logger.Logf(slogger.ERROR, "Failed to remove intent host %v: %v", intentHost.Id, err) } return nil, evergreen.Logger.Errorf(slogger.ERROR, "Failed starting spot instance "+ " for distro '%v' on intent host %v: %v", d.Id, intentHost.Id, err) } spotReqRes := spotResp.SpotRequestResults[0] if spotReqRes.State != SpotStatusOpen && spotReqRes.State != SpotStatusActive { return nil, evergreen.Logger.Errorf(slogger.ERROR, "Spot request %v was found in "+ " state %v on intent host %v", spotReqRes.SpotRequestId, spotReqRes.State, intentHost.Id) } intentHost.Id = spotReqRes.SpotRequestId err = intentHost.Insert() if err != nil { return nil, evergreen.Logger.Errorf(slogger.ERROR, "Could not insert updated host info with id %v"+ " for intent host %v: %v", intentHost.Id, instanceName, err) } //find the old intent host and remove it, since we now have the real //host doc successfully stored. oldIntenthost, err := host.FindOne(host.ById(instanceName)) if err != nil { return nil, evergreen.Logger.Errorf(slogger.ERROR, "Can't locate "+ "record inserted for intended host '%v' due to error: %v", instanceName, err) } if oldIntenthost == nil { return nil, evergreen.Logger.Errorf(slogger.ERROR, "Can't locate "+ "record inserted for intended host '%v'", instanceName) } err = oldIntenthost.Remove() if err != nil { evergreen.Logger.Logf(slogger.ERROR, "Could not remove intent host "+ "“%v”: %v", oldIntenthost.Id, err) return nil, err } // create some tags based on user, hostname, owner, time, etc. tags := makeTags(intentHost) // attach the tags to this instance err = attachTags(ec2Handle, tags, intentHost.Id) if err != nil { evergreen.Logger.Errorf(slogger.ERROR, "Unable to attach tags for %v: %v", intentHost.Id, err) } else { evergreen.Logger.Logf(slogger.DEBUG, "Attached tag name “%v” for “%v”", instanceName, intentHost.Id) } return intentHost, nil }
func (cloudManager *EC2Manager) SpawnInstance(d *distro.Distro, hostOpts cloud.HostOptions) (*host.Host, error) { if d.Provider != OnDemandProviderName { return nil, fmt.Errorf("Can't spawn instance of %v for distro %v: provider is %v", OnDemandProviderName, d.Id, d.Provider) } ec2Handle := getUSEast(*cloudManager.awsCredentials) //Decode and validate the ProviderSettings into the ec2-specific ones. ec2Settings := &EC2ProviderSettings{} if err := mapstructure.Decode(d.ProviderSettings, ec2Settings); err != nil { return nil, fmt.Errorf("Error decoding params for distro %v: %v", d.Id, err) } if err := ec2Settings.Validate(); err != nil { return nil, fmt.Errorf("Invalid EC2 settings in distro %#v: %v and %#v", d, err, ec2Settings) } blockDevices, err := makeBlockDeviceMappings(ec2Settings.MountPoints) if err != nil { return nil, err } instanceName := generateName(d.Id) // proactively write all possible information pertaining // to the host we want to create. this way, if we are unable // to start it or record its instance id, we have a way of knowing // something went wrong - and what intentHost := cloud.NewIntent(*d, instanceName, OnDemandProviderName, hostOpts) intentHost.InstanceType = ec2Settings.InstanceType // record this 'intent host' if err := intentHost.Insert(); err != nil { return nil, evergreen.Logger.Errorf(slogger.ERROR, "Could not insert intent "+ "host “%v”: %v", intentHost.Id, err) } evergreen.Logger.Logf(slogger.DEBUG, "Successfully inserted intent host “%v” "+ "for distro “%v” to signal cloud instance spawn intent", instanceName, d.Id) options := ec2.RunInstancesOptions{ MinCount: 1, MaxCount: 1, ImageId: ec2Settings.AMI, KeyName: ec2Settings.KeyName, InstanceType: ec2Settings.InstanceType, SecurityGroups: ec2.SecurityGroupNames(ec2Settings.SecurityGroup), BlockDevices: blockDevices, } // if it's a Vpc override the options to be the correct VPC settings. if ec2Settings.IsVpc { options.SecurityGroups = ec2.SecurityGroupIds(ec2Settings.SecurityGroup) options.AssociatePublicIpAddress = true options.SubnetId = ec2Settings.SubnetId } // start the instance - starting an instance does not mean you can connect // to it immediately you have to use GetInstanceStatus below to ensure that // it's actually running newHost, resp, err := startEC2Instance(ec2Handle, &options, intentHost) if err != nil { return nil, evergreen.Logger.Errorf(slogger.ERROR, "Could not start new "+ "instance for distro “%v”. Accompanying host record is “%v”: %v", d.Id, intentHost.Id, err) } instance := resp.Instances[0] // create some tags based on user, hostname, owner, time, etc. tags := makeTags(intentHost) // attach the tags to this instance err = attachTags(ec2Handle, tags, instance.InstanceId) if err != nil { evergreen.Logger.Errorf(slogger.ERROR, "Unable to attach tags for %v: %v", instance.InstanceId, err) } else { evergreen.Logger.Logf(slogger.DEBUG, "Attached tag name “%v” for “%v”", instanceName, instance.InstanceId) } return newHost, nil }
//SpawnInstance creates a new droplet for the given distro. func (digoMgr *DigitalOceanManager) SpawnInstance(d *distro.Distro, hostOpts cloud.HostOptions) (*host.Host, error) { if d.Provider != ProviderName { return nil, fmt.Errorf("Can't spawn instance of %v for distro %v: provider is %v", ProviderName, d.Id, d.Provider) } digoSettings := &Settings{} if err := mapstructure.Decode(d.ProviderSettings, digoSettings); err != nil { return nil, fmt.Errorf("Error decoding params for distro %v: %v", d.Id, err) } if err := digoSettings.Validate(); err != nil { return nil, fmt.Errorf("Invalid DigitalOcean settings in distro %v: %v", d.Id, err) } instanceName := "droplet-" + fmt.Sprintf("%v", rand.New(rand.NewSource(time.Now().UnixNano())).Int()) intentHost := cloud.NewIntent(*d, instanceName, ProviderName, hostOpts) dropletReq := &digo.Droplet{ SizeId: digoSettings.SizeId, ImageId: digoSettings.ImageId, RegionId: digoSettings.RegionId, Name: instanceName, SshKey: digoSettings.SSHKeyId, } if err := intentHost.Insert(); err != nil { return nil, evergreen.Logger.Errorf(slogger.ERROR, "Could not insert intent "+ "host '%v': %v", intentHost.Id, err) } evergreen.Logger.Logf(slogger.DEBUG, "Successfully inserted intent host '%v' "+ "for distro '%v' to signal cloud instance spawn intent", instanceName, d.Id) newDroplet, err := digoMgr.account.CreateDroplet(dropletReq) if err != nil { evergreen.Logger.Logf(slogger.ERROR, "DigitalOcean create droplet API call failed"+ " for intent host '%v': %v", intentHost.Id, err) // remove the intent host document rmErr := intentHost.Remove() if rmErr != nil { return nil, evergreen.Logger.Errorf(slogger.ERROR, "Could not remove intent host "+ "'%v': %v", intentHost.Id, rmErr) } return nil, err } // find old intent host host, err := host.FindOne(host.ById(intentHost.Id)) if host == nil { return nil, evergreen.Logger.Errorf(slogger.ERROR, "Can't locate "+ "record inserted for intended host “%v”", intentHost.Id) } if err != nil { return nil, evergreen.Logger.Errorf(slogger.ERROR, "Failed to look up intent host %v: %v", intentHost.Id, err) } host.Id = fmt.Sprintf("%v", newDroplet.Id) host.Host = newDroplet.IpAddress err = host.Insert() if err != nil { return nil, evergreen.Logger.Errorf(slogger.ERROR, "Failed to insert new host %v"+ "for intent host %v: %v", host.Id, intentHost.Id, err) } // remove the intent host document err = intentHost.Remove() if err != nil { return nil, evergreen.Logger.Errorf(slogger.ERROR, "Could not remove "+ "insert host “%v” (replaced by “%v”): %v", intentHost.Id, host.Id, err) } return host, nil }