Beispiel #1
0
// 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
}
Beispiel #2
0
// 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()
}
Beispiel #3
0
// 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
}
Beispiel #4
0
// 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()
}
Beispiel #5
0
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
}
Beispiel #6
0
// 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()
}
Beispiel #7
0
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
}
Beispiel #8
0
// 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()
}
Beispiel #9
0
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()
}
Beispiel #10
0
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()
}
Beispiel #11
0
// 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
}
Beispiel #12
0
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()
}