Skip to content

Commit 41c17c0

Browse files
author
Harsh Desai
committed
New compute methods for vSphere, storage operations by instance ID, misc vSphere fixes
- Fix errors messages for vSphere when disk is not attached - Add InspectInstance implementation for vSphere - Add new methods, ListInstances, CreateInstance, AttachByInstanceID - DeviceMappings can now be done by instance ID - Remove timeout from DeleteInstance. Callers should use exponentional backoff instead - Add attach options to Attach method Signed-off-by: Harsh Desai <harsh@portworx.com>
1 parent 098cedc commit 41c17c0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

85 files changed

+7124
-1531
lines changed

Gopkg.lock

Lines changed: 6 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Gopkg.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363

6464
[[constraint]]
6565
name = "github.com/vmware/govmomi"
66-
version = "v0.15.0"
66+
version = "v0.18.0"
6767

6868
[[constraint]]
6969
branch = "master"

aws/aws.go

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ const (
3535
)
3636

3737
type awsOps struct {
38+
cloudops.Storage
3839
cloudops.Compute
3940
instanceType string
4041
instance string
@@ -337,12 +338,21 @@ func (s *awsOps) matchTag(tag *ec2.Tag, match string) bool {
337338
*tag.Key == match
338339
}
339340

340-
func (s *awsOps) DeviceMappings() (map[string]string, error) {
341-
instance, err := s.describe()
341+
func (s *awsOps) DeviceMappings(instanceID string) (map[string]string, error) {
342+
var (
343+
instance *ec2.Instance
344+
err error
345+
)
346+
347+
if len(instanceID) == 0 {
348+
instance, err = s.describe()
349+
} else {
350+
instance, err = DescribeInstanceByID(s.ec2, instanceID)
351+
}
352+
342353
if err != nil {
343354
return nil, err
344355
}
345-
346356
m := make(map[string]string)
347357
for _, d := range instance.BlockDeviceMappings {
348358
if d.DeviceName != nil && d.Ebs != nil && d.Ebs.VolumeId != nil {
@@ -769,7 +779,7 @@ func (s *awsOps) Delete(id string) error {
769779
return err
770780
}
771781

772-
func (s *awsOps) Attach(volumeID string) (string, error) {
782+
func (s *awsOps) Attach(volumeID string, opts map[string]string) (string, error) {
773783
s.mutex.Lock()
774784
defer s.mutex.Unlock()
775785

@@ -817,6 +827,12 @@ func (s *awsOps) Attach(volumeID string) (string, error) {
817827

818828
return "", fmt.Errorf("failed to attach any of the free devices. Attempted: %v", devices)
819829
}
830+
func (s *awsOps) AttachByInstanceID(
831+
instanceID, volumeID string, options map[string]string) (string, error) {
832+
return "", &cloudops.ErrNotSupported{
833+
Operation: "AttachByInstanceID",
834+
}
835+
}
820836

821837
func (s *awsOps) Detach(volumeID string) error {
822838
return s.detachInternal(volumeID, s.instance)

aws/aws_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ func TestAll(t *testing.T) {
4040
t.Skipf("skipping AWS tests as environment is not set...\n")
4141
}
4242

43-
test.RunTest(drivers, diskTemplates, t)
43+
test.RunTest(drivers, diskTemplates, nil, t)
4444
}
4545

4646
func TestAwsGetPrefixFromRootDeviceName(t *testing.T) {

azure/azure.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ func (a *azureOps) GetDeviceID(disk interface{}) (string, error) {
192192
)
193193
}
194194

195-
func (a *azureOps) Attach(diskName string) (string, error) {
195+
func (a *azureOps) Attach(diskName string, opts map[string]string) (string, error) {
196196
disk, err := a.checkDiskAttachmentStatus(diskName)
197197
if err == nil {
198198
// Disk is already attached locally, return device path
@@ -232,6 +232,13 @@ func (a *azureOps) Attach(diskName string) (string, error) {
232232
return a.waitForAttach(diskName)
233233
}
234234

235+
func (s *azureOps) AttachByInstanceID(
236+
instanceID, volumeID string, options map[string]string) (string, error) {
237+
return "", &cloudops.ErrNotSupported{
238+
Operation: "AttachByInstanceID",
239+
}
240+
}
241+
235242
func (a *azureOps) handleAttachError(err error) error {
236243
if de, ok := err.(autorest.DetailedError); ok {
237244
if re, ok := de.Original.(azure.RequestError); ok &&
@@ -370,7 +377,13 @@ func (a *azureOps) Inspect(diskNames []*string) ([]interface{}, error) {
370377
return disks, nil
371378
}
372379

373-
func (a *azureOps) DeviceMappings() (map[string]string, error) {
380+
func (a *azureOps) DeviceMappings(instanceID string) (map[string]string, error) {
381+
if len(instanceID) > 0 {
382+
return nil, &cloudops.ErrNotSupported{
383+
Operation: "DeviceMappings",
384+
Reason: "API currently does not support providing instanceID",
385+
}
386+
}
374387
dataDisks, err := a.vmsClient.getDataDisks(a.instance)
375388
if err != nil {
376389
return nil, err

azure/azure_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,5 +59,5 @@ func TestAll(t *testing.T) {
5959
d, disks := initAzure(t)
6060
drivers[d.Name()] = d
6161
diskTemplates[d.Name()] = disks
62-
test.RunTest(drivers, diskTemplates, t)
62+
test.RunTest(drivers, diskTemplates, nil, t)
6363
}

backoff/exponential.go

Lines changed: 73 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,56 @@ func (e *exponentialBackoff) InspectInstance(instanceID string) (*cloudops.Insta
7272

7373
}
7474

75+
func (e *exponentialBackoff) CreateInstance(template interface{}) (*cloudops.InstanceInfo, error) {
76+
var (
77+
instanceInfo *cloudops.InstanceInfo
78+
origErr error
79+
)
80+
conditionFn := func() (bool, error) {
81+
instanceInfo, origErr = e.cloudOps.CreateInstance(template)
82+
msg := fmt.Sprintf("Failed to create instance: %v.", template)
83+
return e.handleError(origErr, msg)
84+
}
85+
expErr := wait.ExponentialBackoff(e.backoff, conditionFn)
86+
if expErr == wait.ErrWaitTimeout {
87+
return nil, cloudops.NewStorageError(cloudops.ErrExponentialTimeout, origErr.Error(), "")
88+
}
89+
return instanceInfo, origErr
90+
}
91+
92+
func (e *exponentialBackoff) ListInstances(opts *cloudops.ListInstancesOpts) (
93+
[]*cloudops.InstanceInfo, error) {
94+
95+
var (
96+
instanceInfos []*cloudops.InstanceInfo
97+
origErr error
98+
)
99+
conditionFn := func() (bool, error) {
100+
instanceInfos, origErr = e.cloudOps.ListInstances(opts)
101+
msg := fmt.Sprintf("Failed to list instances with options: %v.", opts)
102+
return e.handleError(origErr, msg)
103+
}
104+
expErr := wait.ExponentialBackoff(e.backoff, conditionFn)
105+
if expErr == wait.ErrWaitTimeout {
106+
return nil, cloudops.NewStorageError(cloudops.ErrExponentialTimeout, origErr.Error(), "")
107+
}
108+
return instanceInfos, origErr
109+
}
110+
111+
func (e *exponentialBackoff) DeleteInstance(instanceID string, zone string) error {
112+
var origErr error
113+
conditionFn := func() (bool, error) {
114+
origErr = e.cloudOps.DeleteInstance(instanceID, zone)
115+
msg := fmt.Sprintf("Failed to inspect instance: %v.", instanceID)
116+
return e.handleError(origErr, msg)
117+
}
118+
expErr := wait.ExponentialBackoff(e.backoff, conditionFn)
119+
if expErr == wait.ErrWaitTimeout {
120+
return cloudops.NewStorageError(cloudops.ErrExponentialTimeout, origErr.Error(), "")
121+
}
122+
return origErr
123+
}
124+
75125
func (e *exponentialBackoff) InspectInstanceGroupForInstance(instanceID string) (*cloudops.InstanceGroupInfo, error) {
76126
var (
77127
instanceGroupInfo *cloudops.InstanceGroupInfo
@@ -140,23 +190,6 @@ func (e *exponentialBackoff) GetClusterSizeForInstance(instanceID string) (int64
140190

141191
}
142192

143-
func (e *exponentialBackoff) DeleteInstance(instanceID string, zone string, timeout time.Duration) error {
144-
var (
145-
origErr error
146-
)
147-
conditionFn := func() (bool, error) {
148-
origErr = e.cloudOps.DeleteInstance(instanceID, zone, timeout)
149-
msg := fmt.Sprintf("Failed to delete instance: %v.", instanceID)
150-
return e.handleError(origErr, msg)
151-
}
152-
expErr := wait.ExponentialBackoff(e.backoff, conditionFn)
153-
if expErr == wait.ErrWaitTimeout {
154-
return cloudops.NewStorageError(cloudops.ErrExponentialTimeout, origErr.Error(), "")
155-
}
156-
return origErr
157-
158-
}
159-
160193
// Create volume based on input template volume and also apply given labels.
161194
func (e *exponentialBackoff) Create(template interface{}, labels map[string]string) (interface{}, error) {
162195
var (
@@ -183,13 +216,13 @@ func (e *exponentialBackoff) GetDeviceID(template interface{}) (string, error) {
183216

184217
// Attach volumeID.
185218
// Return attach path.
186-
func (e *exponentialBackoff) Attach(volumeID string) (string, error) {
219+
func (e *exponentialBackoff) Attach(volumeID string, options map[string]string) (string, error) {
187220
var (
188221
devPath string
189222
origErr error
190223
)
191224
conditionFn := func() (bool, error) {
192-
devPath, origErr = e.cloudOps.Attach(volumeID)
225+
devPath, origErr = e.cloudOps.Attach(volumeID, options)
193226
msg := fmt.Sprintf("Failed to attach drive (%v).", volumeID)
194227
return e.handleError(origErr, msg)
195228
}
@@ -200,11 +233,28 @@ func (e *exponentialBackoff) Attach(volumeID string) (string, error) {
200233
return devPath, origErr
201234
}
202235

203-
// Detach volumeID.
204-
func (e *exponentialBackoff) Detach(volumeID string) error {
236+
func (e *exponentialBackoff) AttachByInstanceID(
237+
instanceID, volumeID string,
238+
options map[string]string) (string, error) {
205239
var (
240+
devPath string
206241
origErr error
207242
)
243+
conditionFn := func() (bool, error) {
244+
devPath, origErr = e.cloudOps.AttachByInstanceID(instanceID, volumeID, options)
245+
msg := fmt.Sprintf("Failed to attach drive (%v) on %s", volumeID, instanceID)
246+
return e.handleError(origErr, msg)
247+
}
248+
expErr := wait.ExponentialBackoff(e.backoff, conditionFn)
249+
if expErr == wait.ErrWaitTimeout {
250+
return "", cloudops.NewStorageError(cloudops.ErrExponentialTimeout, origErr.Error(), "")
251+
}
252+
return devPath, origErr
253+
}
254+
255+
// Detach volumeID.
256+
func (e *exponentialBackoff) Detach(volumeID string) error {
257+
var origErr error
208258
conditionFn := func() (bool, error) {
209259
origErr = e.cloudOps.Detach(volumeID)
210260
msg := fmt.Sprintf("Failed to detach drive (%v).", volumeID)
@@ -312,13 +362,13 @@ func (e *exponentialBackoff) Inspect(volumeIds []*string) ([]interface{}, error)
312362
}
313363

314364
// DeviceMappings returns map[local_attached_volume_path]->volume ID/NAME
315-
func (e *exponentialBackoff) DeviceMappings() (map[string]string, error) {
365+
func (e *exponentialBackoff) DeviceMappings(instanceID string) (map[string]string, error) {
316366
var (
317367
mappings map[string]string
318368
origErr error
319369
)
320370
conditionFn := func() (bool, error) {
321-
mappings, origErr = e.cloudOps.DeviceMappings()
371+
mappings, origErr = e.cloudOps.DeviceMappings(instanceID)
322372
msg := fmt.Sprintf("Failed to get device mappings.")
323373
return e.handleError(origErr, msg)
324374
}

cloudops.go

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,15 @@ type InstanceInfo struct {
6161

6262
// Compute interface to manage compute instances.
6363
type Compute interface {
64-
// DeleteInstance deletes the instance
65-
DeleteInstance(instanceID string, zone string, timeout time.Duration) error
6664
// InstanceID of instance where command is executed.
6765
InstanceID() string
66+
// CreateInstance creates a cloud instance with the given template
67+
CreateInstance(template interface{}) (*InstanceInfo, error)
68+
// DeleteInstance deletes a cloud instance with given ID/name and waits
69+
// until given timeout to check if the instance got deleted
70+
DeleteInstance(instanceID string, zone string) error
71+
// ListInstances lists instances in the cloud provider with given options
72+
ListInstances(opts *ListInstancesOpts) ([]*InstanceInfo, error)
6873
// InspectInstance inspects the node with the given instance ID
6974
// TODO: Add support for taking zone as input to inspect instance in any zone
7075
InspectInstance(instanceID string) (*InstanceInfo, error)
@@ -90,8 +95,13 @@ type Storage interface {
9095
// GetDeviceID returns ID/Name of the given device/disk or snapshot
9196
GetDeviceID(template interface{}) (string, error)
9297
// Attach volumeID.
98+
// options are passthough options given to the cloud provider
9399
// Return attach path.
94-
Attach(volumeID string) (string, error)
100+
Attach(volumeID string, options map[string]string) (string, error)
101+
// AttachByInstanceID attaches diskPath to instance with given ID.
102+
// options are passthough options given to the cloud provider
103+
// Return attach path.
104+
AttachByInstanceID(instanceID, volumeID string, options map[string]string) (string, error)
95105
// Detach volumeID.
96106
Detach(volumeID string) error
97107
// DetachFrom detaches the disk/volume with given ID from the given instance ID
@@ -109,7 +119,10 @@ type Storage interface {
109119
// Inspect volumes specified by volumeID
110120
Inspect(volumeIds []*string) ([]interface{}, error)
111121
// DeviceMappings returns map[local_attached_volume_path]->volume ID/NAME
112-
DeviceMappings() (map[string]string, error)
122+
// instanceID is the ID of the instance where you want to check the device mappings
123+
// If not provided, it returns the device mappings of the instance where it's
124+
// invoked
125+
DeviceMappings(instanceID string) (map[string]string, error)
113126
// Enumerate volumes that match given filters. Organize them into
114127
// sets identified by setIdentifier.
115128
// labels can be nil, setIdentifier can be empty string.
@@ -140,3 +153,14 @@ type Ops interface {
140153
// Compute operations in the cloud
141154
Compute
142155
}
156+
157+
// ListInstancesOpts are options for the list instances call
158+
type ListInstancesOpts struct {
159+
// LabelSelector to filter the instances that are listed. The labels are
160+
// interpreted by the cloud provider
161+
LabelSelector map[string]string
162+
// NamePrefix if provided will be used to return all instances whose name
163+
// starts with the given prefix. This should be used in cloud providers that
164+
// don't support labels in instances
165+
NamePrefix string
166+
}

errors.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,23 @@ const (
2020
ErrExponentialTimeout
2121
)
2222

23+
// ErrInvalidArgument is the error type to use when invalid args as given to APIs
24+
type ErrInvalidArgument struct {
25+
// Operation is the operation that was being performed
26+
Operation string
27+
// Reason is an optional reason explaining the invalid argument error
28+
Reason string
29+
}
30+
31+
func (e *ErrInvalidArgument) Error() string {
32+
errString := fmt.Sprintf("Operation: %s was given invalid argument", e.Operation)
33+
if len(e.Reason) > 0 {
34+
errString = fmt.Sprintf("%s. Reason: %s", errString, e.Reason)
35+
}
36+
37+
return errString
38+
}
39+
2340
// ErrNotSupported is the error type for unsupported operations
2441
type ErrNotSupported struct {
2542
// Operation is the operation not being supported

0 commit comments

Comments
 (0)