File: machines.go

package info (click to toggle)
golang-github-joyent-gosdc 0.0~git20161202.ec8b350-3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, forky, sid, trixie
  • size: 472 kB
  • sloc: makefile: 3
file content (307 lines) | stat: -rw-r--r-- 11,334 bytes parent folder | download
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
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
package cloudapi

import (
	"encoding/json"
	"fmt"
	"net/http"

	"strings"

	"github.com/joyent/gocommon/client"
	"github.com/joyent/gocommon/errors"
)

// Machine represent a provisioned virtual machines
type Machine struct {
	Id              string            // Unique identifier for the image
	Name            string            // Machine friendly name
	Type            string            // Machine type, one of 'smartmachine' or 'virtualmachine'
	State           string            // Current state of the machine
	Dataset         string            // The dataset URN the machine was provisioned with. For new images/datasets this value will be the dataset id, i.e, same value than the image attribute
	Memory          int               // The amount of memory the machine has (in Mb)
	Disk            int               // The amount of disk the machine has (in Gb)
	IPs             []string          // The IP addresses the machine has
	Metadata        map[string]string // Map of the machine metadata, e.g. authorized-keys
	Tags            map[string]string // Map of the machine tags
	Created         string            // When the machine was created
	Updated         string            // When the machine was updated
	Package         string            // The name of the package used to create the machine
	Image           string            // The image id the machine was provisioned with
	PrimaryIP       string            // The primary (public) IP address for the machine
	Networks        []string          // The network IDs for the machine
	FirewallEnabled bool              `json:"firewall_enabled"` // whether or not the firewall is enabled
	DomainNames     []string          `json:"dns_names"` // The domain names of this machine
}

// Equals compares two machines. Ignores state and timestamps.
func (m Machine) Equals(other Machine) bool {
	if m.Id == other.Id && m.Name == other.Name && m.Type == other.Type && m.Dataset == other.Dataset &&
		m.Memory == other.Memory && m.Disk == other.Disk && m.Package == other.Package && m.Image == other.Image &&
		m.compareIPs(other) && m.compareMetadata(other) {
		return true
	}
	return false
}

// Helper method to compare two machines IPs
func (m Machine) compareIPs(other Machine) bool {
	if len(m.IPs) != len(other.IPs) {
		return false
	}
	for i, v := range m.IPs {
		if v != other.IPs[i] {
			return false
		}
	}
	return true
}

// Helper method to compare two machines metadata
func (m Machine) compareMetadata(other Machine) bool {
	if len(m.Metadata) != len(other.Metadata) {
		return false
	}
	for k, v := range m.Metadata {
		if v != other.Metadata[k] {
			return false
		}
	}
	return true
}

// CreateMachineOpts represent the option that can be specified
// when creating a new machine.
type CreateMachineOpts struct {
	Name            string            `json:"name"`             // Machine friendly name, default is a randomly generated name
	Package         string            `json:"package"`          // Name of the package to use on provisioning
	Image           string            `json:"image"`            // The image UUID
	Networks        []string          `json:"networks"`         // Desired networks IDs
	Metadata        map[string]string `json:"-"`                // An arbitrary set of metadata key/value pairs can be set at provision time
	Tags            map[string]string `json:"-"`                // An arbitrary set of tags can be set at provision time
	FirewallEnabled bool              `json:"firewall_enabled"` // Completely enable or disable firewall for this machine (new in API version 7.0)
}

// AuditAction represents an action/event accomplished by a machine.
type AuditAction struct {
	Action     string                 // Action name
	Parameters map[string]interface{} // Original set of parameters sent when the action was requested
	Time       string                 // When the action finished
	Success    string                 // Either 'yes' or 'no', depending on the action successfulness
	Caller     Caller                 // Account requesting the action
}

// Caller represents an account requesting an action.
type Caller struct {
	Type  string // Authentication type for the action request. One of 'basic', 'operator', 'signature' or 'token'
	User  string // When the authentication type is 'basic', this member will be present and include user login
	IP    string // The IP addresses this from which the action was requested. Not present if type is 'operator'
	KeyId string // When authentication type is either 'signature' or 'token', SSH key identifier
}

// appendJSON marshals the given attribute value and appends it as an encoded value to the given json data.
// The newly encode (attr, value) is inserted just before the closing "}" in the json data.
func appendJSON(data []byte, attr string, value interface{}) ([]byte, error) {
	newData, err := json.Marshal(&value)
	if err != nil {
		return nil, err
	}
	strData := string(data)
	result := fmt.Sprintf(`%s, "%s":%s}`, strData[:len(strData)-1], attr, string(newData))
	return []byte(result), nil
}

type jsonOpts CreateMachineOpts

// MarshalJSON turns the given CreateMachineOpts into JSON
func (opts CreateMachineOpts) MarshalJSON() ([]byte, error) {
	jo := jsonOpts(opts)
	data, err := json.Marshal(&jo)
	if err != nil {
		return nil, err
	}
	for k, v := range opts.Tags {
		if !strings.HasPrefix(k, "tag.") {
			k = "tag." + k
		}
		data, err = appendJSON(data, k, v)
		if err != nil {
			return nil, err
		}
	}
	for k, v := range opts.Metadata {
		if !strings.HasPrefix(k, "metadata.") {
			k = "metadata." + k
		}
		data, err = appendJSON(data, k, v)
		if err != nil {
			return nil, err
		}
	}
	return data, nil
}

// ListMachines lists all machines on record for an account.
// You can paginate this API by passing in offset, and limit
// See API docs: http://apidocs.joyent.com/cloudapi/#ListMachines
func (c *Client) ListMachines(filter *Filter) ([]Machine, error) {
	var resp []Machine
	req := request{
		method: client.GET,
		url:    apiMachines,
		filter: filter,
		resp:   &resp,
	}
	if _, err := c.sendRequest(req); err != nil {
		return nil, errors.Newf(err, "failed to get list of machines")
	}
	return resp, nil
}

