github.com/billybanfield/evergreen@v0.0.0-20170525200750-eeee692790f7/cloud/cloud.go (about)

     1  package cloud
     2  
     3  import (
     4  	"time"
     5  
     6  	"github.com/evergreen-ci/evergreen"
     7  	"github.com/evergreen-ci/evergreen/model/distro"
     8  	"github.com/evergreen-ci/evergreen/model/host"
     9  	"github.com/evergreen-ci/evergreen/util"
    10  )
    11  
    12  type CloudStatus int
    13  
    14  const (
    15  	//Catch-all for unrecognized status codes
    16  	StatusUnknown = CloudStatus(iota)
    17  
    18  	//StatusPending indicates that it is not yet clear if the instance
    19  	//has been successfully started or not (e.g., pending spot request)
    20  	StatusPending
    21  
    22  	//StatusInitializing means the instance request has been successfully
    23  	//fulfilled, but it's not yet done booting up
    24  	StatusInitializing
    25  
    26  	//StatusFailed indicates that an attempt to start the instance has failed;
    27  	//Could be due to billing, lack of capacity, etc.
    28  	StatusFailed
    29  
    30  	//StatusRunning means the machine is done booting, and active
    31  	StatusRunning
    32  
    33  	StatusStopped
    34  	StatusTerminated
    35  )
    36  
    37  func (stat CloudStatus) String() string {
    38  	switch stat {
    39  	case StatusPending:
    40  		return "pending"
    41  	case StatusFailed:
    42  		return "failed"
    43  	case StatusInitializing:
    44  		return "initializing"
    45  	case StatusRunning:
    46  		return "running"
    47  	case StatusStopped:
    48  		return "stopped"
    49  	case StatusTerminated:
    50  		return "terminated"
    51  	default:
    52  		return "unknown"
    53  	}
    54  }
    55  
    56  // ProviderSettings exposes provider-specific configuration settings for a CloudManager.
    57  type ProviderSettings interface {
    58  	Validate() error
    59  }
    60  
    61  //CloudManager is an interface which handles creating new hosts or modifying
    62  //them via some third-party API.
    63  type CloudManager interface {
    64  	// Returns a pointer to the manager's configuration settings struct
    65  	GetSettings() ProviderSettings
    66  
    67  	//Load credentials or other settings from the config file
    68  	Configure(*evergreen.Settings) error
    69  
    70  	// SpawnInstance attempts to create a new host by requesting one from the
    71  	// provider's API.
    72  	SpawnInstance(*distro.Distro, HostOptions) (*host.Host, error)
    73  
    74  	// CanSpawn indicates if this provider is capable of creating new instances
    75  	// with SpawnInstance(). If this provider doesn't support spawning new
    76  	// hosts, this will return false (and calls to SpawnInstance will
    77  	// return errors)
    78  	CanSpawn() (bool, error)
    79  
    80  	// get the status of an instance
    81  	GetInstanceStatus(*host.Host) (CloudStatus, error)
    82  
    83  	// TerminateInstances destroys the host in the underlying provider
    84  	TerminateInstance(*host.Host) error
    85  
    86  	//IsUp returns true if the underlying provider has not destroyed the
    87  	//host (in other words, if the host "should" be reachable. This does not
    88  	//necessarily mean that the host actually *is* reachable via SSH
    89  	IsUp(*host.Host) (bool, error)
    90  
    91  	//Called by the hostinit process when the host is actually up. Used
    92  	//to set additional provider-specific metadata
    93  	OnUp(*host.Host) error
    94  
    95  	//IsSSHReachable returns true if the host can successfully
    96  	//accept and run an ssh command.
    97  	IsSSHReachable(host *host.Host, keyPath string) (bool, error)
    98  
    99  	// GetDNSName returns the DNS name of a host.
   100  	GetDNSName(*host.Host) (string, error)
   101  
   102  	// GetSSHOptions generates the command line args to be passed to ssh to
   103  	// allow connection to the machine
   104  	GetSSHOptions(host *host.Host, keyName string) ([]string, error)
   105  
   106  	// TimeTilNextPayment returns how long there is until the next payment
   107  	// is due for a particular host
   108  	TimeTilNextPayment(host *host.Host) time.Duration
   109  }
   110  
   111  // CloudCostCalculator is an interface for cloud managers that can estimate an
   112  // what a span of time on a given host costs.
   113  type CloudCostCalculator interface {
   114  	CostForDuration(host *host.Host, start time.Time, end time.Time) (float64, error)
   115  }
   116  
   117  // HostOptions is a struct of options that are commonly passed around when creating a
   118  // new cloud host.
   119  type HostOptions struct {
   120  	ProvisionOptions   *host.ProvisionOptions
   121  	ExpirationDuration *time.Duration
   122  	UserName           string
   123  	UserData           string
   124  	UserHost           bool
   125  }
   126  
   127  // NewIntent creates an IntentHost using the given host settings. An IntentHost is a host that
   128  // does not exist yet but is intended to be picked up by the hostinit package and started. This
   129  // function takes distro information, the name of the instance, the provider of the instance and
   130  // a HostOptions and returns an IntentHost.
   131  func NewIntent(d distro.Distro, instanceName, provider string, options HostOptions) *host.Host {
   132  
   133  	creationTime := time.Now()
   134  	// proactively write all possible information pertaining
   135  	// to the host we want to create. this way, if we are unable
   136  	// to start it or record its instance id, we have a way of knowing
   137  	// something went wrong - and what
   138  	intentHost := &host.Host{
   139  		Id:               instanceName,
   140  		User:             d.User,
   141  		Distro:           d,
   142  		Tag:              instanceName,
   143  		CreationTime:     creationTime,
   144  		Status:           evergreen.HostUninitialized,
   145  		TerminationTime:  util.ZeroTime,
   146  		TaskDispatchTime: util.ZeroTime,
   147  		Provider:         provider,
   148  		StartedBy:        options.UserName,
   149  		UserHost:         options.UserHost,
   150  	}
   151  
   152  	if options.ExpirationDuration != nil {
   153  		intentHost.ExpirationTime = creationTime.Add(*options.ExpirationDuration)
   154  	}
   155  	if options.ProvisionOptions != nil {
   156  		intentHost.ProvisionOptions = options.ProvisionOptions
   157  	}
   158  	if options.UserData != "" {
   159  		intentHost.UserData = options.UserData
   160  	}
   161  
   162  	return intentHost
   163  
   164  }
   165  
   166  //CloudHost is a provider-agnostic host object that delegates methods
   167  //like status checks, ssh options, DNS name checks, termination, etc. to the
   168  //underlying provider's implementation.
   169  type CloudHost struct {
   170  	Host     *host.Host
   171  	KeyPath  string
   172  	CloudMgr CloudManager
   173  }
   174  
   175  func (cloudHost *CloudHost) IsSSHReachable() (bool, error) {
   176  	return cloudHost.CloudMgr.IsSSHReachable(cloudHost.Host, cloudHost.KeyPath)
   177  }
   178  
   179  func (cloudHost *CloudHost) IsUp() (bool, error) {
   180  	return cloudHost.CloudMgr.IsUp(cloudHost.Host)
   181  }
   182  
   183  func (cloudHost *CloudHost) TerminateInstance() error {
   184  	return cloudHost.CloudMgr.TerminateInstance(cloudHost.Host)
   185  }
   186  
   187  func (cloudHost *CloudHost) GetInstanceStatus() (CloudStatus, error) {
   188  	return cloudHost.CloudMgr.GetInstanceStatus(cloudHost.Host)
   189  }
   190  
   191  func (cloudHost *CloudHost) GetDNSName() (string, error) {
   192  	return cloudHost.CloudMgr.GetDNSName(cloudHost.Host)
   193  }
   194  
   195  func (cloudHost *CloudHost) GetSSHOptions() ([]string, error) {
   196  	return cloudHost.CloudMgr.GetSSHOptions(cloudHost.Host, cloudHost.KeyPath)
   197  }