X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/855a0afbc604487ddaedaed0cc1a4ad6da34b602..ff11506c916cb2cd8abd1905e16c4d4f5ddd4240:/lib/cloud/interfaces.go diff --git a/lib/cloud/interfaces.go b/lib/cloud/interfaces.go index e3a072582b..2d53a49c51 100644 --- a/lib/cloud/interfaces.go +++ b/lib/cloud/interfaces.go @@ -5,10 +5,13 @@ package cloud import ( + "encoding/json" + "errors" "io" "time" - "git.curoverse.com/arvados.git/sdk/go/arvados" + "git.arvados.org/arvados.git/sdk/go/arvados" + "github.com/sirupsen/logrus" "golang.org/x/crypto/ssh" ) @@ -33,6 +36,7 @@ type QuotaError interface { error } +type SharedResourceTags map[string]string type InstanceSetID string type InstanceTags map[string]string type InstanceID string @@ -55,17 +59,25 @@ type Executor interface { Execute(cmd string, stdin io.Reader) (stdout, stderr []byte, err error) } +var ErrNotImplemented = errors.New("not implemented") + // An ExecutorTarget is a remote command execution service. type ExecutorTarget interface { // SSH server hostname or IP address, or empty string if // unknown while instance is booting. Address() string + // Remote username to send during SSH authentication. + RemoteUser() string + // Return nil if the given public key matches the instance's // SSH server key. If the provided Dialer is not nil, // VerifyHostKey can use it to make outgoing network // connections from the instance -- e.g., to use the cloud's // "this instance's metadata" API. + // + // Return ErrNotImplemented if no verification mechanism is + // available. VerifyHostKey(ssh.PublicKey, *ssh.Client) error } @@ -100,12 +112,18 @@ type Instance interface { // All public methods of an InstanceSet, and all public methods of the // instances it returns, are goroutine safe. type InstanceSet interface { - // Create a new instance. If supported by the driver, add the + // Create a new instance with the given type, image, and + // initial set of tags. If supported by the driver, add the // provided public key to /root/.ssh/authorized_keys. // + // The given InitCommand should be executed on the newly + // created instance. This is optional for a driver whose + // instances' VerifyHostKey() method never returns + // ErrNotImplemented. InitCommand will be under 1 KiB. + // // The returned error should implement RateLimitError and // QuotaError where applicable. - Create(arvados.InstanceType, ImageID, InstanceTags, ssh.PublicKey) (Instance, error) + Create(arvados.InstanceType, ImageID, InstanceTags, InitCommand, ssh.PublicKey) (Instance, error) // Return all instances, including ones that are booting or // shutting down. Optionally, filter out nodes that don't have @@ -123,9 +141,15 @@ type InstanceSet interface { Stop() } +type InitCommand string + // A Driver returns an InstanceSet that uses the given InstanceSetID // and driver-dependent configuration parameters. // +// If the driver creates cloud resources that aren't attached to a +// single VM instance (like SSH key pairs on AWS) and support tagging, +// they should be tagged with the provided SharedResourceTags. +// // The supplied id will be of the form "zzzzz-zzzzz-zzzzzzzzzzzzzzz" // where each z can be any alphanum. The returned InstanceSet must use // this id to tag long-lived cloud resources that it creates, and must @@ -135,13 +159,17 @@ type InstanceSet interface { // other mechanism. The tags must be visible to another instance of // the same driver running on a different host. // -// The returned InstanceSet must ignore existing resources that are -// visible but not tagged with the given id, except that it should log -// a summary of such resources -- only once -- when it starts -// up. Thus, two identically configured InstanceSets running on -// different hosts with different ids should log about the existence -// of each other's resources at startup, but will not interfere with -// each other. +// The returned InstanceSet must not modify or delete cloud resources +// unless they are tagged with the given InstanceSetID or the caller +// (dispatcher) calls Destroy() on them. It may log a summary of +// untagged resources once at startup, though. Thus, two identically +// configured InstanceSets running on different hosts with different +// ids should log about the existence of each other's resources at +// startup, but will not interfere with each other. +// +// The dispatcher always passes the InstanceSetID as a tag when +// calling Create() and Instances(), so the driver does not need to +// tag/filter VMs by InstanceSetID itself. // // Example: // @@ -152,9 +180,9 @@ type InstanceSet interface { // // type exampleDriver struct {} // -// func (*exampleDriver) InstanceSet(config map[string]interface{}, id InstanceSetID) (InstanceSet, error) { +// func (*exampleDriver) InstanceSet(config json.RawMessage, id cloud.InstanceSetID, tags cloud.SharedResourceTags, logger logrus.FieldLogger) (cloud.InstanceSet, error) { // var is exampleInstanceSet -// if err := mapstructure.Decode(config, &is); err != nil { +// if err := json.Unmarshal(config, &is); err != nil { // return nil, err // } // is.ownID = id @@ -163,17 +191,17 @@ type InstanceSet interface { // // var _ = registerCloudDriver("example", &exampleDriver{}) type Driver interface { - InstanceSet(config map[string]interface{}, id InstanceSetID) (InstanceSet, error) + InstanceSet(config json.RawMessage, id InstanceSetID, tags SharedResourceTags, logger logrus.FieldLogger) (InstanceSet, error) } // DriverFunc makes a Driver using the provided function as its // InstanceSet method. This is similar to http.HandlerFunc. -func DriverFunc(fn func(config map[string]interface{}, id InstanceSetID) (InstanceSet, error)) Driver { +func DriverFunc(fn func(config json.RawMessage, id InstanceSetID, tags SharedResourceTags, logger logrus.FieldLogger) (InstanceSet, error)) Driver { return driverFunc(fn) } -type driverFunc func(config map[string]interface{}, id InstanceSetID) (InstanceSet, error) +type driverFunc func(config json.RawMessage, id InstanceSetID, tags SharedResourceTags, logger logrus.FieldLogger) (InstanceSet, error) -func (df driverFunc) InstanceSet(config map[string]interface{}, id InstanceSetID) (InstanceSet, error) { - return df(config, id) +func (df driverFunc) InstanceSet(config json.RawMessage, id InstanceSetID, tags SharedResourceTags, logger logrus.FieldLogger) (InstanceSet, error) { + return df(config, id, tags, logger) }