func (task *provisionerTask) constructInstanceConfig( machine *apiprovisioner.Machine, auth authentication.AuthenticationProvider, pInfo *params.ProvisioningInfo, ) (*instancecfg.InstanceConfig, error) { stateInfo, apiInfo, err := auth.SetupAuthentication(machine) if err != nil { return nil, errors.Annotate(err, "failed to setup authentication") } // Generated a nonce for the new instance, with the format: "machine-#:UUID". // The first part is a badge, specifying the tag of the machine the provisioner // is running on, while the second part is a random UUID. uuid, err := utils.NewUUID() if err != nil { return nil, errors.Annotate(err, "failed to generate a nonce for machine "+machine.Id()) } nonce := fmt.Sprintf("%s:%s", task.machineTag, uuid) return instancecfg.NewInstanceConfig( machine.Id(), nonce, task.imageStream, pInfo.Series, task.secureServerConnection, nil, stateInfo, apiInfo, ) }
func constructStartInstanceParams( machine *apiprovisioner.Machine, instanceConfig *instancecfg.InstanceConfig, provisioningInfo *params.ProvisioningInfo, possibleTools coretools.List, ) (environs.StartInstanceParams, error) { volumes := make([]storage.VolumeParams, len(provisioningInfo.Volumes)) for i, v := range provisioningInfo.Volumes { volumeTag, err := names.ParseVolumeTag(v.VolumeTag) if err != nil { return environs.StartInstanceParams{}, errors.Trace(err) } if v.Attachment == nil { return environs.StartInstanceParams{}, errors.Errorf("volume params missing attachment") } machineTag, err := names.ParseMachineTag(v.Attachment.MachineTag) if err != nil { return environs.StartInstanceParams{}, errors.Trace(err) } if machineTag != machine.Tag() { return environs.StartInstanceParams{}, errors.Errorf("volume attachment params has invalid machine tag") } if v.Attachment.InstanceId != "" { return environs.StartInstanceParams{}, errors.Errorf("volume attachment params specifies instance ID") } volumes[i] = storage.VolumeParams{ volumeTag, v.Size, storage.ProviderType(v.Provider), v.Attributes, v.Tags, &storage.VolumeAttachmentParams{ AttachmentParams: storage.AttachmentParams{ Machine: machineTag, ReadOnly: v.Attachment.ReadOnly, }, Volume: volumeTag, }, } } var subnetsToZones map[network.Id][]string if provisioningInfo.SubnetsToZones != nil { // Convert subnet provider ids from string to network.Id. subnetsToZones = make(map[network.Id][]string, len(provisioningInfo.SubnetsToZones)) for providerId, zones := range provisioningInfo.SubnetsToZones { subnetsToZones[network.Id(providerId)] = zones } } return environs.StartInstanceParams{ Constraints: provisioningInfo.Constraints, Tools: possibleTools, InstanceConfig: instanceConfig, Placement: provisioningInfo.Placement, DistributionGroup: machine.DistributionGroup, Volumes: volumes, SubnetsToZones: subnetsToZones, }, nil }
func (task *provisionerTask) startMachine( machine *apiprovisioner.Machine, provisioningInfo *params.ProvisioningInfo, startInstanceParams environs.StartInstanceParams, ) error { inst, metadata, networkInfo, err := task.broker.StartInstance(startInstanceParams) if err != nil { // Set the state to error, so the machine will be skipped next // time until the error is resolved, but don't return an // error; just keep going with the other machines. return task.setErrorStatus("cannot start instance for machine %q: %v", machine, err) } nonce := startInstanceParams.MachineConfig.MachineNonce networks, ifaces := task.prepareNetworkAndInterfaces(networkInfo) err = machine.SetInstanceInfo(inst.Id(), nonce, metadata, networks, ifaces) if err != nil && params.IsCodeNotImplemented(err) { return fmt.Errorf("cannot provision instance %v for machine %q with networks: not implemented", inst.Id(), machine) } else if err == nil { logger.Infof("started machine %s as instance %s with hardware %q, networks %v, interfaces %v", machine, inst.Id(), metadata, networks, ifaces) return nil } // We need to stop the instance right away here, set error status and go on. task.setErrorStatus("cannot register instance for machine %v: %v", machine, err) if err := task.broker.StopInstances(inst.Id()); err != nil { // We cannot even stop the instance, log the error and quit. return errors.Annotatef(err, "cannot stop instance %q for machine %v", inst.Id(), machine) } return nil }
func (task *provisionerTask) startMachine( machine *apiprovisioner.Machine, provisioningInfo *params.ProvisioningInfo, startInstanceParams environs.StartInstanceParams, ) error { result, err := task.broker.StartInstance(startInstanceParams) if err != nil { // If this is a retryable error, we retry once if instance.IsRetryableCreationError(errors.Cause(err)) { logger.Infof("retryable error received on start instance - retrying instance creation") result, err = task.broker.StartInstance(startInstanceParams) if err != nil { return task.setErrorStatus("cannot start instance for machine after a retry %q: %v", machine, err) } } else { // Set the state to error, so the machine will be skipped next // time until the error is resolved, but don't return an // error; just keep going with the other machines. return task.setErrorStatus("cannot start instance for machine %q: %v", machine, err) } } inst := result.Instance hardware := result.Hardware nonce := startInstanceParams.InstanceConfig.MachineNonce networks, ifaces, err := task.prepareNetworkAndInterfaces(result.NetworkInfo) if err != nil { return task.setErrorStatus("cannot prepare network for machine %q: %v", machine, err) } volumes := volumesToApiserver(result.Volumes) volumeAttachments := volumeAttachmentsToApiserver(result.VolumeAttachments) // TODO(dimitern) In a newer Provisioner API version, change // SetInstanceInfo or add a new method that takes and saves in // state all the information available on a network.InterfaceInfo // for each interface, so we can later manage interfaces // dynamically at run-time. err = machine.SetInstanceInfo(inst.Id(), nonce, hardware, networks, ifaces, volumes, volumeAttachments) if err != nil && params.IsCodeNotImplemented(err) { return fmt.Errorf("cannot provision instance %v for machine %q with networks: not implemented", inst.Id(), machine) } else if err == nil { logger.Infof( "started machine %s as instance %s with hardware %q, networks %v, interfaces %v, volumes %v, volume attachments %v, subnets to zones %v", machine, inst.Id(), hardware, networks, ifaces, volumes, volumeAttachments, startInstanceParams.SubnetsToZones, ) return nil } // We need to stop the instance right away here, set error status and go on. task.setErrorStatus("cannot register instance for machine %v: %v", machine, err) if err := task.broker.StopInstances(inst.Id()); err != nil { // We cannot even stop the instance, log the error and quit. return errors.Annotatef(err, "cannot stop instance %q for machine %v", inst.Id(), machine) } return nil }
func (task *provisionerTask) setErrorStatus(message string, machine *apiprovisioner.Machine, err error) error { logger.Errorf(message, machine, err) if err1 := machine.SetStatus(params.StatusError, err.Error(), nil); err1 != nil { // Something is wrong with this machine, better report it back. return errors.Annotatef(err1, "cannot set error status for machine %q", machine) } return nil }
func (task *provisionerTask) constructInstanceConfig( machine *apiprovisioner.Machine, auth authentication.AuthenticationProvider, pInfo *params.ProvisioningInfo, ) (*instancecfg.InstanceConfig, error) { stateInfo, apiInfo, err := auth.SetupAuthentication(machine) if err != nil { return nil, errors.Annotate(err, "failed to setup authentication") } // Generated a nonce for the new instance, with the format: "machine-#:UUID". // The first part is a badge, specifying the tag of the machine the provisioner // is running on, while the second part is a random UUID. uuid, err := utils.NewUUID() if err != nil { return nil, errors.Annotate(err, "failed to generate a nonce for machine "+machine.Id()) } nonce := fmt.Sprintf("%s:%s", task.machineTag, uuid) instanceConfig, err := instancecfg.NewInstanceConfig( names.NewControllerTag(controller.Config(pInfo.ControllerConfig).ControllerUUID()), machine.Id(), nonce, task.imageStream, pInfo.Series, apiInfo, ) if err != nil { return nil, errors.Trace(err) } instanceConfig.Tags = pInfo.Tags if len(pInfo.Jobs) > 0 { instanceConfig.Jobs = pInfo.Jobs } if multiwatcher.AnyJobNeedsState(instanceConfig.Jobs...) { publicKey, err := simplestreams.UserPublicSigningKey() if err != nil { return nil, err } instanceConfig.Controller = &instancecfg.ControllerConfig{ PublicImageSigningKey: publicKey, MongoInfo: stateInfo, } instanceConfig.Controller.Config = make(map[string]interface{}) for k, v := range pInfo.ControllerConfig { instanceConfig.Controller.Config[k] = v } } return instanceConfig, nil }
func (task *provisionerTask) startMachine( machine *apiprovisioner.Machine, provisioningInfo *params.ProvisioningInfo, startInstanceParams environs.StartInstanceParams, ) error { var result *environs.StartInstanceResult for attemptsLeft := task.retryStartInstanceStrategy.retryCount; attemptsLeft >= 0; attemptsLeft-- { attemptResult, err := task.broker.StartInstance(startInstanceParams) if err == nil { result = attemptResult break } else if attemptsLeft <= 0 { // Set the state to error, so the machine will be skipped // next time until the error is resolved, but don't return // an error; just keep going with the other machines. return task.setErrorStatus("cannot start instance for machine %q: %v", machine, err) } logger.Warningf("%v", errors.Annotate(err, "starting instance")) retryMsg := fmt.Sprintf("will retry to start instance in %v", task.retryStartInstanceStrategy.retryDelay) if err2 := machine.SetStatus(status.Pending, retryMsg, nil); err2 != nil { logger.Errorf("%v", err2) } logger.Infof(retryMsg) select { case <-task.catacomb.Dying(): return task.catacomb.ErrDying() case <-time.After(task.retryStartInstanceStrategy.retryDelay): } } networkConfig := networkingcommon.NetworkConfigFromInterfaceInfo(result.NetworkInfo) volumes := volumesToAPIserver(result.Volumes) volumeNameToAttachmentInfo := volumeAttachmentsToAPIserver(result.VolumeAttachments) if err := machine.SetInstanceInfo( result.Instance.Id(), startInstanceParams.InstanceConfig.MachineNonce, result.Hardware, networkConfig, volumes, volumeNameToAttachmentInfo, ); err != nil { // We need to stop the instance right away here, set error status and go on. if err2 := task.setErrorStatus("cannot register instance for machine %v: %v", machine, err); err2 != nil { logger.Errorf("%v", errors.Annotate(err2, "cannot set machine's status")) } if err2 := task.broker.StopInstances(result.Instance.Id()); err2 != nil { logger.Errorf("%v", errors.Annotate(err2, "after failing to set instance info")) } return errors.Annotate(err, "cannot set instance info") } logger.Infof( "started machine %s as instance %s with hardware %q, network config %+v, volumes %v, volume attachments %v, subnets to zones %v", machine, result.Instance.Id(), result.Hardware, networkConfig, volumes, volumeNameToAttachmentInfo, startInstanceParams.SubnetsToZones, ) return nil }
func constructStartInstanceParams( controllerUUID string, machine *apiprovisioner.Machine, instanceConfig *instancecfg.InstanceConfig, provisioningInfo *params.ProvisioningInfo, possibleTools coretools.List, ) (environs.StartInstanceParams, error) { volumes := make([]storage.VolumeParams, len(provisioningInfo.Volumes)) for i, v := range provisioningInfo.Volumes { volumeTag, err := names.ParseVolumeTag(v.VolumeTag) if err != nil { return environs.StartInstanceParams{}, errors.Trace(err) } if v.Attachment == nil { return environs.StartInstanceParams{}, errors.Errorf("volume params missing attachment") } machineTag, err := names.ParseMachineTag(v.Attachment.MachineTag) if err != nil { return environs.StartInstanceParams{}, errors.Trace(err) } if machineTag != machine.Tag() { return environs.StartInstanceParams{}, errors.Errorf("volume attachment params has invalid machine tag") } if v.Attachment.InstanceId != "" { return environs.StartInstanceParams{}, errors.Errorf("volume attachment params specifies instance ID") } volumes[i] = storage.VolumeParams{ volumeTag, v.Size, storage.ProviderType(v.Provider), v.Attributes, v.Tags, &storage.VolumeAttachmentParams{ AttachmentParams: storage.AttachmentParams{ Machine: machineTag, ReadOnly: v.Attachment.ReadOnly, }, Volume: volumeTag, }, } } var subnetsToZones map[network.Id][]string if provisioningInfo.SubnetsToZones != nil { // Convert subnet provider ids from string to network.Id. subnetsToZones = make(map[network.Id][]string, len(provisioningInfo.SubnetsToZones)) for providerId, zones := range provisioningInfo.SubnetsToZones { subnetsToZones[network.Id(providerId)] = zones } } var endpointBindings map[string]network.Id if len(provisioningInfo.EndpointBindings) != 0 { endpointBindings = make(map[string]network.Id) for endpoint, space := range provisioningInfo.EndpointBindings { endpointBindings[endpoint] = network.Id(space) } } possibleImageMetadata := make([]*imagemetadata.ImageMetadata, len(provisioningInfo.ImageMetadata)) for i, metadata := range provisioningInfo.ImageMetadata { possibleImageMetadata[i] = &imagemetadata.ImageMetadata{ Id: metadata.ImageId, Arch: metadata.Arch, RegionAlias: metadata.Region, RegionName: metadata.Region, Storage: metadata.RootStorageType, Stream: metadata.Stream, VirtType: metadata.VirtType, Version: metadata.Version, } } return environs.StartInstanceParams{ ControllerUUID: controllerUUID, Constraints: provisioningInfo.Constraints, Tools: possibleTools, InstanceConfig: instanceConfig, Placement: provisioningInfo.Placement, DistributionGroup: machine.DistributionGroup, Volumes: volumes, SubnetsToZones: subnetsToZones, EndpointBindings: endpointBindings, ImageMetadata: possibleImageMetadata, StatusCallback: machine.SetInstanceStatus, }, nil }
func (task *provisionerTask) startMachine( machine *apiprovisioner.Machine, provisioningInfo *params.ProvisioningInfo, startInstanceParams environs.StartInstanceParams, ) error { result, err := task.broker.StartInstance(startInstanceParams) if err != nil { if !instance.IsRetryableCreationError(errors.Cause(err)) { // Set the state to error, so the machine will be skipped next // time until the error is resolved, but don't return an // error; just keep going with the other machines. return task.setErrorStatus("cannot start instance for machine %q: %v", machine, err) } logger.Infof("retryable error received on start instance: %v", err) for count := task.retryStartInstanceStrategy.retryCount; count > 0; count-- { if task.retryStartInstanceStrategy.retryDelay > 0 { select { case <-task.catacomb.Dying(): return task.catacomb.ErrDying() case <-time.After(task.retryStartInstanceStrategy.retryDelay): } } result, err = task.broker.StartInstance(startInstanceParams) if err == nil { break } // If this was the last attempt and an error was received, set the error // status on the machine. if count == 1 { return task.setErrorStatus("cannot start instance for machine %q: %v", machine, err) } } } inst := result.Instance hardware := result.Hardware nonce := startInstanceParams.InstanceConfig.MachineNonce networks, ifaces, err := task.prepareNetworkAndInterfaces(result.NetworkInfo) if err != nil { return task.setErrorStatus("cannot prepare network for machine %q: %v", machine, err) } volumes := volumesToApiserver(result.Volumes) volumeAttachments := volumeAttachmentsToApiserver(result.VolumeAttachments) // TODO(dimitern) In a newer Provisioner API version, change // SetInstanceInfo or add a new method that takes and saves in // state all the information available on a network.InterfaceInfo // for each interface, so we can later manage interfaces // dynamically at run-time. err = machine.SetInstanceInfo(inst.Id(), nonce, hardware, networks, ifaces, volumes, volumeAttachments) if err == nil { logger.Infof( "started machine %s as instance %s with hardware %q, networks %v, interfaces %v, volumes %v, volume attachments %v, subnets to zones %v", machine, inst.Id(), hardware, networks, ifaces, volumes, volumeAttachments, startInstanceParams.SubnetsToZones, ) return nil } // We need to stop the instance right away here, set error status and go on. task.setErrorStatus("cannot register instance for machine %v: %v", machine, err) if err := task.broker.StopInstances(inst.Id()); err != nil { // We cannot even stop the instance, log the error and quit. return errors.Annotatef(err, "cannot stop instance %q for machine %v", inst.Id(), machine) } return nil }