-
Notifications
You must be signed in to change notification settings - Fork 8
/
snapshots.go
265 lines (233 loc) · 8.75 KB
/
snapshots.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
package compute
import (
"fmt"
"time"
)
const waitForSnapshotCompletePollInterval = 30 * time.Second
const waitForSnapshotCompleteTimeout = 600 * time.Second
// SnapshotsClient is a client for the Snapshot functions of the Compute API.
type SnapshotsClient struct {
ResourceClient
}
// Snapshots obtains an SnapshotsClient which can be used to access to the
// Snapshot functions of the Compute API
func (c *Client) Snapshots() *SnapshotsClient {
return &SnapshotsClient{
ResourceClient: ResourceClient{
Client: c,
ResourceDescription: "Snapshot",
ContainerPath: "/snapshot/",
ResourceRootPath: "/snapshot",
}}
}
// SnapshotState defines the constant states a snapshot can be in
type SnapshotState string
const (
// SnapshotActive - active
SnapshotActive SnapshotState = "active"
// SnapshotComplete - complete
SnapshotComplete SnapshotState = "complete"
// SnapshotQueued - queued
SnapshotQueued SnapshotState = "queued"
// SnapshotError - error
SnapshotError SnapshotState = "error"
)
// SnapshotDelay defines the constant values snapshot delay can be
type SnapshotDelay string
const (
// SnapshotDelayShutdown - shutdown
SnapshotDelayShutdown SnapshotDelay = "shutdown"
)
// Snapshot describes an existing Snapshot.
type Snapshot struct {
// Shows the default account for your identity domain.
Account string `json:"account"`
// Timestamp when this request was created.
CreationTime string `json:"creation_time"`
// Snapshot of the instance is not taken immediately.
Delay SnapshotDelay `json:"delay"`
// A description of the reason this request entered "error" state.
ErrorReason string `json:"error_reason"`
// Fully Qualified Domain Name
FQDN string `json:"name"`
// Name of the instance
Instance string `json:"instance"`
// Name of the machine image generated from the instance snapshot request.
MachineImage string `json:"machineimage"`
// Name of the instance snapshot request.
Name string
// Not used
Quota string `json:"quota"`
// The state of the request.
State SnapshotState `json:"state"`
// Uniform Resource Identifier
URI string `json:"uri"`
}
// CreateSnapshotInput defines an Snapshot to be created.
type CreateSnapshotInput struct {
// The name of the account that contains the credentials and access details of
// Oracle Storage Cloud Service. The machine image file is uploaded to the Oracle
// Storage Cloud Service account that you specify.
// Optional
Account string `json:"account,omitempty"`
// Use this option when you want to preserve the custom changes you have made
// to an instance before deleting the instance. The only permitted value is shutdown.
// Snapshot of the instance is not taken immediately. It creates a machine image which
// preserves the changes you have made to the instance, and then the instance is deleted.
// Note: This option has no effect if you shutdown the instance from inside it. Any pending
// snapshot request on that instance goes into error state. You must delete the instance
// (DELETE /instance/{name}).
// Optional
Delay SnapshotDelay `json:"delay,omitempty"`
// Name of the instance that you want to clone.
// Required
Instance string `json:"instance"`
// Specify the name of the machine image created by the snapshot request.
// Object names can contain only alphanumeric characters, hyphens, underscores, and periods.
// Object names are case-sensitive.
// If you don't specify a name for this object, then the name is generated automatically.
// Optional
MachineImage string `json:"machineimage,omitempty"`
// Time to wait between polling snapshot status
PollInterval time.Duration `json:"-"`
// Time to wait for snapshot to be completed
Timeout time.Duration `json:"-"`
}
// CreateSnapshot creates a new Snapshot
func (c *SnapshotsClient) CreateSnapshot(input *CreateSnapshotInput) (*Snapshot, error) {
input.Account = c.getQualifiedACMEName(input.Account)
input.Instance = c.getQualifiedName(input.Instance)
input.MachineImage = c.getQualifiedName(input.MachineImage)
var snapshotInfo Snapshot
if err := c.createResource(&input, &snapshotInfo); err != nil {
return nil, err
}
// Call wait for snapshot complete now, as creating the snashot is an eventually consistent operation
getInput := &GetSnapshotInput{
Name: snapshotInfo.Name,
}
if input.PollInterval == 0 {
input.PollInterval = waitForSnapshotCompletePollInterval
}
if input.Timeout == 0 {
input.Timeout = waitForSnapshotCompleteTimeout
}
// Wait for snapshot to be complete and return the result
return c.WaitForSnapshotComplete(getInput, input.PollInterval, input.Timeout)
}
// GetSnapshotInput describes the snapshot to get
type GetSnapshotInput struct {
// The name of the Snapshot
// Required
Name string `json:"name"`
}
// GetSnapshot retrieves the Snapshot with the given name.
func (c *SnapshotsClient) GetSnapshot(getInput *GetSnapshotInput) (*Snapshot, error) {
getInput.Name = c.getQualifiedName(getInput.Name)
var snapshotInfo Snapshot
if err := c.getResource(getInput.Name, &snapshotInfo); err != nil {
return nil, err
}
return c.success(&snapshotInfo)
}
// DeleteSnapshotInput describes the snapshot to delete
type DeleteSnapshotInput struct {
// The name of the Snapshot
// Required
Snapshot string
// The name of the machine image
// Required
MachineImage string
// Time to wait between polls to check snapshot status
PollInterval time.Duration
// Time to wait for snapshot to be deleted
Timeout time.Duration
}
// DeleteSnapshot deletes the Snapshot with the given name.
// A machine image gets created with the associated snapshot and needs to be deleted as well.
func (c *SnapshotsClient) DeleteSnapshot(machineImagesClient *MachineImagesClient, input *DeleteSnapshotInput) error {
// Wait for snapshot complete in case delay is active and the corresponding instance needs to be deleted first
getInput := &GetSnapshotInput{
Name: input.Snapshot,
}
if input.PollInterval == 0 {
input.PollInterval = waitForSnapshotCompletePollInterval
}
if input.Timeout == 0 {
input.Timeout = waitForSnapshotCompleteTimeout
}
if _, err := c.WaitForSnapshotComplete(getInput, input.PollInterval, input.Timeout); err != nil {
return fmt.Errorf("Could not delete snapshot: %s", err)
}
if err := c.deleteResource(input.Snapshot); err != nil {
return fmt.Errorf("Could not delete snapshot: %s", err)
}
deleteMachineImageRequest := &DeleteMachineImageInput{
Name: input.MachineImage,
}
if err := machineImagesClient.DeleteMachineImage(deleteMachineImageRequest); err != nil {
return fmt.Errorf("Could not delete machine image associated with snapshot: %s", err)
}
return nil
}
// DeleteSnapshotResourceOnly deletes the Snapshot with the given name.
// The machine image that gets created with the associated snapshot is not
// deleted by this method.
func (c *SnapshotsClient) DeleteSnapshotResourceOnly(input *DeleteSnapshotInput) error {
// Wait for snapshot complete in case delay is active and the corresponding
// instance needs to be deleted first
getInput := &GetSnapshotInput{
Name: input.Snapshot,
}
if input.PollInterval == 0 {
input.PollInterval = waitForSnapshotCompletePollInterval
}
if input.Timeout == 0 {
input.Timeout = waitForSnapshotCompleteTimeout
}
if _, err := c.WaitForSnapshotComplete(getInput, input.PollInterval, input.Timeout); err != nil {
return fmt.Errorf("Could not delete snapshot: %s", err)
}
if err := c.deleteResource(input.Snapshot); err != nil {
return fmt.Errorf("Could not delete snapshot: %s", err)
}
return nil
}
// WaitForSnapshotComplete waits for an snapshot to be completely initialized and available.
func (c *SnapshotsClient) WaitForSnapshotComplete(input *GetSnapshotInput, pollInterval, timeout time.Duration) (*Snapshot, error) {
var info *Snapshot
var getErr error
err := c.client.WaitFor("snapshot to be complete", pollInterval, timeout, func() (bool, error) {
info, getErr = c.GetSnapshot(input)
if getErr != nil {
return false, getErr
}
switch s := info.State; s {
case SnapshotError:
return false, fmt.Errorf("Error initializing snapshot: %s", info.ErrorReason)
case SnapshotComplete:
c.client.DebugLogString("Snapshot Complete")
return true, nil
case SnapshotQueued:
c.client.DebugLogString("Snapshot Queuing")
return false, nil
case SnapshotActive:
c.client.DebugLogString("Snapshot Active")
if info.Delay == SnapshotDelayShutdown {
return true, nil
}
return false, nil
default:
c.client.DebugLogString(fmt.Sprintf("Unknown snapshot state: %s, waiting", s))
return false, nil
}
})
return info, err
}
func (c *SnapshotsClient) success(snapshotInfo *Snapshot) (*Snapshot, error) {
c.unqualify(&snapshotInfo.Account)
c.unqualify(&snapshotInfo.Instance)
c.unqualify(&snapshotInfo.MachineImage)
snapshotInfo.Name = c.getUnqualifiedName(snapshotInfo.FQDN)
return snapshotInfo, nil
}