// CreateSnapshot creates a new snapshot from the given volumeId and // description. It waits until it's ready. func (a *Amazon) CreateSnapshot(volumeId, desc string) (*ec2.Snapshot, error) { snapshot, err := a.Client.CreateSnapshot(volumeId, desc) if err != nil { return nil, err } checkSnapshot := func(int) (machinestate.State, error) { s, err := a.Client.SnapshotByID(aws.StringValue(snapshot.SnapshotId)) if IsNotFound(err) { // shouldn't happen but let's check it anyway return machinestate.Pending, nil } if err != nil { return 0, err } if aws.StringValue(s.State) != ec2.SnapshotStateCompleted { return machinestate.Pending, nil } snapshot = s return machinestate.Stopped, nil } ws := waitstate.WaitState{ StateFunc: checkSnapshot, DesiredState: machinestate.Stopped, } if err := ws.Wait(); err != nil { return nil, err } return snapshot, nil }
// DetachVolume detach the given volumeID. It waits until it's ready. func (a *Amazon) DetachVolume(volumeID string) error { if err := a.Client.DetachVolume(volumeID); err != nil { return err } checkDetaching := func(currentPercentage int) (machinestate.State, error) { vol, err := a.Client.VolumeByID(volumeID) if err != nil { return 0, err } // ready! if len(vol.Attachments) == 0 { return machinestate.Stopped, nil } // otherwise wait until it's detached if aws.StringValue(vol.Attachments[0].State) != "detached" { return machinestate.Pending, nil } return machinestate.Stopped, nil } ws := waitstate.WaitState{ StateFunc: checkDetaching, DesiredState: machinestate.Stopped, } return ws.Wait() }
// CreateVolume creates a new volume from the given snapshot id and size. It // waits until it's ready. func (a *Amazon) CreateVolume(snapshotID, availZone, volumeType string, size int) (vol *ec2.Volume, err error) { v, err := a.Client.CreateVolume(snapshotID, availZone, volumeType, int64(size)) if err != nil { return nil, err } checkVolume := func(currentPercentage int) (machinestate.State, error) { vol, err = a.Client.VolumeByID(aws.StringValue(v.VolumeId)) if err != nil { return 0, err } if aws.StringValue(vol.State) != "available" { return machinestate.Pending, nil } return machinestate.Stopped, nil // TODO(rjeczalik): Attached? } ws := waitstate.WaitState{ StateFunc: checkVolume, DesiredState: machinestate.Stopped, } if err := ws.Wait(); err != nil { return nil, err } return vol, nil }
// AttachVolume attach the given volumeID to the instance. DevicePath defines // the root path of the volume such as /dev/sda1. It waits until it's ready. func (a *Amazon) AttachVolume(volumeID, instanceID, devicePath string) error { if err := a.Client.AttachVolume(volumeID, instanceID, devicePath); err != nil { return err } checkAttaching := func(currentPercentage int) (machinestate.State, error) { vol, err := a.Client.VolumeByID(volumeID) if err != nil { return 0, err } if len(vol.Attachments) == 0 { return machinestate.Pending, nil } if aws.StringValue(vol.Attachments[0].State) != "attached" { return machinestate.Pending, nil } return machinestate.Stopped, nil } ws := waitstate.WaitState{ StateFunc: checkAttaching, DesiredState: machinestate.Stopped, } return ws.Wait() }
func (a *Amazon) Start(ctx context.Context) (*ec2.Instance, error) { if a.Id() == "" { return nil, ErrInstanceEmptyID } // if we have eventer, use it ev, withPush := eventer.FromContext(ctx) if withPush { ev.Push(&eventer.Event{ Message: "Starting machine", Status: machinestate.Starting, Percentage: 25, }) } a.Log.Debug("amazon start eventer: %t", withPush) _, err := a.Client.StartInstance(a.Id()) if err != nil { return nil, err } var instance *ec2.Instance stateFunc := func(currentPercentage int) (machinestate.State, error) { if withPush { ev.Push(&eventer.Event{ Message: "Starting machine", Status: machinestate.Starting, Percentage: currentPercentage, }) } instance, err = a.Instance() if err != nil { return 0, err } return StatusToState(aws.StringValue(instance.State.Name)), nil } ws := waitstate.WaitState{ StateFunc: stateFunc, DesiredState: machinestate.Running, Start: 45, Finish: 60, } if err := ws.Wait(); err != nil { return nil, err } return instance, nil }
// Start stops Google compute instance. func (m *Machine) Stop(ctx context.Context) (interface{}, error) { ev, withPush := eventer.FromContext(ctx) if withPush { ev.Push(&eventer.Event{ Message: "Stopping machine", Status: machinestate.Stopping, Percentage: 25, }) } project, zone, name := m.Location() _, err := m.InstancesService.Stop(project, zone, name).Do() // Ignore http.StatusNotModified status. if err != nil && googleapi.IsNotModified(err) { if withPush { ev.Push(&eventer.Event{ Message: "Machine stopped", Status: machinestate.Stopped, Percentage: 60, }) } return nil, nil } if err != nil { return nil, err } stateFunc := func(currentPercentage int) (machinestate.State, error) { if withPush { ev.Push(&eventer.Event{ Message: "Stopping machine", Status: machinestate.Stopping, Percentage: currentPercentage, }) } state, _, err := m.Info(nil) if err != nil { return machinestate.Unknown, err } return state, err } ws := waitstate.WaitState{ StateFunc: stateFunc, DesiredState: machinestate.Stopped, Start: 45, Finish: 60, } return nil, ws.Wait() }
func (a *Amazon) CheckBuild(ctx context.Context, instanceId string, start, finish int) (*ec2.Instance, error) { ev, withPush := eventer.FromContext(ctx) if withPush { ev.Push(&eventer.Event{ Message: "Building machine", Status: machinestate.Building, Percentage: start, }) } var instance *ec2.Instance var err error stateFunc := func(currentPercentage int) (machinestate.State, error) { if withPush { ev.Push(&eventer.Event{ Message: "Building machine", Status: machinestate.Building, Percentage: currentPercentage, }) } instance, err = a.Client.InstanceByID(instanceId) if err != nil { return 0, err } currentStatus := StatusToState(aws.StringValue(instance.State.Name)) // happens when there is no volume limit. The instance will be not // build and it returns terminated from AWS if currentStatus.In(machinestate.Terminated, machinestate.Terminating) { return 0, ErrInstanceTerminated } return currentStatus, nil } ws := waitstate.WaitState{ Timeout: 15 * time.Minute, StateFunc: stateFunc, DesiredState: machinestate.Running, Start: start, Finish: finish, } if err := ws.Wait(); err != nil { return nil, err } return instance, nil }
// Stop the remote Softlayer instance. func (m *Machine) Stop(ctx context.Context) (interface{}, error) { ev, withPush := eventer.FromContext(ctx) if withPush { ev.Push(&eventer.Event{ Message: "Stopping machine", Status: machinestate.Stopping, Percentage: 25, }) } metadata := m.BaseMachine.Metadata.(*Metadata) service, err := m.Client.GetSoftLayer_Virtual_Guest_Service() if err != nil { return nil, err } _, err = service.PowerOffSoft(metadata.Id) if err != nil { return nil, err } stateFunc := func(currentPercentage int) (machinestate.State, error) { if withPush { ev.Push(&eventer.Event{ Message: "Stopping machine", Status: machinestate.Stopping, Percentage: currentPercentage, }) } state, _, err := m.Info(nil) if err != nil { return machinestate.Unknown, err } return state, err } ws := waitstate.WaitState{ StateFunc: stateFunc, DesiredState: machinestate.Stopped, Start: 45, Finish: 60, } return nil, ws.Wait() }
func (a *Amazon) Destroy(ctx context.Context, start, finish int) error { if a.Id() == "" { return ErrInstanceEmptyID } ev, withPush := eventer.FromContext(ctx) if withPush { ev.Push(&eventer.Event{ Message: "Terminating machine", Status: machinestate.Terminating, Percentage: start, }) } _, err := a.Client.TerminateInstance(a.Id()) if err != nil { return err } stateFunc := func(currentPercentage int) (machinestate.State, error) { if withPush { ev.Push(&eventer.Event{ Message: "Terminating machine", Status: machinestate.Terminating, Percentage: currentPercentage, }) } instance, err := a.Instance() if err != nil { return 0, err } return StatusToState(aws.StringValue(instance.State.Name)), nil } ws := waitstate.WaitState{ StateFunc: stateFunc, DesiredState: machinestate.Terminated, Start: start, Finish: finish, } return ws.Wait() }
func (a *Amazon) Restart(ctx context.Context) error { if a.Id() == "" { return ErrInstanceEmptyID } ev, withPush := eventer.FromContext(ctx) if withPush { ev.Push(&eventer.Event{ Message: "Restarting machine", Status: machinestate.Rebooting, Percentage: 10, }) } err := a.Client.RebootInstance(a.Id()) if err != nil { return err } stateFunc := func(currentPercentage int) (machinestate.State, error) { if withPush { ev.Push(&eventer.Event{ Message: "Restarting machine", Status: machinestate.Rebooting, Percentage: currentPercentage, }) } instance, err := a.Instance() if err != nil { return 0, err } return StatusToState(aws.StringValue(instance.State.Name)), nil } ws := waitstate.WaitState{ StateFunc: stateFunc, DesiredState: machinestate.Running, Start: 25, Finish: 60, } return ws.Wait() }
// ExistingVolume retrieves the volume for the given existing volume ID. This // can be used instead of the plain a.Client.Volumes, because the plain method // returns "(InvalidVolume.NotFound)" even if the volume exists. This method // tries for one minute to get a successfull response(errors are neglected), so // try this only if the Volume exists. func (a *Amazon) ExistingVolume(volumeID string) (vol *ec2.Volume, err error) { getVolume := func(currentPercentage int) (machinestate.State, error) { vol, err = a.Client.VolumeByID(volumeID) if err != nil { return machinestate.Pending, nil // we don't return until we get a result } return machinestate.Running, nil } ws := waitstate.WaitState{ StateFunc: getVolume, DesiredState: machinestate.Running, Timeout: time.Minute, } if err := ws.Wait(); err != nil { return nil, err } return vol, nil }
func (a *Amazon) Stop(ctx context.Context) error { if a.Id() == "" { return ErrInstanceEmptyID } ev, withPush := eventer.FromContext(ctx) if withPush { ev.Push(&eventer.Event{ Message: "Stopping machine", Status: machinestate.Stopping, Percentage: 25, }) } a.Log.Debug("amazon stop eventer: %t", withPush) var ( // needs to be declared so we can call it recursively tryStop func() error tried int = 0 lastError error ) // we try to stop if there is an error in the stop instance call. For // example if it's a timeout we shouldn't return and try again. tryStop = func() error { if tried == 2 { return fmt.Errorf("Tried to stop three times without any success. Last error is:'%s'", lastError) } _, err := a.Client.StopInstance(a.Id()) if err != nil { lastError = err tried++ return tryStop() } return nil } if err := tryStop(); err != nil { return err } stateFunc := func(currentPercentage int) (machinestate.State, error) { if withPush { ev.Push(&eventer.Event{ Message: "Stopping machine", Status: machinestate.Stopping, Percentage: currentPercentage, }) } instance, err := a.Instance() if err != nil { return 0, err } return StatusToState(aws.StringValue(instance.State.Name)), nil } ws := waitstate.WaitState{ StateFunc: stateFunc, DesiredState: machinestate.Stopped, Start: 45, Finish: 60, } return ws.Wait() }