2
// gosdc - Go library to interact with the Joyent CloudAPI
4
// CloudAPI double testing service - internal direct API implementation
6
// Copyright (c) 2013 Joyent Inc.
8
// Written by Daniele Stroppa <daniele.stroppa@joyent.com>
21
"github.com/joyent/gosdc/cloudapi"
22
"github.com/joyent/gosdc/localservices"
27
packagesFilters = []string{"name", "memory", "disk", "swap", "version", "vcpus", "group"}
28
imagesFilters = []string{"name", "os", "version", "public", "state", "owner", "type"}
29
machinesFilters = []string{"type", "name", "image", "state", "memory", "tombstone", "limit", "offset", "credentials"}
32
type CloudAPI struct {
33
localservices.ServiceInstance
35
packages []cloudapi.Package
36
images []cloudapi.Image
37
machines []*cloudapi.Machine
38
machineFw map[string]bool
39
snapshots map[string][]cloudapi.Snapshot
40
firewallRules []*cloudapi.FirewallRule
41
networks []cloudapi.Network
44
func New(serviceURL, userAccount string) *CloudAPI {
45
URL, err := url.Parse(serviceURL)
50
if !strings.HasSuffix(hostname, separator) {
54
keys := make([]cloudapi.Key, 0)
55
machines := make([]*cloudapi.Machine, 0)
56
machineFw := make(map[string]bool)
57
snapshots := make(map[string][]cloudapi.Snapshot)
58
firewallRules := make([]*cloudapi.FirewallRule, 0)
60
cloudapiService := &CloudAPI{
62
packages: initPackages(),
67
firewallRules: firewallRules,
68
networks: []cloudapi.Network{
69
{Id: "123abc4d-0011-aabb-2233-ccdd4455", Name: "Test-Joyent-Public", Public: true},
70
{Id: "456def0a-33ff-7f8e-9a0b-33bb44cc", Name: "Test-Joyent-Private", Public: false},
72
ServiceInstance: localservices.ServiceInstance{
75
UserAccount: userAccount,
79
return cloudapiService
82
func initPackages() []cloudapi.Package {
83
return []cloudapi.Package{
91
Id: "12345678-aaaa-bbbb-cccc-000000000000",
101
Id: "11223344-1212-abab-3434-aabbccddeeff",
111
Id: "aabbccdd-abcd-abcd-abcd-112233445566",
121
Id: "00998877-dddd-eeee-ffff-111111111111",
127
func initImages() []cloudapi.Image {
128
return []cloudapi.Image{
130
Id: "12345678-a1a1-b2b2-c3c3-098765432100",
134
Type: "smartmachine",
135
Description: "Test SmartOS image (32 bit)",
136
Homepage: "http://test.joyent.com/Standard_Instance",
137
PublishedAt: "2014-01-08T17:42:31Z",
142
Id: "12345678-b1b1-a4a4-d8d8-111111999999",
146
Type: "smartmachine",
147
Description: "Test SmartOS image (64 bit)",
148
Homepage: "http://test.joyent.com/Standard_Instance",
149
PublishedAt: "2014-01-08T17:43:16Z",
154
Id: "a1b2c3d4-0011-2233-4455-0f1e2d3c4b5a",
158
Type: "virtualmachine",
159
Description: "Test CentOS 6.4 image (64 bit)",
160
PublishedAt: "2014-01-02T10:58:31Z",
165
Id: "11223344-0a0a-ff99-11bb-0a1b2c3d4e5f",
169
Type: "virtualmachine",
170
Description: "Test Ubuntu 12.04 image (64 bit)",
171
PublishedAt: "2014-01-20T16:12:31Z",
176
Id: "11223344-0a0a-ee88-22ab-00aa11bb22cc",
180
Type: "virtualmachine",
181
Description: "Test Ubuntu 12.10 image (64 bit)",
182
PublishedAt: "2014-01-20T16:12:31Z",
187
Id: "11223344-0a0a-dd77-33cd-abcd1234e5f6",
191
Type: "virtualmachine",
192
Description: "Test Ubuntu 13.04 image (64 bit)",
193
PublishedAt: "2014-01-20T16:12:31Z",
200
func generatePublicIPAddress() string {
201
r := rand.New(rand.NewSource(time.Now().UnixNano()))
202
return fmt.Sprintf("32.151.%d.%d", r.Intn(255), r.Intn(255))
205
func generatePrivateIPAddress() string {
206
r := rand.New(rand.NewSource(time.Now().UnixNano()))
207
return fmt.Sprintf("10.201.%d.%d", r.Intn(255), r.Intn(255))
210
func contains(list []string, elem string) bool {
211
for _, t := range list {
220
func (c *CloudAPI) ListKeys() ([]cloudapi.Key, error) {
221
if err := c.ProcessFunctionHook(c); err != nil {
228
func (c *CloudAPI) GetKey(keyName string) (*cloudapi.Key, error) {
229
if err := c.ProcessFunctionHook(c, keyName); err != nil {
233
for _, key := range c.keys {
234
if key.Name == keyName {
239
return nil, fmt.Errorf("Key %s not found", keyName)
242
func (c *CloudAPI) CreateKey(keyName, key string) (*cloudapi.Key, error) {
243
if err := c.ProcessFunctionHook(c, keyName, key); err != nil {
247
// check if key already exists or keyName already in use
248
for _, k := range c.keys {
249
if k.Name == keyName {
250
return nil, fmt.Errorf("Key name %s already in use", keyName)
253
return nil, fmt.Errorf("Key %s already exists", key)
257
newKey := cloudapi.Key{Name: keyName, Fingerprint: "", Key: key}
258
c.keys = append(c.keys, newKey)
263
func (c *CloudAPI) DeleteKey(keyName string) error {
264
if err := c.ProcessFunctionHook(c, keyName); err != nil {
268
for i, key := range c.keys {
269
if key.Name == keyName {
270
c.keys = append(c.keys[:i], c.keys[i+1:]...)
275
return fmt.Errorf("Key %s not found", keyName)
279
func (c *CloudAPI) ListPackages(filters map[string]string) ([]cloudapi.Package, error) {
280
if err := c.ProcessFunctionHook(c, filters); err != nil {
284
availablePackages := c.packages
287
for k, f := range filters {
288
// check if valid filter
289
if contains(packagesFilters, k) {
290
pkgs := []cloudapi.Package{}
291
// filter from availablePackages and add to pkgs
292
for _, p := range availablePackages {
293
if k == "name" && p.Name == f {
294
pkgs = append(pkgs, p)
295
} else if k == "memory" {
296
i, err := strconv.Atoi(f)
297
if err == nil && p.Memory == i {
298
pkgs = append(pkgs, p)
300
} else if k == "disk" {
301
i, err := strconv.Atoi(f)
302
if err == nil && p.Disk == i {
303
pkgs = append(pkgs, p)
305
} else if k == "swap" {
306
i, err := strconv.Atoi(f)
307
if err == nil && p.Swap == i {
308
pkgs = append(pkgs, p)
310
} else if k == "version" && p.Version == f {
311
pkgs = append(pkgs, p)
312
} else if k == "vcpus" {
313
i, err := strconv.Atoi(f)
314
if err == nil && p.VCPUs == i {
315
pkgs = append(pkgs, p)
317
} else if k == "group" && p.Group == f {
318
pkgs = append(pkgs, p)
321
availablePackages = pkgs
326
return availablePackages, nil
329
func (c *CloudAPI) GetPackage(packageName string) (*cloudapi.Package, error) {
330
if err := c.ProcessFunctionHook(c, packageName); err != nil {
334
for _, pkg := range c.packages {
335
if pkg.Name == packageName {
338
if pkg.Id == packageName {
343
return nil, fmt.Errorf("Package %s not found", packageName)
347
func (c *CloudAPI) ListImages(filters map[string]string) ([]cloudapi.Image, error) {
348
if err := c.ProcessFunctionHook(c, filters); err != nil {
352
availableImages := c.images
355
for k, f := range filters {
356
// check if valid filter
357
if contains(imagesFilters, k) {
358
imgs := []cloudapi.Image{}
359
// filter from availableImages and add to imgs
360
for _, i := range availableImages {
361
if k == "name" && i.Name == f {
362
imgs = append(imgs, i)
363
} else if k == "os" && i.OS == f {
364
imgs = append(imgs, i)
365
} else if k == "version" && i.Version == f {
366
imgs = append(imgs, i)
367
} else if k == "public" && i.Public == f {
368
imgs = append(imgs, i)
369
} else if k == "state" && i.State == f {
370
imgs = append(imgs, i)
371
} else if k == "owner" && i.Owner == f {
372
imgs = append(imgs, i)
373
} else if k == "type" && i.Type == f {
374
imgs = append(imgs, i)
377
availableImages = imgs
382
return availableImages, nil
385
func (c *CloudAPI) GetImage(imageId string) (*cloudapi.Image, error) {
386
if err := c.ProcessFunctionHook(c, imageId); err != nil {
390
for _, image := range c.images {
391
if image.Id == imageId {
396
return nil, fmt.Errorf("Image %s not found", imageId)
400
func (c *CloudAPI) ListMachines(filters map[string]string) ([]*cloudapi.Machine, error) {
401
if err := c.ProcessFunctionHook(c, filters); err != nil {
405
availableMachines := c.machines
408
for k, f := range filters {
409
// check if valid filter
410
if contains(machinesFilters, k) {
411
machines := []*cloudapi.Machine{}
412
// filter from availableMachines and add to machines
413
for _, m := range availableMachines {
414
if k == "name" && m.Name == f {
415
machines = append(machines, m)
416
} else if k == "type" && m.Type == f {
417
machines = append(machines, m)
418
} else if k == "state" && m.State == f {
419
machines = append(machines, m)
420
} else if k == "image" && m.Image == f {
421
machines = append(machines, m)
422
} else if k == "memory" {
423
i, err := strconv.Atoi(f)
424
if err == nil && m.Memory == i {
425
machines = append(machines, m)
427
} else if strings.HasPrefix(k, "tags.") {
428
for t, v := range m.Tags {
429
if t == k[strings.Index(k, ".")+1:] && v == f {
430
machines = append(machines, m)
435
availableMachines = machines
440
return availableMachines, nil
443
func (c *CloudAPI) CountMachines() (int, error) {
444
if err := c.ProcessFunctionHook(c); err != nil {
448
return len(c.machines), nil
451
func (c *CloudAPI) GetMachine(machineId string) (*cloudapi.Machine, error) {
452
if err := c.ProcessFunctionHook(c, machineId); err != nil {
456
for _, machine := range c.machines {
457
if machine.Id == machineId {
462
return nil, fmt.Errorf("Machine %s not found", machineId)
465
func (c *CloudAPI) CreateMachine(name, pkg, image string, metadata, tags map[string]string) (*cloudapi.Machine, error) {
466
if err := c.ProcessFunctionHook(c, name, pkg, image); err != nil {
470
machineId, err := localservices.NewUUID()
475
mPkg, err := c.GetPackage(pkg)
480
mImg, err := c.GetImage(image)
485
publicIP := generatePublicIPAddress()
487
newMachine := &cloudapi.Machine{
494
IPs: []string{publicIP, generatePrivateIPAddress()},
495
Created: time.Now().Format("2013-11-26T19:47:13.448Z"),
502
c.machines = append(c.machines, newMachine)
504
return newMachine, nil
507
func (c *CloudAPI) StopMachine(machineId string) error {
508
if err := c.ProcessFunctionHook(c, machineId); err != nil {
512
for _, machine := range c.machines {
513
if machine.Id == machineId {
514
machine.State = "stopped"
515
machine.Updated = time.Now().Format("2013-11-26T19:47:13.448Z")
520
return fmt.Errorf("Machine %s not found", machineId)
523
func (c *CloudAPI) StartMachine(machineId string) error {
524
if err := c.ProcessFunctionHook(c, machineId); err != nil {
528
for _, machine := range c.machines {
529
if machine.Id == machineId {
530
machine.State = "running"
531
machine.Updated = time.Now().Format("2013-11-26T19:47:13.448Z")
536
return fmt.Errorf("Machine %s not found", machineId)
539
func (c *CloudAPI) RebootMachine(machineId string) error {
540
if err := c.ProcessFunctionHook(c, machineId); err != nil {
544
for _, machine := range c.machines {
545
if machine.Id == machineId {
546
machine.State = "running"
547
machine.Updated = time.Now().Format("2013-11-26T19:47:13.448Z")
552
return fmt.Errorf("Machine %s not found", machineId)
555
func (c *CloudAPI) ResizeMachine(machineId, packageName string) error {
556
if err := c.ProcessFunctionHook(c, machineId, packageName); err != nil {
560
mPkg, err := c.GetPackage(packageName)
565
for _, machine := range c.machines {
566
if machine.Id == machineId {
567
machine.Package = packageName
568
machine.Memory = mPkg.Memory
569
machine.Disk = mPkg.Disk
570
machine.Updated = time.Now().Format("2013-11-26T19:47:13.448Z")
575
return fmt.Errorf("Machine %s not found", machineId)
578
func (c *CloudAPI) RenameMachine(machineId, newName string) error {
579
if err := c.ProcessFunctionHook(c, machineId, newName); err != nil {
583
for _, machine := range c.machines {
584
if machine.Id == machineId {
585
machine.Name = newName
586
machine.Updated = time.Now().Format("2013-11-26T19:47:13.448Z")
591
return fmt.Errorf("Machine %s not found", machineId)
594
func (c *CloudAPI) ListMachineFirewallRules(machineId string) ([]*cloudapi.FirewallRule, error) {
595
if err := c.ProcessFunctionHook(c, machineId); err != nil {
599
fwRules := []*cloudapi.FirewallRule{}
600
for _, r := range c.firewallRules {
601
vm := "vm " + machineId
602
if strings.Contains(r.Rule, vm) {
603
fwRules = append(fwRules, r)
610
func (c *CloudAPI) EnableFirewallMachine(machineId string) error {
611
if err := c.ProcessFunctionHook(c, machineId); err != nil {
615
c.machineFw[machineId] = true
620
func (c *CloudAPI) DisableFirewallMachine(machineId string) error {
621
if err := c.ProcessFunctionHook(c, machineId); err != nil {
625
c.machineFw[machineId] = false
630
func (c *CloudAPI) DeleteMachine(machineId string) error {
631
if err := c.ProcessFunctionHook(c, machineId); err != nil {
635
for i, machine := range c.machines {
636
if machine.Id == machineId {
637
if machine.State == "stopped" {
638
c.machines = append(c.machines[:i], c.machines[i+1:]...)
641
return fmt.Errorf("Cannot Delete machine %s, machine is not stopped.", machineId)
646
return fmt.Errorf("Machine %s not found", machineId)
650
func (c *CloudAPI) ListFirewallRules() ([]*cloudapi.FirewallRule, error) {
651
if err := c.ProcessFunctionHook(c); err != nil {
655
return c.firewallRules, nil
658
func (c *CloudAPI) GetFirewallRule(fwRuleId string) (*cloudapi.FirewallRule, error) {
659
if err := c.ProcessFunctionHook(c, fwRuleId); err != nil {
663
for _, r := range c.firewallRules {
664
if strings.EqualFold(r.Id, fwRuleId) {
669
return nil, fmt.Errorf("Firewall rule %s not found", fwRuleId)
672
func (c *CloudAPI) CreateFirewallRule(rule string, enabled bool) (*cloudapi.FirewallRule, error) {
673
if err := c.ProcessFunctionHook(c, rule, enabled); err != nil {
677
fwRuleId, err := localservices.NewUUID()
679
return nil, fmt.Errorf("Error creating firewall rule: %q", err)
682
fwRule := &cloudapi.FirewallRule{Id: fwRuleId, Rule: rule, Enabled: enabled}
683
c.firewallRules = append(c.firewallRules, fwRule)
688
func (c *CloudAPI) UpdateFirewallRule(fwRuleId, rule string, enabled bool) (*cloudapi.FirewallRule, error) {
689
if err := c.ProcessFunctionHook(c, fwRuleId, rule, enabled); err != nil {
693
for _, r := range c.firewallRules {
694
if strings.EqualFold(r.Id, fwRuleId) {
701
return nil, fmt.Errorf("Firewall rule %s not found", fwRuleId)
704
func (c *CloudAPI) EnableFirewallRule(fwRuleId string) (*cloudapi.FirewallRule, error) {
705
if err := c.ProcessFunctionHook(c, fwRuleId); err != nil {
709
for _, r := range c.firewallRules {
710
if strings.EqualFold(r.Id, fwRuleId) {
716
return nil, fmt.Errorf("Firewall rule %s not found", fwRuleId)
719
func (c *CloudAPI) DisableFirewallRule(fwRuleId string) (*cloudapi.FirewallRule, error) {
720
if err := c.ProcessFunctionHook(c, fwRuleId); err != nil {
724
for _, r := range c.firewallRules {
725
if strings.EqualFold(r.Id, fwRuleId) {
731
return nil, fmt.Errorf("Firewall rule %s not found", fwRuleId)
734
func (c *CloudAPI) DeleteFirewallRule(fwRuleId string) error {
735
if err := c.ProcessFunctionHook(c, fwRuleId); err != nil {
739
for i, r := range c.firewallRules {
740
if strings.EqualFold(r.Id, fwRuleId) {
741
c.firewallRules = append(c.firewallRules[:i], c.firewallRules[i+1:]...)
746
return fmt.Errorf("Firewall rule %s not found", fwRuleId)
749
func (c *CloudAPI) ListFirewallRuleMachines(fwRuleId string) ([]*cloudapi.Machine, error) {
750
if err := c.ProcessFunctionHook(c, fwRuleId); err != nil {
754
return c.machines, nil
758
func (c *CloudAPI) ListNetworks() ([]cloudapi.Network, error) {
759
if err := c.ProcessFunctionHook(c); err != nil {
763
return c.networks, nil
766
func (c *CloudAPI) GetNetwork(networkId string) (*cloudapi.Network, error) {
767
if err := c.ProcessFunctionHook(c, networkId); err != nil {
771
for _, n := range c.networks {
772
if strings.EqualFold(n.Id, networkId) {
777
return nil, fmt.Errorf("Network %s not found", networkId)