1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
12 "git.curoverse.com/arvados.git/sdk/go/arvados"
13 "github.com/sirupsen/logrus"
14 "golang.org/x/crypto/ssh"
17 // A RateLimitError should be returned by an InstanceSet when the
18 // cloud service indicates it is rejecting all API calls for some time
20 type RateLimitError interface {
21 // Time before which the caller should expect requests to
23 EarliestRetry() time.Time
27 // A QuotaError should be returned by an InstanceSet when the cloud
28 // service indicates the account cannot create more VMs than already
30 type QuotaError interface {
31 // If true, don't create more instances until some existing
32 // instances are destroyed. If false, don't handle the error
38 type InstanceSetID string
39 type InstanceTags map[string]string
40 type InstanceID string
43 // An Executor executes commands on an ExecutorTarget.
44 type Executor interface {
45 // Update the set of private keys used to authenticate to
47 SetSigners(...ssh.Signer)
49 // Set the target used for subsequent command executions.
50 SetTarget(ExecutorTarget)
52 // Return the current target.
53 Target() ExecutorTarget
55 // Execute a shell command and return the resulting stdout and
56 // stderr. stdin can be nil.
57 Execute(cmd string, stdin io.Reader) (stdout, stderr []byte, err error)
60 // An ExecutorTarget is a remote command execution service.
61 type ExecutorTarget interface {
62 // SSH server hostname or IP address, or empty string if
63 // unknown while instance is booting.
66 // Remote username to send during SSH authentication.
69 // Return nil if the given public key matches the instance's
70 // SSH server key. If the provided Dialer is not nil,
71 // VerifyHostKey can use it to make outgoing network
72 // connections from the instance -- e.g., to use the cloud's
73 // "this instance's metadata" API.
74 VerifyHostKey(ssh.PublicKey, *ssh.Client) error
77 // Instance is implemented by the provider-specific instance types.
78 type Instance interface {
81 // ID returns the provider's instance ID. It must be stable
82 // for the life of the instance.
85 // String typically returns the cloud-provided instance ID.
88 // Cloud provider's "instance type" ID. Matches a ProviderType
89 // in the cluster's InstanceTypes configuration.
95 // Replace tags with the given tags
96 SetTags(InstanceTags) error
102 // An InstanceSet manages a set of VM instances created by an elastic
103 // cloud provider like AWS, GCE, or Azure.
105 // All public methods of an InstanceSet, and all public methods of the
106 // instances it returns, are goroutine safe.
107 type InstanceSet interface {
108 // Create a new instance. If supported by the driver, add the
109 // provided public key to /root/.ssh/authorized_keys.
111 // The returned error should implement RateLimitError and
112 // QuotaError where applicable.
113 Create(arvados.InstanceType, ImageID, InstanceTags, ssh.PublicKey) (Instance, error)
115 // Return all instances, including ones that are booting or
116 // shutting down. Optionally, filter out nodes that don't have
117 // all of the given InstanceTags (the caller will ignore these
120 // An instance returned by successive calls to Instances() may
121 // -- but does not need to -- be represented by the same
122 // Instance object each time. Thus, the caller is responsible
123 // for de-duplicating the returned instances by comparing the
124 // InstanceIDs returned by the instances' ID() methods.
125 Instances(InstanceTags) ([]Instance, error)
127 // Stop any background tasks and release other resources.
131 // A Driver returns an InstanceSet that uses the given InstanceSetID
132 // and driver-dependent configuration parameters.
134 // The supplied id will be of the form "zzzzz-zzzzz-zzzzzzzzzzzzzzz"
135 // where each z can be any alphanum. The returned InstanceSet must use
136 // this id to tag long-lived cloud resources that it creates, and must
137 // assume control of any existing resources that are tagged with the
138 // same id. Tagging can be accomplished by including the ID in
139 // resource names, using the cloud provider's tagging feature, or any
140 // other mechanism. The tags must be visible to another instance of
141 // the same driver running on a different host.
143 // The returned InstanceSet must ignore existing resources that are
144 // visible but not tagged with the given id, except that it should log
145 // a summary of such resources -- only once -- when it starts
146 // up. Thus, two identically configured InstanceSets running on
147 // different hosts with different ids should log about the existence
148 // of each other's resources at startup, but will not interfere with
153 // type exampleInstanceSet struct {
158 // type exampleDriver struct {}
160 // func (*exampleDriver) InstanceSet(config json.RawMessage, id InstanceSetID) (InstanceSet, error) {
161 // var is exampleInstanceSet
162 // if err := json.Unmarshal(config, &is); err != nil {
169 // var _ = registerCloudDriver("example", &exampleDriver{})
170 type Driver interface {
171 InstanceSet(config json.RawMessage, id InstanceSetID, logger logrus.FieldLogger) (InstanceSet, error)
174 // DriverFunc makes a Driver using the provided function as its
175 // InstanceSet method. This is similar to http.HandlerFunc.
176 func DriverFunc(fn func(config json.RawMessage, id InstanceSetID, logger logrus.FieldLogger) (InstanceSet, error)) Driver {
177 return driverFunc(fn)
180 type driverFunc func(config json.RawMessage, id InstanceSetID, logger logrus.FieldLogger) (InstanceSet, error)
182 func (df driverFunc) InstanceSet(config json.RawMessage, id InstanceSetID, logger logrus.FieldLogger) (InstanceSet, error) {
183 return df(config, id, logger)