diff --git a/Gopkg.lock b/Gopkg.lock index 0af3b83284..ce49a77b51 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -129,8 +129,8 @@ [[projects]] name = "github.com/exoscale/egoscale" packages = ["."] - revision = "ef19347c0fa98812afe9240b049c48cbbf2e5fa9" - version = "v0.9.6" + revision = "401e52c49deedb261ddf0e2432a645abbb66bb4e" + version = "v0.9.10" [[projects]] name = "github.com/go-ini/ini" @@ -336,6 +336,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "15f268444f73c8446a63dc2c8e790c943c82acfd05327630cb0d86095f8e7232" + inputs-digest = "e99aac76aae722f534984877f01c84fa023d10b3cf07e4acba9f33b792fd23a3" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index a3f5dfeb8c..d6310fdea3 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -43,7 +43,7 @@ [[constraint]] name = "github.com/exoscale/egoscale" - version = "0.9.6" + version = "0.9.10" [[constraint]] branch = "master" diff --git a/drivers/exoscale/exoscale.go b/drivers/exoscale/exoscale.go index 9d217f7f31..d5dde85963 100644 --- a/drivers/exoscale/exoscale.go +++ b/drivers/exoscale/exoscale.go @@ -1,18 +1,21 @@ package exoscale import ( + "bytes" "encoding/base64" "errors" "fmt" "io/ioutil" "net" "os" - "regexp" + "os/user" + "path/filepath" "strings" "github.com/docker/machine/libmachine/drivers" "github.com/docker/machine/libmachine/log" "github.com/docker/machine/libmachine/mcnflag" + "github.com/docker/machine/libmachine/mcnutils" "github.com/docker/machine/libmachine/state" "github.com/exoscale/egoscale" ) @@ -26,9 +29,10 @@ type Driver struct { InstanceProfile string DiskSize int64 Image string - SecurityGroup string - AffinityGroup string + SecurityGroups []string + AffinityGroups []string AvailabilityZone string + SSHKey string KeyPair string PublicKey string UserDataFile string @@ -43,6 +47,7 @@ const ( defaultImage = "Linux Ubuntu 16.04 LTS 64-bit" defaultAvailabilityZone = "CH-DK-2" defaultSSHUser = "root" + defaultSecurityGroup = "docker-machine" defaultAffinityGroupType = "host anti-affinity" defaultCloudInit = `#cloud-config manage_etc_hosts: localhost @@ -89,7 +94,7 @@ func (d *Driver) GetCreateFlags() []mcnflag.Flag { mcnflag.StringSliceFlag{ EnvVar: "EXOSCALE_SECURITY_GROUP", Name: "exoscale-security-group", - Value: []string{}, + Value: []string{defaultSecurityGroup}, Usage: "exoscale security group", }, mcnflag.StringFlag{ @@ -104,6 +109,12 @@ func (d *Driver) GetCreateFlags() []mcnflag.Flag { Value: "", Usage: "name of the ssh user", }, + mcnflag.StringFlag{ + EnvVar: "EXOSCALE_SSH_KEY", + Name: "exoscale-ssh-key", + Value: "", + Usage: "path to the SSH user private key", + }, mcnflag.StringFlag{ EnvVar: "EXOSCALE_USERDATA", Name: "exoscale-userdata", @@ -145,19 +156,23 @@ func (d *Driver) GetSSHHostname() (string, error) { func (d *Driver) GetSSHUsername() string { if d.SSHUser == "" { name := strings.ToLower(d.Image) - re := regexp.MustCompile(`\b[0-9.]+\b`) - version := re.FindString(d.Image) if strings.Contains(name, "ubuntu") { return "ubuntu" } - if strings.Contains(name, "centos") && version >= "7.3" { + if strings.Contains(name, "centos") { return "centos" } + if strings.Contains(name, "redhat") { + return "cloud-user" + } + if strings.Contains(name, "fedora") { + return "fedora" + } if strings.Contains(name, "coreos") { return "core" } - if strings.Contains(name, "debian") && version >= "8" { + if strings.Contains(name, "debian") { return "debian" } return defaultSSHUser @@ -180,17 +195,11 @@ func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error { d.InstanceProfile = flags.String("exoscale-instance-profile") d.DiskSize = int64(flags.Int("exoscale-disk-size")) d.Image = flags.String("exoscale-image") - securityGroups := flags.StringSlice("exoscale-security-group") - if len(securityGroups) == 0 { - securityGroups = []string{"docker-machine"} - } - d.SecurityGroup = strings.Join(securityGroups, ",") - affinityGroups := flags.StringSlice("exoscale-affinity-group") - if len(affinityGroups) > 0 { - d.AffinityGroup = strings.Join(affinityGroups, ",") - } + d.SecurityGroups = flags.StringSlice("exoscale-security-group") + d.AffinityGroups = flags.StringSlice("exoscale-affinity-group") d.AvailabilityZone = flags.String("exoscale-availability-zone") d.SSHUser = flags.String("exoscale-ssh-user") + d.SSHKey = flags.String("exoscale-ssh-key") d.UserDataFile = flags.String("exoscale-userdata") d.SetSwarmConfigFromFlags(flags) @@ -394,7 +403,6 @@ func (d *Driver) Create() error { if err != nil { return err } - userData := base64.StdEncoding.EncodeToString(cloudInit) log.Infof("Querying exoscale for the requested parameters...") client := egoscale.NewClient(d.URL, d.APIKey, d.APISecretKey) @@ -414,8 +422,16 @@ func (d *Driver) Create() error { // Image UUID var tpl string images, ok := topology.Images[strings.ToLower(d.Image)] + if ok { - tpl, ok = images[d.DiskSize] + smallestDiskSize := d.DiskSize + for s := range images { + if s < smallestDiskSize { + smallestDiskSize = s + } + } + + tpl, ok = images[smallestDiskSize] } if !ok { return fmt.Errorf("Unable to find image %v with size %d", @@ -432,9 +448,12 @@ func (d *Driver) Create() error { log.Debugf("Profile %v = %s", d.InstanceProfile, profile) // Security groups - securityGroups := strings.Split(d.SecurityGroup, ",") - sgs := make([]string, len(securityGroups)) - for idx, group := range securityGroups { + sgs := make([]string, 0, len(d.SecurityGroups)) + for _, group := range d.SecurityGroups { + if group == "" { + continue + } + sg, ok := topology.SecurityGroups[group] if !ok { log.Infof("Security group %v does not exist, create it", group) @@ -445,13 +464,15 @@ func (d *Driver) Create() error { sg = securityGroup.ID } log.Debugf("Security group %v = %s", group, sg) - sgs[idx] = sg + sgs = append(sgs, sg) } // Affinity Groups - affinityGroups := strings.Split(d.AffinityGroup, ",") - ags := make([]string, len(affinityGroups)) - for idx, group := range affinityGroups { + ags := make([]string, 0, len(d.AffinityGroups)) + for _, group := range d.AffinityGroups { + if group == "" { + continue + } ag, ok := topology.AffinityGroups[group] if !ok { log.Infof("Affinity Group %v does not exist, create it", group) @@ -462,32 +483,73 @@ func (d *Driver) Create() error { ag = affinityGroup.ID } log.Debugf("Affinity group %v = %s", group, ag) - ags[idx] = ag + ags = append(ags, ag) } - log.Infof("Generate an SSH keypair...") - keypairName := fmt.Sprintf("docker-machine-%s", d.MachineName) - kpresp, err := client.CreateKeypair(keypairName) - if err != nil { - return err - } - err = ioutil.WriteFile(d.GetSSHKeyPath(), []byte(kpresp.PrivateKey), 0600) - if err != nil { - return err + // SSH key pair + if d.SSHKey == "" { + var keyPairName string + keyPairName = fmt.Sprintf("docker-machine-%s", d.MachineName) + log.Infof("Generate an SSH keypair...") + resp, err := client.Request(&egoscale.CreateSSHKeyPair{ + Name: keyPairName, + }) + if err != nil { + return fmt.Errorf("SSH Key pair creation failed %s", err) + } + keyPair := resp.(*egoscale.CreateSSHKeyPairResponse).KeyPair + if err = ioutil.WriteFile(d.GetSSHKeyPath(), []byte(keyPair.PrivateKey), 0600); err != nil { + return fmt.Errorf("SSH public key could not be written %s", err) + } + d.KeyPair = keyPairName + } else { + log.Infof("Importing SSH key from %s", d.SSHKey) + + sshKey := d.SSHKey + if strings.HasPrefix(sshKey, "~/") { + usr, _ := user.Current() + sshKey = filepath.Join(usr.HomeDir, sshKey[2:]) + } else { + var err error + if sshKey, err = filepath.Abs(sshKey); err != nil { + return err + } + } + + // Sending the SSH public key through the cloud-init config + pubKey, err := ioutil.ReadFile(sshKey + ".pub") + if err != nil { + return fmt.Errorf("Cannot read SSH public key %s", err) + } + + sshAuthorizedKeys := ` +ssh_authorized_keys: +- ` + cloudInit = bytes.Join([][]byte{cloudInit, []byte(sshAuthorizedKeys), pubKey}, []byte("")) + + // Copying the private key into docker-machine + if err := mcnutils.CopyFile(sshKey, d.GetSSHKeyPath()); err != nil { + return fmt.Errorf("Unable to copy SSH file: %s", err) + } + if err := os.Chmod(d.GetSSHKeyPath(), 0600); err != nil { + return fmt.Errorf("Unable to set permissions on the SSH file: %s", err) + } } - d.KeyPair = keypairName log.Infof("Spawn exoscale host...") log.Debugf("Using the following cloud-init file:") log.Debugf("%s", string(cloudInit)) + // Base64 encode the userdata + userData := base64.StdEncoding.EncodeToString(cloudInit) + req := &egoscale.DeployVirtualMachine{ TemplateID: tpl, ServiceOfferingID: profile, UserData: userData, ZoneID: zone, - KeyPair: d.KeyPair, Name: d.MachineName, + KeyPair: d.KeyPair, DisplayName: d.MachineName, RootDiskSize: d.DiskSize, SecurityGroupIDs: sgs, @@ -506,6 +568,19 @@ func (d *Driver) Create() error { d.IPAddress = IPAddress.String() } d.ID = vm.ID + log.Infof("IP Address: %v, SSH User: %v", d.IPAddress, d.GetSSHUsername()) + + // Destroy the SSH key from CloudStack + if d.KeyPair != "" { + if err := drivers.WaitForSSH(d); err != nil { + return err + } + + if err := client.BooleanRequest(&egoscale.DeleteSSHKeyPair{Name: d.KeyPair}); err != nil { + return err + } + d.KeyPair = "" + } return nil } @@ -547,19 +622,25 @@ func (d *Driver) Kill() error { // Remove destroys the VM instance and the associated SSH key. func (d *Driver) Remove() error { - cs := d.client() + client := d.client() - // Destroy the SSH key - if err := cs.BooleanRequest(&egoscale.DeleteSSHKeyPair{Name: d.KeyPair}); err != nil { - return err + // Destroy the SSH key from CloudStack + if d.KeyPair != "" { + if err := client.BooleanRequest(&egoscale.DeleteSSHKeyPair{Name: d.KeyPair}); err != nil { + return err + } } // Destroy the virtual machine - _, err := cs.AsyncRequest(&egoscale.DestroyVirtualMachine{ID: d.ID}, d.async) + if d.ID != "" { + if _, err := client.AsyncRequest(&egoscale.DestroyVirtualMachine{ID: d.ID}, d.async); err != nil { + return err + } + } log.Infof("The Anti-Affinity group and Security group were not removed") - return err + return nil } // Build a cloud-init user data string that will install and run diff --git a/vendor/github.com/exoscale/egoscale/.travis.yml b/vendor/github.com/exoscale/egoscale/.travis.yml index d399c404f5..1ef39023e1 100644 --- a/vendor/github.com/exoscale/egoscale/.travis.yml +++ b/vendor/github.com/exoscale/egoscale/.travis.yml @@ -5,6 +5,7 @@ dist: trusty go: - 1.8 - 1.9 +- "1.10" - tip cache: apt diff --git a/vendor/github.com/exoscale/egoscale/AUTHORS b/vendor/github.com/exoscale/egoscale/AUTHORS index 72ada50c98..a8d7a14850 100644 --- a/vendor/github.com/exoscale/egoscale/AUTHORS +++ b/vendor/github.com/exoscale/egoscale/AUTHORS @@ -4,3 +4,4 @@ Chris Baumbauer Marc-Aurèle Brothier Sebastien Goasguen Yoan Blanc +Stefano Marengo diff --git a/vendor/github.com/exoscale/egoscale/CHANGELOG.md b/vendor/github.com/exoscale/egoscale/CHANGELOG.md index 0820f98744..5ace7c8f12 100644 --- a/vendor/github.com/exoscale/egoscale/CHANGELOG.md +++ b/vendor/github.com/exoscale/egoscale/CHANGELOG.md @@ -1,3 +1,56 @@ +Changelog +========= + +0.9.10 +------ + +- fix: typo made ListAll required in ListPublicIPAddresses +- fix: all bool are now *bool, respecting CS default value +- feat: (*VM).DefaultNic() to obtain the main Nic + +0.9.9 +----- + +- fix: affinity groups virtualmachineIds attribute +- fix: uuidList is not a list of strings + +0.9.8 +----- + +- feat: add RootDiskSize to RestoreVirtualMachine +- fix: monotonic polling using Context + +0.9.7 +----- + +- feat: add Taggable interface to expose ResourceType +- feat: add (Create|Update|Delete|List)InstanceGroup(s) +- feat: add RegisterUserKeys +- feat: add ListResourceLimits +- feat: add ListAccounts + +0.9.6 +----- + +- fix: update UpdateVirtualMachine userdata +- fix. Network's name/displaytext might be empty + +0.9.5 +----- + +- fix: serialization of slice + +0.9.4 +----- + +- fix: constants + +0.9.3 +----- + +- change: userdata expects a string +- change: no pointer in sub-struct's + 0.9.2 ----- diff --git a/vendor/github.com/exoscale/egoscale/README.md b/vendor/github.com/exoscale/egoscale/README.md index 0b08521cb6..1be67a0934 100644 --- a/vendor/github.com/exoscale/egoscale/README.md +++ b/vendor/github.com/exoscale/egoscale/README.md @@ -1,12 +1,15 @@ egoscale: exoscale driver for golang ==================================== + + [![Build Status](https://travis-ci.org/exoscale/egoscale.svg?branch=master)](https://travis-ci.org/exoscale/egoscale) [![Maintainability](https://api.codeclimate.com/v1/badges/fcab3b624b7d3ca96a9d/maintainability)](https://codeclimate.com/github/exoscale/egoscale/maintainability) +[![Test Coverage](https://api.codeclimate.com/v1/badges/fcab3b624b7d3ca96a9d/test_coverage)](https://codeclimate.com/github/exoscale/egoscale/test_coverage) +[![GoDoc](https://godoc.org/github.com/exoscale/egoscale?status.svg)](https://godoc.org/github.com/exoscale/egoscale) +[![Go Report Card](https://goreportcard.com/badge/github.com/exoscale/egoscale)](https://goreportcard.com/report/github.com/exoscale/egoscale) -An API wrapper for the exoscale public cloud - -[godoc](https://godoc.org/github.com/exoscale/egoscale) +An API wrapper for the CloudStack based [Exoscale public cloud](https://www.exoscale.com). ### License diff --git a/vendor/github.com/exoscale/egoscale/accounts.go b/vendor/github.com/exoscale/egoscale/accounts.go new file mode 100644 index 0000000000..aeb8a214b5 --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/accounts.go @@ -0,0 +1,98 @@ +package egoscale + +const ( + // UserAccount represents a User + UserAccount = iota + // AdminAccount represents an Admin + AdminAccount + // DomainAdminAccount represents a Domain Admin + DomainAdminAccount +) + +// AccountType represents the type of an Account +// +// http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/4.8/accounts.html#accounts-users-and-domains +type AccountType int64 + +// Account provides the detailed account information +type Account struct { + ID string `json:"id"` + AccountType AccountType `json:"accounttype,omitempty"` + AccountDetails string `json:"accountdetails,omitempty"` + CPUAvailable string `json:"cpuavailable,omitempty"` + CPULimit string `json:"cpulimit,omitempty"` + CPUTotal int64 `json:"cputotal,omitempty"` + DefaultZoneID string `json:"defaultzoneid,omitempty"` + Domain string `json:"domain,omitempty"` + DomainID string `json:"domainid,omitempty"` + EIPLimit string `json:"eiplimit,omitempty"` + Groups []string `json:"groups,omitempty"` + IPAvailable string `json:"ipavailable,omitempty"` + IPLimit string `json:"iplimit,omitempty"` + IPTotal int64 `json:"iptotal,omitempty"` + IsDefault bool `json:"isdefault,omitempty"` + MemoryAvailable string `json:"memoryavailable,omitempty"` + MemoryLimit string `json:"memorylimit,omitempty"` + MemoryTotal int64 `json:"memorytotal,omitempty"` + Name string `json:"name,omitempty"` + NetworkAvailable string `json:"networkavailable,omitempty"` + NetworkDomain string `json:"networkdomain,omitempty"` + NetworkLimit string `json:"networklimit,omitempty"` + NetworkTotal int16 `json:"networktotal,omitempty"` + PrimaryStorageAvailable string `json:"primarystorageavailable,omitempty"` + PrimaryStorageLimit string `json:"primarystoragelimit,omitempty"` + PrimaryStorageTotal int64 `json:"primarystoragetotal,omitempty"` + ProjectAvailable string `json:"projectavailable,omitempty"` + ProjectLimit string `json:"projectlimit,omitempty"` + ProjectTotal int64 `json:"projecttotal,omitempty"` + SecondaryStorageAvailable string `json:"secondarystorageavailable,omitempty"` + SecondaryStorageLimit string `json:"secondarystoragelimit,omitempty"` + SecondaryStorageTotal int64 `json:"secondarystoragetotal,omitempty"` + SnapshotAvailable string `json:"snapshotavailable,omitempty"` + SnapshotLimit string `json:"snapshotlimit,omitempty"` + SnapshotTotal int64 `json:"snapshottotal,omitempty"` + State string `json:"state,omitempty"` + TemplateAvailable string `json:"templateavailable,omitempty"` + TemplateLimit string `json:"templatelimit,omitempty"` + TemplateTotal int64 `json:"templatetotal,omitempty"` + VMAvailable string `json:"vmavailable,omitempty"` + VMLimit string `json:"vmlimit,omitempty"` + VMTotal int64 `json:"vmtotal,omitempty"` + VolumeAvailable string `json:"volumeavailable,omitempty"` + VolumeLimit string `json:"volumelimit,omitempty"` + VolumeTotal int64 `json:"volumetotal,omitempty"` + VPCAvailable string `json:"vpcavailable,omitempty"` + VPCLimit string `json:"vpclimit,omitempty"` + VPCTotal int64 `json:"vpctotal,omitempty"` + User []User `json:"user,omitempty"` +} + +// ListAccounts represents a query to display the accounts +// +// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/listAccounts.html +type ListAccounts struct { + AccountType int64 `json:"accounttype,omitempty"` + DomainID string `json:"domainid,omitempty"` + ID string `json:"id,omitempty"` + IsCleanUpRequired *bool `json:"iscleanuprequired,omitempty"` + IsRecursive *bool `json:"isrecursive,omitempty"` + Keyword string `json:"keyword,omitempty"` + ListAll *bool `json:"listall,omitempty"` + Page int `json:"page,omitempty"` + PageSize int `json:"pagesize,omitempty"` + State string `json:"state,omitempty"` +} + +func (*ListAccounts) name() string { + return "listAccounts" +} + +func (*ListAccounts) response() interface{} { + return new(ListAccountsResponse) +} + +// ListAccountsResponse represents a list of accounts +type ListAccountsResponse struct { + Count int `json:"count"` + Account []Account `json:"account"` +} diff --git a/vendor/github.com/exoscale/egoscale/addresses.go b/vendor/github.com/exoscale/egoscale/addresses.go index 6febbef740..9c012dffd0 100644 --- a/vendor/github.com/exoscale/egoscale/addresses.go +++ b/vendor/github.com/exoscale/egoscale/addresses.go @@ -38,14 +38,19 @@ type IPAddress struct { JobStatus JobStatusType `json:"jobstatus,omitempty"` } +// ResourceType returns the type of the resource +func (*IPAddress) ResourceType() string { + return "PublicIpAddress" +} + // AssociateIPAddress (Async) represents the IP creation // // CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/associateIpAddress.html type AssociateIPAddress struct { Account string `json:"account,omitempty"` DomainID string `json:"domainid,omitempty"` - ForDisplay bool `json:"fordisplay,omitempty"` - IsPortable bool `json:"isportable,omitempty"` + ForDisplay *bool `json:"fordisplay,omitempty"` + IsPortable *bool `json:"isportable,omitempty"` NetworkdID string `json:"networkid,omitempty"` ProjectID string `json:"projectid,omitempty"` RegionID string `json:"regionid,omitempty"` @@ -86,7 +91,7 @@ func (*DisassociateIPAddress) asyncResponse() interface{} { type UpdateIPAddress struct { ID string `json:"id"` CustomID string `json:"customid,omitempty"` // root only - ForDisplay bool `json:"fordisplay,omitempty"` + ForDisplay *bool `json:"fordisplay,omitempty"` } func (*UpdateIPAddress) name() string { @@ -104,20 +109,20 @@ type UpdateIPAddressResponse AssociateIPAddressResponse // CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/listPublicIpAddresses.html type ListPublicIPAddresses struct { Account string `json:"account,omitempty"` - AllocatedOnly bool `json:"allocatedonly,omitempty"` + AllocatedOnly *bool `json:"allocatedonly,omitempty"` AllocatedNetworkID string `json:"allocatednetworkid,omitempty"` DomainID string `json:"domainid,omitempty"` - ForDisplay bool `json:"fordisplay,omitempty"` - ForLoadBalancing bool `json:"forloadbalancing,omitempty"` + ForDisplay *bool `json:"fordisplay,omitempty"` + ForLoadBalancing *bool `json:"forloadbalancing,omitempty"` ForVirtualNetwork string `json:"forvirtualnetwork,omitempty"` ID string `json:"id,omitempty"` IPAddress net.IP `json:"ipaddress,omitempty"` - IsElastic bool `json:"iselastic,omitempty"` - IsRecursive bool `json:"isrecursive,omitempty"` - IsSourceNat bool `json:"issourcenat,omitempty"` - IsStaticNat bool `json:"isstaticnat,omitempty"` + IsElastic *bool `json:"iselastic,omitempty"` + IsRecursive *bool `json:"isrecursive,omitempty"` + IsSourceNat *bool `json:"issourcenat,omitempty"` + IsStaticNat *bool `json:"isstaticnat,omitempty"` Keyword string `json:"keyword,omitempty"` - ListAll bool `json:"listall,omiempty"` + ListAll *bool `json:"listall,omitempty"` Page int `json:"page,omitempty"` PageSize int `json:"pagesize,omitempty"` PhysicalNetworkID string `json:"physicalnetworkid,omitempty"` diff --git a/vendor/github.com/exoscale/egoscale/affinity_groups.go b/vendor/github.com/exoscale/egoscale/affinity_groups.go index eed628ce98..1f5bf80e79 100644 --- a/vendor/github.com/exoscale/egoscale/affinity_groups.go +++ b/vendor/github.com/exoscale/egoscale/affinity_groups.go @@ -13,7 +13,7 @@ type AffinityGroup struct { DomainID string `json:"domainid,omitempty"` Name string `json:"name,omitempty"` Type string `json:"type,omitempty"` - VirtualMachineIDs []string `json:"virtualmachineIDs,omitempty"` // *I*ds is not a typo + VirtualMachineIDs []string `json:"virtualmachineIds,omitempty"` // *I*ds is not a typo } // AffinityGroupType represent an affinity group type @@ -100,9 +100,9 @@ type ListAffinityGroups struct { Account string `json:"account,omitempty"` DomainID string `json:"domainid,omitempty"` ID string `json:"id,omitempty"` - IsRecursive bool `json:"isrecursive,omitempty"` + IsRecursive *bool `json:"isrecursive,omitempty"` Keyword string `json:"keyword,omitempty"` - ListAll bool `json:"listall,omitempty"` + ListAll *bool `json:"listall,omitempty"` Name string `json:"name,omitempty"` Page int `json:"page,omitempty"` PageSize int `json:"pagesize,omitempty"` diff --git a/vendor/github.com/exoscale/egoscale/async_jobs.go b/vendor/github.com/exoscale/egoscale/async_jobs.go index 906fd2f381..35625de06c 100644 --- a/vendor/github.com/exoscale/egoscale/async_jobs.go +++ b/vendor/github.com/exoscale/egoscale/async_jobs.go @@ -44,7 +44,7 @@ type QueryAsyncJobResultResponse AsyncJobResult type ListAsyncJobs struct { Account string `json:"account,omitempty"` DomainID string `json:"domainid,omitempty"` - IsRecursive bool `json:"isrecursive,omitempty"` + IsRecursive *bool `json:"isrecursive,omitempty"` Keyword string `json:"keyword,omitempty"` Page int `json:"page,omitempty"` PageSize int `json:"pagesize,omitempty"` diff --git a/vendor/github.com/exoscale/egoscale/doc.go b/vendor/github.com/exoscale/egoscale/doc.go index e53861ba05..bd8e1396f2 100644 --- a/vendor/github.com/exoscale/egoscale/doc.go +++ b/vendor/github.com/exoscale/egoscale/doc.go @@ -1,6 +1,6 @@ /* -Package egoscale is a mapping for with the CloudStack API (http://cloudstack.apache.org/api.html) from Go. It has been designed against the Exoscale (https://www.exoscale.ch/) infrastructure but should fit other CloudStack services. +Package egoscale is a mapping for with the CloudStack API (http://cloudstack.apache.org/api.html) from Go. It has been designed against the Exoscale (https://www.exoscale.com/) infrastructure but should fit other CloudStack services. Requests and Responses diff --git a/vendor/github.com/exoscale/egoscale/events.go b/vendor/github.com/exoscale/egoscale/events.go index 3eaaf48dad..018c00b402 100644 --- a/vendor/github.com/exoscale/egoscale/events.go +++ b/vendor/github.com/exoscale/egoscale/events.go @@ -32,10 +32,10 @@ type ListEvents struct { EndDate string `json:"enddate,omitempty"` EntryTime int `json:"entrytime,omitempty"` ID string `json:"id,omitempty"` - IsRecursive bool `json:"isrecursive,omitempty"` + IsRecursive *bool `json:"isrecursive,omitempty"` Keyword string `json:"keyword,omitempty"` Level string `json:"level,omitempty"` // INFO, WARN, ERROR - ListAll bool `json:"listall,omitempty"` + ListAll *bool `json:"listall,omitempty"` Page int `json:"page,omitempty"` PageSize int `json:"pagesize,omitempty"` ProjectID string `json:"projectid,omitempty"` diff --git a/vendor/github.com/exoscale/egoscale/keypairs.go b/vendor/github.com/exoscale/egoscale/keypairs.go index 63f3f9b611..fc8cea1c41 100644 --- a/vendor/github.com/exoscale/egoscale/keypairs.go +++ b/vendor/github.com/exoscale/egoscale/keypairs.go @@ -82,9 +82,9 @@ type ListSSHKeyPairs struct { Account string `json:"account,omitempty"` DomainID string `json:"domainid,omitempty"` Fingerprint string `json:"fingerprint,omitempty"` - IsRecursive bool `json:"isrecursive,omitempty"` + IsRecursive *bool `json:"isrecursive,omitempty"` Keyword string `json:"keyword,omitempty"` - ListAll bool `json:"listall,omitempty"` + ListAll *bool `json:"listall,omitempty"` Name string `json:"name,omitempty"` Page int `json:"page,omitempty"` PageSize int `json:"pagesize,omitempty"` diff --git a/vendor/github.com/exoscale/egoscale/limits.go b/vendor/github.com/exoscale/egoscale/limits.go new file mode 100644 index 0000000000..f2e1f67911 --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/limits.go @@ -0,0 +1,106 @@ +package egoscale + +// https://github.com/apache/cloudstack/blob/master/api/src/main/java/com/cloud/configuration/Resource.java + +// ResourceTypeName represents the name of a resource type (for limits) +type ResourceTypeName string + +const ( + // VirtualMachineTypeName is the resource type name of a VM + VirtualMachineTypeName ResourceTypeName = "user_vm" + // IPAddressTypeName is the resource type name of an IP address + IPAddressTypeName = "public_ip" + // VolumeTypeName is the resource type name of a volume + VolumeTypeName = "volume" + // SnapshotTypeName is the resource type name of a snapshot + SnapshotTypeName = "snapshot" + // TemplateTypeName is the resource type name of a template + TemplateTypeName = "template" + // ProjectTypeName is the resource type name of a project + ProjectTypeName = "project" + // NetworkTypeName is the resource type name of a network + NetworkTypeName = "network" + // VPCTypeName is the resource type name of a VPC + VPCTypeName = "vpc" + // CPUTypeName is the resource type name of a CPU + CPUTypeName = "cpu" + // MemoryTypeName is the resource type name of Memory + MemoryTypeName = "memory" + // PrimaryStorageTypeName is the resource type name of primary storage + PrimaryStorageTypeName = "primary_storage" + // SecondaryStorageTypeName is the resource type name of secondary storage + SecondaryStorageTypeName = "secondary_storage" +) + +// ResourceType represents the ID of a resource type (for limits) +type ResourceType int64 + +const ( + // VirtualMachineType is the resource type ID of a VM + VirtualMachineType ResourceType = iota + // IPAddressType is the resource type ID of an IP address + IPAddressType + // VolumeType is the resource type ID of a volume + VolumeType + // SnapshotType is the resource type ID of a snapshot + SnapshotType + // TemplateType is the resource type ID of a template + TemplateType + // ProjectType is the resource type ID of a project + ProjectType + // NetworkType is the resource type ID of a network + NetworkType + // VPCType is the resource type ID of a VPC + VPCType + // CPUType is the resource type ID of a CPU + CPUType + // MemoryType is the resource type ID of Memory + MemoryType + // PrimaryStorageType is the resource type ID of primary storage + PrimaryStorageType + // SecondaryStorageType is the resource type ID of secondary storage + SecondaryStorageType +) + +// ListResourceLimits lists the resource limits +// +// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/listResourceLimits.html +type ListResourceLimits struct { + Account string `json:"account,omittempty"` + DomainID string `json:"domainid,omitempty"` + ID string `json:"id,omitempty"` + IsRecursive *bool `json:"isrecursive,omitempty"` + Keyword string `json:"keyword,omitempty"` + ListAll *bool `json:"listall,omitempty"` + Page int `json:"page,omitempty"` + PageSize int `json:"pagesize,omitempty"` + ProjectID string `json:"projectid,omitempty"` + ResourceType ResourceType `json:"resourcetype,omitempty"` + ResourceTypeName ResourceTypeName `json:"resourcetypename,omitempty"` +} + +func (*ListResourceLimits) name() string { + return "listResourceLimits" +} + +func (*ListResourceLimits) response() interface{} { + return new(ListResourceLimitsResponse) +} + +// ListResourceLimitsResponse represents a list of resource limits +type ListResourceLimitsResponse struct { + Count int `json:"count"` + ResourceLimit []ResourceLimit `json:"resourcelimit"` +} + +// ResourceLimit represents the limit on a particular resource +type ResourceLimit struct { + Account string `json:"account,omitempty"` + Domain string `json:"domain,omitempty"` + DomainID string `json:"domainid,omitempty"` + Max int64 `json:"max,omitempty"` // -1 means the sky is the limit + Project string `json:"project,omitempty"` + ProjectID string `json:"projectid,omitempty"` + ResourceType ResourceType `json:"resourcetype,omitempty"` + ResourceTypeName ResourceTypeName `json:"resourcetypename,omitempty"` +} diff --git a/vendor/github.com/exoscale/egoscale/networks.go b/vendor/github.com/exoscale/egoscale/networks.go index 7e68731e50..82c243a004 100644 --- a/vendor/github.com/exoscale/egoscale/networks.go +++ b/vendor/github.com/exoscale/egoscale/networks.go @@ -56,6 +56,11 @@ type Network struct { Tags []ResourceTag `json:"tags"` } +// ResourceType returns the type of the resource +func (*Network) ResourceType() string { + return "Network" +} + // Service is a feature of a network type Service struct { Name string `json:"name"` @@ -96,7 +101,7 @@ type CreateNetwork struct { Account string `json:"account,omitempty"` ACLID string `json:"aclid,omitempty"` ACLType string `json:"acltype,omitempty"` // Account or Domain - DisplayNetwork bool `json:"displaynetwork,omitempty"` // root only + DisplayNetwork *bool `json:"displaynetwork,omitempty"` // root only DomainID string `json:"domainid,omitempty"` EndIP net.IP `json:"endip,omitempty"` EndIpv6 net.IP `json:"endipv6,omitempty"` @@ -142,16 +147,16 @@ type CreateNetworkResponse NetworkResponse // CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/updateNetwork.html type UpdateNetwork struct { ID string `json:"id"` - ChangeCidr bool `json:"changecidr,omitempty"` + ChangeCidr *bool `json:"changecidr,omitempty"` CustomID string `json:"customid,omitempty"` // root only DisplayNetwork string `json:"displaynetwork,omitempty"` DisplayText string `json:"displaytext,omitempty"` - Forced bool `json:"forced,omitempty"` + Forced *bool `json:"forced,omitempty"` GuestVMCidr string `json:"guestvmcidr,omitempty"` Name string `json:"name,omitempty"` NetworkDomain string `json:"networkdomain,omitempty"` NetworkOfferingID string `json:"networkofferingid,omitempty"` - UpdateInSequence bool `json:"updateinsequence,omitempty"` + UpdateInSequence *bool `json:"updateinsequence,omitempty"` } func (*UpdateNetwork) name() string { @@ -170,7 +175,7 @@ type UpdateNetworkResponse NetworkResponse // CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/restartNetwork.html type RestartNetwork struct { ID string `json:"id"` - Cleanup bool `json:"cleanup,omitempty"` + Cleanup *bool `json:"cleanup,omitempty"` } func (*RestartNetwork) name() string { @@ -189,7 +194,7 @@ type RestartNetworkResponse NetworkResponse // CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/deleteNetwork.html type DeleteNetwork struct { ID string `json:"id"` - Forced bool `json:"forced,omitempty"` + Forced *bool `json:"forced,omitempty"` } func (*DeleteNetwork) name() string { @@ -206,21 +211,21 @@ func (*DeleteNetwork) asyncResponse() interface{} { type ListNetworks struct { Account string `json:"account,omitempty"` ACLType string `json:"acltype,omitempty"` // Account or Domain - CanUseForDeploy bool `json:"canusefordeploy,omitempty"` - DisplayNetwork bool `json:"displaynetwork,omitempty"` // root only + CanUseForDeploy *bool `json:"canusefordeploy,omitempty"` + DisplayNetwork *bool `json:"displaynetwork,omitempty"` // root only DomainID string `json:"domainid,omitempty"` ForVpc string `json:"forvpc,omitempty"` ID string `json:"id,omitempty"` - IsRecursive bool `json:"isrecursive,omitempty"` - IsSystem bool `json:"issystem,omitempty"` + IsRecursive *bool `json:"isrecursive,omitempty"` + IsSystem *bool `json:"issystem,omitempty"` Keyword string `json:"keyword,omitempty"` - ListAll bool `json:"listall,omitempty"` + ListAll *bool `json:"listall,omitempty"` Page int `json:"page,omitempty"` PageSize int `json:"pagesize,omitempty"` PhysicalNetworkID string `json:"physicalnetworkid,omitempty"` ProjectID string `json:"projectid,omitempty"` - RestartRequired bool `json:"restartrequired,omitempty"` - SpecifyRanges bool `json:"specifyranges,omitempty"` + RestartRequired *bool `json:"restartrequired,omitempty"` + SpecifyRanges *bool `json:"specifyranges,omitempty"` SupportedServices []Service `json:"supportedservices,omitempty"` Tags []ResourceTag `json:"resourcetag,omitempty"` TrafficType string `json:"traffictype,omitempty"` diff --git a/vendor/github.com/exoscale/egoscale/nics.go b/vendor/github.com/exoscale/egoscale/nics.go index b8b405b7f8..22a9ce6ef9 100644 --- a/vendor/github.com/exoscale/egoscale/nics.go +++ b/vendor/github.com/exoscale/egoscale/nics.go @@ -21,7 +21,7 @@ type Nic struct { NetworkID string `json:"networkid,omitempty"` NetworkName string `json:"networkname,omitempty"` SecondaryIP []NicSecondaryIP `json:"secondaryip,omitempty"` - Traffictype string `json:"traffictype,omitempty"` + TrafficType string `json:"traffictype,omitempty"` Type string `json:"type,omitempty"` VirtualMachineID string `json:"virtualmachineid,omitempty"` } diff --git a/vendor/github.com/exoscale/egoscale/request.go b/vendor/github.com/exoscale/egoscale/request.go index 6418965529..1d165fd291 100644 --- a/vendor/github.com/exoscale/egoscale/request.go +++ b/vendor/github.com/exoscale/egoscale/request.go @@ -2,6 +2,7 @@ package egoscale import ( "bytes" + "context" "crypto/hmac" "crypto/sha1" "encoding/base64" @@ -43,6 +44,14 @@ type onBeforeHook interface { onBeforeSend(params *url.Values) error } +// AsyncInfo represents the details for any async call +// +// It retries at most Retries time and waits for Delay between each retry +type AsyncInfo struct { + Retries int + Delay int +} + const ( // Pending represents a job in progress Pending JobStatusType = iota @@ -107,10 +116,17 @@ type JobResultResponse struct { // ErrorResponse represents the standard error response from CloudStack type ErrorResponse struct { - ErrorCode ErrorCode `json:"errorcode"` - CsErrorCode int `json:"cserrorcode"` - ErrorText string `json:"errortext"` - UUIDList []string `json:"uuidList,omitempty"` // uuid*L*ist is not a typo + ErrorCode ErrorCode `json:"errorcode"` + CsErrorCode int `json:"cserrorcode"` + ErrorText string `json:"errortext"` + UUIDList []UUIDItem `json:"uuidList,omitempty"` // uuid*L*ist is not a typo +} + +// UUIDItem represents an item of the UUIDList part of an ErrorResponse +type UUIDItem struct { + Description string `json:"description,omitempty"` + SerialVersionUID int64 `json:"serialVersionUID,omitempty"` + UUID string `json:"uuid"` } // Error formats a CloudStack error into a standard error @@ -146,12 +162,13 @@ func (e *booleanSyncResponse) Error() error { return fmt.Errorf("API error: %s", e.DisplayText) } -// AsyncInfo represents the details for any async call -// -// It retries at most Retries time and waits for Delay between each retry -type AsyncInfo struct { - Retries int - Delay int +type asyncJob struct { + command AsyncCommand + delay int + retries int + responseChan chan<- *AsyncJobResult + errorChan chan<- error + ctx context.Context } func csQuotePlus(s string) string { @@ -212,66 +229,111 @@ func (exo *Client) parseResponse(resp *http.Response) (json.RawMessage, error) { return b, nil } -// AsyncRequest performs an asynchronous request and polls it for retries * day [s] -func (exo *Client) AsyncRequest(req AsyncCommand, async AsyncInfo) (interface{}, error) { - body, err := exo.request(req.name(), req) +func (exo *Client) processAsyncJob(ctx context.Context, job *asyncJob) { + defer close(job.responseChan) + defer close(job.errorChan) + + body, err := exo.request(job.command.name(), job.command) if err != nil { - return nil, err + job.errorChan <- err + return } - // Is it a Job? - job := new(JobResultResponse) - if err := json.Unmarshal(body, &job); err != nil { - return nil, err + jobResult := new(AsyncJobResult) + if err := json.Unmarshal(body, jobResult); err != nil { + r := new(ErrorResponse) + if e := json.Unmarshal(body, r); e != nil { + job.errorChan <- r + return + } + job.errorChan <- err + return } - // Error response - errorResponse := new(ErrorResponse) // Successful response - resp := req.asyncResponse() - if job.JobID == "" || job.JobStatus != Pending { - if err := json.Unmarshal(*job.JobResult, resp); err != nil { - return job, err - } - return resp, nil + if jobResult.JobID == "" || jobResult.JobStatus != Pending { + job.responseChan <- jobResult + return } - // we've got a pending job - result := &QueryAsyncJobResultResponse{ - JobStatus: job.JobStatus, - } - for async.Retries > 0 && result.JobStatus == Pending { - time.Sleep(time.Duration(async.Delay) * time.Second) + for job.retries > 0 { + select { + case <-ctx.Done(): + job.errorChan <- ctx.Err() + return + default: + job.retries-- - async.Retries-- + end, _ := ctx.Deadline() - req := &QueryAsyncJobResult{JobID: job.JobID} - resp, err := exo.Request(req) - if err != nil { - return nil, err - } - result = resp.(*QueryAsyncJobResultResponse) - } + when := end.Add(time.Duration(job.delay*-job.retries) * time.Second) + time.Sleep(when.Sub(time.Now())) - if result.JobStatus == Failure { - if err := json.Unmarshal(*result.JobResult, &errorResponse); err != nil { - return nil, err + req := &QueryAsyncJobResult{JobID: jobResult.JobID} + resp, err := exo.Request(req) + if err != nil { + job.errorChan <- err + return + } + + result := resp.(*QueryAsyncJobResultResponse) + if result.JobStatus == Success { + job.responseChan <- (*AsyncJobResult)(result) + return + } else if result.JobStatus == Failure { + r := new(ErrorResponse) + e := json.Unmarshal(*result.JobResult, r) + if e != nil { + job.errorChan <- e + return + } + job.errorChan <- r + return + } } - return errorResponse, errorResponse } - if result.JobStatus == Pending { - return result, fmt.Errorf("Maximum number of retries reached") - } + job.errorChan <- fmt.Errorf("Maximum number of retries reached") +} - if err := json.Unmarshal(*result.JobResult, resp); err != nil { - if err := json.Unmarshal(*result.JobResult, errorResponse); err != nil { +// AsyncRequest performs an asynchronous request and polls it for retries * day [s] +func (exo *Client) AsyncRequest(req AsyncCommand, async AsyncInfo) (interface{}, error) { + totalTime := time.Duration(async.Delay*async.Retries) * time.Second + end := time.Now().Add(totalTime) + + ctx, cancel := context.WithDeadline(context.Background(), end) + defer cancel() + + responseChan := make(chan *AsyncJobResult, 1) + errorChan := make(chan error, 1) + + go exo.processAsyncJob(ctx, &asyncJob{ + command: req, + delay: async.Delay, + retries: async.Retries, + responseChan: responseChan, + errorChan: errorChan, + ctx: ctx, + }) + + select { + case result := <-responseChan: + cancel() + resp := req.asyncResponse() + if err := json.Unmarshal(*(result.JobResult), resp); err != nil { return nil, err } - return errorResponse, errorResponse - } + return resp, nil - return resp, nil + case err := <-errorChan: + cancel() + return nil, err + + case <-ctx.Done(): + cancel() + err := <-errorChan + return nil, err + } } // BooleanRequest performs a sync request on a boolean call @@ -435,6 +497,19 @@ func prepareValues(prefix string, params *url.Values, command interface{}) error } else { (*params).Set(name, "true") } + case reflect.Ptr: + if val.IsNil() { + if required { + return fmt.Errorf("%s.%s (%v) is required, got empty ptr", typeof.Name(), field.Name, val.Kind()) + } + } else { + switch field.Type.Elem().Kind() { + case reflect.Bool: + params.Set(name, strconv.FormatBool(val.Elem().Bool())) + default: + log.Printf("[SKIP] %s.%s (%v) not supported", typeof.Name(), field.Name, field.Type.Elem().Kind()) + } + } case reflect.Slice: switch field.Type.Elem().Kind() { case reflect.Uint8: diff --git a/vendor/github.com/exoscale/egoscale/security_groups.go b/vendor/github.com/exoscale/egoscale/security_groups.go index ae2c30d475..c970515287 100644 --- a/vendor/github.com/exoscale/egoscale/security_groups.go +++ b/vendor/github.com/exoscale/egoscale/security_groups.go @@ -24,6 +24,11 @@ type SecurityGroup struct { JobStatus JobStatusType `json:"jobstatus,omitempty"` } +// ResourceType returns the type of the resource +func (*SecurityGroup) ResourceType() string { + return "SecurityGroup" +} + // IngressRule represents the ingress rule type IngressRule struct { RuleID string `json:"ruleid"` @@ -193,9 +198,9 @@ type ListSecurityGroups struct { Account string `json:"account,omitempty"` DomainID string `json:"domainid,omitempty"` ID string `json:"id,omitempty"` - IsRecursive bool `json:"isrecursive,omitempty"` + IsRecursive *bool `json:"isrecursive,omitempty"` Keyword string `json:"keyword,omitempty"` - ListAll bool `json:"listall,omitempty"` + ListAll *bool `json:"listall,omitempty"` Page int `json:"page,omitempty"` PageSize int `json:"pagesize,omitempty"` ProjectID string `json:"projectid,omitempty"` diff --git a/vendor/github.com/exoscale/egoscale/service_offerings.go b/vendor/github.com/exoscale/egoscale/service_offerings.go index 3c9a0b4ab1..c3a6dac67d 100644 --- a/vendor/github.com/exoscale/egoscale/service_offerings.go +++ b/vendor/github.com/exoscale/egoscale/service_offerings.go @@ -40,8 +40,8 @@ type ServiceOffering struct { type ListServiceOfferings struct { DomainID string `json:"domainid,omitempty"` ID string `json:"id,omitempty"` - IsRecursive bool `json:"isrecursive,omitempty"` - IsSystem bool `json:"issystem,omitempty"` + IsRecursive *bool `json:"isrecursive,omitempty"` + IsSystem *bool `json:"issystem,omitempty"` Keyword string `json:"keyword,omitempty"` Name string `json:"name,omitempty"` Page int `json:"page,omitempty"` diff --git a/vendor/github.com/exoscale/egoscale/snapshots.go b/vendor/github.com/exoscale/egoscale/snapshots.go index 4dee5058b2..55e5956c34 100644 --- a/vendor/github.com/exoscale/egoscale/snapshots.go +++ b/vendor/github.com/exoscale/egoscale/snapshots.go @@ -25,6 +25,11 @@ type Snapshot struct { JobStatus JobStatusType `json:"jobstatus,omitempty"` } +// ResourceType returns the type of the resource +func (*Snapshot) ResourceType() string { + return "Snapshot" +} + // CreateSnapshot represents a request to create a volume snapshot // // CloudStackAPI: http://cloudstack.apache.org/api/apidocs-4.10/apis/createSnapshot.html @@ -33,7 +38,7 @@ type CreateSnapshot struct { Account string `json:"account,omitempty"` DomainID string `json:"domainid,omitempty"` PolicyID string `json:"policyid,omitempty"` - QuiesceVM bool `json:"quiescevm,omitempty"` + QuiesceVM *bool `json:"quiescevm,omitempty"` } func (*CreateSnapshot) name() string { @@ -57,9 +62,9 @@ type ListSnapshots struct { DomainID string `json:"domainid,omitempty"` ID string `json:"id,omitempty"` IntervalType string `json:"intervaltype,omitempty"` - IsRecursive bool `json:"isrecursive,omitempty"` + IsRecursive *bool `json:"isrecursive,omitempty"` Keyword string `json:"keyword,omitempty"` - ListAll bool `json:"listall,omitempty"` + ListAll *bool `json:"listall,omitempty"` Name string `json:"name,omitempty"` Page int `json:"page,omitempty"` PageSize int `json:"pagesize,omitempty"` diff --git a/vendor/github.com/exoscale/egoscale/tags.go b/vendor/github.com/exoscale/egoscale/tags.go index 0ea3386fe2..e921231af6 100644 --- a/vendor/github.com/exoscale/egoscale/tags.go +++ b/vendor/github.com/exoscale/egoscale/tags.go @@ -1,5 +1,13 @@ package egoscale +// Taggable represents a resource which can have tags attached +// +// This is a helper to fill the resourcetype of a CreateTags call +type Taggable interface { + // CloudStack resource type of the Taggable type + ResourceType() string +} + // ResourceTag is a tag associated with a resource // // http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/4.9/management.html @@ -58,10 +66,10 @@ type ListTags struct { Account string `json:"account,omitempty"` Customer string `json:"customer,omitempty"` DomainID string `json:"domainid,omitempty"` - IsRecursive bool `json:"isrecursive,omitempty"` + IsRecursive *bool `json:"isrecursive,omitempty"` Key string `json:"key,omitempty"` Keyword string `json:"keyword,omitempty"` - ListAll bool `json:"listall,omitempty"` + ListAll *bool `json:"listall,omitempty"` Page int `json:"page,omitempty"` PageSize int `json:"pagesize,omitempty"` ProjectID string `json:"projectid,omitempty"` diff --git a/vendor/github.com/exoscale/egoscale/templates.go b/vendor/github.com/exoscale/egoscale/templates.go index f71bbebaed..7f30bdf055 100644 --- a/vendor/github.com/exoscale/egoscale/templates.go +++ b/vendor/github.com/exoscale/egoscale/templates.go @@ -37,6 +37,11 @@ type Template struct { Zonename string `json:"zonename,omitempty"` } +// ResourceType returns the type of the resource +func (*Template) ResourceType() string { + return "Template" +} + // ListTemplates represents a template query filter type ListTemplates struct { TemplateFilter string `json:"templatefilter"` // featured, etc. @@ -44,14 +49,14 @@ type ListTemplates struct { DomainID string `json:"domainid,omitempty"` Hypervisor string `json:"hypervisor,omitempty"` ID string `json:"id,omitempty"` - IsRecursive bool `json:"isrecursive,omitempty"` + IsRecursive *bool `json:"isrecursive,omitempty"` Keyword string `json:"keyword,omitempty"` - ListAll bool `json:"listall,omitempty"` + ListAll *bool `json:"listall,omitempty"` Name string `json:"name,omitempty"` Page int `json:"page,omitempty"` PageSize int `json:"pagesize,omitempty"` ProjectID string `json:"projectid,omitempty"` - ShowRemoved bool `json:"showremoved,omitempty"` + ShowRemoved *bool `json:"showremoved,omitempty"` Tags []ResourceTag `json:"tags,omitempty"` ZoneID string `json:"zoneid,omitempty"` } diff --git a/vendor/github.com/exoscale/egoscale/users.go b/vendor/github.com/exoscale/egoscale/users.go new file mode 100644 index 0000000000..353e4b6bab --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/users.go @@ -0,0 +1,43 @@ +package egoscale + +// User represents a User +type User struct { + Account string `json:"account,omitempty"` + AccountID string `json:"accountid,omitempty"` + AccountType string `json:"accounttype,omitempty"` + APIKey string `json:"apikey,omitempty"` + Created string `json:"created,omitempty"` + Domain string `json:"domain,omitempty"` + DomainID string `json:"domainid,omitempty"` + Email string `json:"email,omitempty"` + FirstName string `json:"firstname,omitempty"` + ID string `json:"id,omitempty"` + IsCallerChildDomain bool `json:"iscallerchilddomain,omitempty"` + IsDefault bool `json:"isdefault,omitempty"` + LastName string `json:"lastname,omitempty"` + SecretKey string `json:"secretkey,omitempty"` + State string `json:"state,omitempty"` + UserName string `json:"username,omitempty"` +} + +// RegisterUserKeys registers a new set of key of the given user +// +// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/registerUserKeys.html +type RegisterUserKeys struct { + ID string `json:"id"` +} + +func (*RegisterUserKeys) name() string { + return "registerUserKeys" +} + +func (*RegisterUserKeys) response() interface{} { + return new(RegisterUserKeysResponse) +} + +// RegisterUserKeysResponse represents a new set of UserKeys +// +// NB: only the APIKey and SecretKey will be filled +type RegisterUserKeysResponse struct { + UserKeys User `json:"userkeys"` +} diff --git a/vendor/github.com/exoscale/egoscale/virtual_machines.go b/vendor/github.com/exoscale/egoscale/virtual_machines.go index c660ccbff2..efc6ae5b72 100644 --- a/vendor/github.com/exoscale/egoscale/virtual_machines.go +++ b/vendor/github.com/exoscale/egoscale/virtual_machines.go @@ -27,7 +27,7 @@ type VirtualMachine struct { Group string `json:"group,omitempty"` GroupID string `json:"groupid,omitempty"` GuestOsID string `json:"guestosid,omitempty"` - HaEnable bool `json:"haenable,omitempty"` + HAEnable bool `json:"haenable,omitempty"` HostID string `json:"hostid,omitempty"` HostName string `json:"hostname,omitempty"` Hypervisor string `json:"hypervisor,omitempty"` @@ -77,6 +77,22 @@ type VirtualMachine struct { JobStatus JobStatusType `json:"jobstatus,omitempty"` } +// ResourceType returns the type of the resource +func (*VirtualMachine) ResourceType() string { + return "UserVM" +} + +// DefaultNic returns the default nic +func (vm *VirtualMachine) DefaultNic() *Nic { + for _, nic := range vm.Nic { + if nic.IsDefault { + return &nic + } + } + + return nil +} + // NicsByType returns the corresponding interfaces base on the given type func (vm *VirtualMachine) NicsByType(nicType string) []Nic { nics := make([]Nic, 0) @@ -91,6 +107,8 @@ func (vm *VirtualMachine) NicsByType(nicType string) []Nic { } // NicByNetworkID returns the corresponding interface based on the given NetworkID +// +// A VM cannot be connected twice to a same network. func (vm *VirtualMachine) NicByNetworkID(networkID string) *Nic { for _, nic := range vm.Nic { if nic.NetworkID == networkID { @@ -140,7 +158,7 @@ type DeployVirtualMachine struct { Details map[string]string `json:"details,omitempty"` DiskOfferingID string `json:"diskofferingid,omitempty"` DisplayName string `json:"displayname,omitempty"` - DisplayVM bool `json:"displayvm,omitempty"` + DisplayVM *bool `json:"displayvm,omitempty"` DomainID string `json:"domainid,omitempty"` Group string `json:"group,omitempty"` HostID string `json:"hostid,omitempty"` @@ -157,7 +175,7 @@ type DeployVirtualMachine struct { SecurityGroupIDs []string `json:"securitygroupids,omitempty"` SecurityGroupNames []string `json:"securitygroupnames,omitempty"` // does nothing, mutually exclusive Size string `json:"size,omitempty"` // mutually exclusive with DiskOfferingID - StartVM bool `json:"startvm,omitempty"` + StartVM *bool `json:"startvm,omitempty"` UserData string `json:"userdata,omitempty"` // the client is responsible to base64/gzip it } @@ -196,7 +214,7 @@ type StartVirtualMachineResponse VirtualMachineResponse // CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/stopVirtualMachine.html type StopVirtualMachine struct { ID string `json:"id"` - Forced bool `json:"forced,omitempty"` + Forced *bool `json:"forced,omitempty"` } func (*StopVirtualMachine) name() string { @@ -234,6 +252,7 @@ type RebootVirtualMachineResponse VirtualMachineResponse type RestoreVirtualMachine struct { VirtualMachineID string `json:"virtualmachineid"` TemplateID string `json:"templateid,omitempty"` + RootDiskSize string `json:"rootdisksize,omitempty"` // in GiB, Exoscale specific } func (*RestoreVirtualMachine) name() string { @@ -270,7 +289,7 @@ type RecoverVirtualMachineResponse VirtualMachineResponse // CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/destroyVirtualMachine.html type DestroyVirtualMachine struct { ID string `json:"id"` - Expunge bool `json:"expunge,omitempty"` + Expunge *bool `json:"expunge,omitempty"` } func (*DestroyVirtualMachine) name() string { @@ -292,10 +311,10 @@ type UpdateVirtualMachine struct { CustomID string `json:"customid,omitempty"` // root only Details map[string]string `json:"details,omitempty"` DisplayName string `json:"displayname,omitempty"` - DisplayVM bool `json:"displayvm,omitempty"` + DisplayVM *bool `json:"displayvm,omitempty"` Group string `json:"group,omitempty"` - HAEnable bool `json:"haenable,omitempty"` - IsDynamicallyScalable bool `json:"isdynamicallyscalable,omitempty"` + HAEnable *bool `json:"haenable,omitempty"` + IsDynamicallyScalable *bool `json:"isdynamicallyscalable,omitempty"` Name string `json:"name,omitempty"` // must reboot OsTypeID int64 `json:"ostypeid,omitempty"` SecurityGroupIDs []string `json:"securitygroupids,omitempty"` @@ -406,19 +425,19 @@ type ListVirtualMachines struct { Account string `json:"account,omitempty"` AffinityGroupID string `json:"affinitygroupid,omitempty"` Details map[string]string `json:"details,omitempty"` - DisplayVM bool `json:"displayvm,omitempty"` // root only + DisplayVM *bool `json:"displayvm,omitempty"` // root only DomainID string `json:"domainid,omitempty"` - ForVirtualNetwork bool `json:"forvirtualnetwork,omitempty"` + ForVirtualNetwork *bool `json:"forvirtualnetwork,omitempty"` GroupID string `json:"groupid,omitempty"` HostID string `json:"hostid,omitempty"` Hypervisor string `json:"hypervisor,omitempty"` ID string `json:"id,omitempty"` IDs []string `json:"ids,omitempty"` // mutually exclusive with id IsoID string `json:"isoid,omitempty"` - IsRecursive bool `json:"isrecursive,omitempty"` + IsRecursive *bool `json:"isrecursive,omitempty"` KeyPair string `json:"keypair,omitempty"` Keyword string `json:"keyword,omitempty"` - ListAll bool `json:"listall,omitempty"` + ListAll *bool `json:"listall,omitempty"` Name string `json:"name,omitempty"` NetworkID string `json:"networkid,omitempty"` Page int `json:"page,omitempty"` diff --git a/vendor/github.com/exoscale/egoscale/vm_groups.go b/vendor/github.com/exoscale/egoscale/vm_groups.go new file mode 100644 index 0000000000..12dffa54da --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/vm_groups.go @@ -0,0 +1,106 @@ +package egoscale + +// InstanceGroup represents a group of VM +type InstanceGroup struct { + ID string `json:"id"` + Account string `json:"account,omitempty"` + Created string `json:"created,omitempty"` + Domain string `json:"domain,omitempty"` + DomainID string `json:"domainid,omitempty"` + Name string `json:"name,omitempty"` + Project string `json:"project,omitempty"` + ProjectID string `json:"projectid,omitempty"` +} + +// InstanceGroupResponse represents a VM group +type InstanceGroupResponse struct { + InstanceGroup InstanceGroup `json:"instancegroup"` +} + +// CreateInstanceGroup creates a VM group +// +// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/createInstanceGroup.html +type CreateInstanceGroup struct { + Name string `json:"name"` + Account string `json:"account,omitempty"` + DomainID string `json:"domainid,omitempty"` + ProjectID string `json:"projectid,omitempty"` +} + +func (*CreateInstanceGroup) name() string { + return "createInstanceGroup" +} + +func (*CreateInstanceGroup) response() interface{} { + return new(CreateInstanceGroupResponse) +} + +// CreateInstanceGroupResponse represents a freshly created VM group +type CreateInstanceGroupResponse InstanceGroupResponse + +// UpdateInstanceGroup creates a VM group +// +// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/updateInstanceGroup.html +type UpdateInstanceGroup struct { + ID string `json:"id"` + Name string `json:"name,omitempty"` +} + +func (*UpdateInstanceGroup) name() string { + return "updateInstanceGroup" +} + +func (*UpdateInstanceGroup) response() interface{} { + return new(UpdateInstanceGroupResponse) +} + +// UpdateInstanceGroupResponse represents an updated VM group +type UpdateInstanceGroupResponse InstanceGroupResponse + +// DeleteInstanceGroup creates a VM group +// +// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/deleteInstanceGroup.html +type DeleteInstanceGroup struct { + Name string `json:"name"` + Account string `json:"account,omitempty"` + DomainID string `json:"domainid,omitempty"` + ProjectID string `json:"projectid,omitempty"` +} + +func (*DeleteInstanceGroup) name() string { + return "deleteInstanceGroup" +} + +func (*DeleteInstanceGroup) response() interface{} { + return new(booleanSyncResponse) +} + +// ListInstanceGroups lists VM groups +// +// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/listInstanceGroups.html +type ListInstanceGroups struct { + Account string `json:"account,omitempty"` + DomainID string `json:"domainid,omitempty"` + ID string `json:"id,omitempty"` + IsRecursive *bool `json:"isrecursive,omitempty"` + Keyword string `json:"keyword,omitempty"` + ListAll *bool `json:"listall,omitempty"` + Page int `json:"page,omitempty"` + PageSize int `json:"pagesize,omitempty"` + State string `json:"state,omitempty"` + ProjectID string `json:"projectid,omitempty"` +} + +func (*ListInstanceGroups) name() string { + return "listInstanceGroups" +} + +func (*ListInstanceGroups) response() interface{} { + return new(ListInstanceGroupsResponse) +} + +// ListInstanceGroupsResponse represents a list of instance groups +type ListInstanceGroupsResponse struct { + Count int `json:"count"` + InstanceGroup []InstanceGroup `json:"instancegroup"` +} diff --git a/vendor/github.com/exoscale/egoscale/volumes.go b/vendor/github.com/exoscale/egoscale/volumes.go index a93d3d6a8c..3914275655 100644 --- a/vendor/github.com/exoscale/egoscale/volumes.go +++ b/vendor/github.com/exoscale/egoscale/volumes.go @@ -33,13 +33,18 @@ type Volume struct { JobStatus JobStatusType `json:"jobstatus,omitempty"` } +// ResourceType returns the type of the resource +func (*Volume) ResourceType() string { + return "Volume" +} + // ResizeVolume (Async) resizes a volume // // CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/resizeVolume.html type ResizeVolume struct { ID string `json:"id"` DiskOfferingID string `json:"diskofferingid,omitempty"` - ShrinkOk bool `json:"shrinkok,omitempty"` + ShrinkOk *bool `json:"shrinkok,omitempty"` Size int64 `json:"size,omitempty"` // in GiB } @@ -66,9 +71,9 @@ type ListVolumes struct { DomainID string `json:"domainid,omitempty"` HostID string `json:"hostid,omitempty"` ID string `json:"id,omitempty"` - IsRecursive bool `json:"isrecursive,omitempty"` + IsRecursive *bool `json:"isrecursive,omitempty"` Keyword string `json:"keyword,omitempty"` - ListAll bool `json:"listall,omitempty"` + ListAll *bool `json:"listall,omitempty"` Name string `json:"name,omitempty"` Page int `json:"page,omitempty"` PageSize int `json:"pagesize,omitempty"` diff --git a/vendor/github.com/exoscale/egoscale/zones.go b/vendor/github.com/exoscale/egoscale/zones.go index 228a7e3696..59642f0389 100644 --- a/vendor/github.com/exoscale/egoscale/zones.go +++ b/vendor/github.com/exoscale/egoscale/zones.go @@ -34,14 +34,14 @@ type Zone struct { // // CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/listZones.html type ListZones struct { - Available bool `json:"available,omitempty"` + Available *bool `json:"available,omitempty"` DomainID string `json:"domainid,omitempty"` ID string `json:"id,omitempty"` Keyword string `json:"keyword,omitempty"` Name string `json:"name,omitempty"` Page int `json:"page,omitempty"` PageSize int `json:"pagesize,omitempty"` - ShowCapacities bool `json:"showcapacities,omitempty"` + ShowCapacities *bool `json:"showcapacities,omitempty"` Tags []ResourceTag `json:"tags,omitempty"` }