// CountMachines returns the number of machines on record for an account.
// See API docs: http://apidocs.joyent.com/cloudapi/#ListMachines
func (c *Client) CountMachines() (int, error) {
	var resp int
	req := request{
		method: client.HEAD,
		url:    apiMachines,
		resp:   &resp,
	}
	if _, err := c.sendRequest(req); err != nil {
		return -1, errors.Newf(err, "failed to get count of machines")
	}
	return resp, nil
}

// GetMachine returns the machine specified by machineId.
// See API docs: http://apidocs.joyent.com/cloudapi/#GetMachine
func (c *Client) GetMachine(machineID string) (*Machine, error) {
	var resp Machine
	req := request{
		method: client.GET,
		url:    makeURL(apiMachines, machineID),
		resp:   &resp,
	}
	if _, err := c.sendRequest(req); err != nil {
		return nil, errors.Newf(err, "failed to get machine with id: %s", machineID)
	}
	return &resp, nil
}

// CreateMachine creates a new machine with the options specified.
// See API docs: http://apidocs.joyent.com/cloudapi/#CreateMachine
func (c *Client) CreateMachine(opts CreateMachineOpts) (*Machine, error) {
	var resp Machine
	req := request{
		method:         client.POST,
		url:            apiMachines,
		reqValue:       opts,
		resp:           &resp,
		expectedStatus: http.StatusCreated,
	}
	if _, err := c.sendRequest(req); err != nil {
		return nil, errors.Newf(err, "failed to create machine with name: %s", opts.Name)
	}
	return &resp, nil
}

// StopMachine stops a running machine.
// See API docs: http://apidocs.joyent.com/cloudapi/#StopMachine
func (c *Client) StopMachine(machineID string) error {
	req := request{
		method:         client.POST,
		url:            fmt.Sprintf("%s/%s?action=%s", apiMachines, machineID, actionStop),
		expectedStatus: http.StatusAccepted,
	}
	if _, err := c.sendRequest(req); err != nil {
		return errors.Newf(err, "failed to stop machine with id: %s", machineID)
	}
	return nil
}

// StartMachine starts a stopped machine.
// See API docs: http://apidocs.joyent.com/cloudapi/#StartMachine
func (c *Client) StartMachine(machineID string) error {
	req := request{
		method:         client.POST,
		url:            fmt.Sprintf("%s/%s?action=%s", apiMachines, machineID, actionStart),
		expectedStatus: http.StatusAccepted,
	}
	if _, err := c.sendRequest(req); err != nil {
		return errors.Newf(err, "failed to start machine with id: %s", machineID)
	}
	return nil
}

// RebootMachine reboots (stop followed by a start) a machine.
// See API docs: http://apidocs.joyent.com/cloudapi/#RebootMachine
func (c *Client) RebootMachine(machineID string) error {
	req := request{
		method:         client.POST,
		url:            fmt.Sprintf("%s/%s?action=%s", apiMachines, machineID, actionReboot),
		expectedStatus: http.StatusAccepted,
	}
	if _, err := c.sendRequest(req); err != nil {
		return errors.Newf(err, "failed to reboot machine with id: %s", machineID)
	}
	return nil
}

// ResizeMachine allows you to resize a SmartMachine. Virtual machines can also
// be resized, but only resizing virtual machines to a higher capacity package
// is supported.
// See API docs: http://apidocs.joyent.com/cloudapi/#ResizeMachine
func (c *Client) ResizeMachine(machineID, packageName string) error {
	req := request{
		method:         client.POST,
		url:            fmt.Sprintf("%s/%s?action=%s&package=%s", apiMachines, machineID, actionResize, packageName),
		expectedStatus: http.StatusAccepted,
	}
	if _, err := c.sendRequest(req); err != nil {
		return errors.Newf(err, "failed to resize machine with id: %s", machineID)
	}
	return nil
}

// RenameMachine renames an existing machine.
// See API docs: http://apidocs.joyent.com/cloudapi/#RenameMachine
func (c *Client) RenameMachine(machineID, machineName string) error {
	req := request{
		method:         client.POST,
		url:            fmt.Sprintf("%s/%s?action=%s&name=%s", apiMachines, machineID, actionRename, machineName),
		expectedStatus: http.StatusAccepted,
	}
	if _, err := c.sendRequest(req); err != nil {
		return errors.Newf(err, "failed to rename machine with id: %s", machineID)
	}
	return nil
}

// DeleteMachine allows you to completely destroy a machine. Machine must be in the 'stopped' state.
// See API docs: http://apidocs.joyent.com/cloudapi/#DeleteMachine
func (c *Client) DeleteMachine(machineID string) error {
	req := request{
		method:         client.DELETE,
		url:            makeURL(apiMachines, machineID),
		expectedStatus: http.StatusNoContent,
	}
	if _, err := c.sendRequest(req); err != nil {
		return errors.Newf(err, "failed to delete machine with id %s", machineID)
	}
	return nil
}

// MachineAudit provides a list of machine's accomplished actions, (sorted from
// latest to older one).
// See API docs: http://apidocs.joyent.com/cloudapi/#MachineAudit
func (c *Client) MachineAudit(machineID string) ([]AuditAction, error) {
	var resp []AuditAction
	req := request{
		method: client.GET,
		url:    makeURL(apiMachines, machineID, apiAudit),
		resp:   &resp,
	}
	if _, err := c.sendRequest(req); err != nil {
		return nil, errors.Newf(err, "failed to get actions for machine with id %s", machineID)
	}
	return resp, nil
}