diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..b7513274c --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +vendor/github.com/xenolf/lego/providers/dns/cloudxns/cloudxns.go eol=crlf + diff --git a/Gopkg.lock b/Gopkg.lock index 94cdddb86..33e617f69 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -525,7 +525,8 @@ [[projects]] name = "github.com/exoscale/egoscale" packages = ["."] - revision = "325740036187ddae3a5b74be00fbbc70011c4d96" + revision = "e4fedc381fbddb7fef4d7060388a726c6de37c88" + version = "v0.9.7" [[projects]] name = "github.com/fatih/color" @@ -1182,6 +1183,7 @@ "providers/dns/auroradns", "providers/dns/azure", "providers/dns/cloudflare", + "providers/dns/cloudxns", "providers/dns/digitalocean", "providers/dns/dnsimple", "providers/dns/dnsmadeeasy", @@ -1189,6 +1191,7 @@ "providers/dns/dyn", "providers/dns/exoscale", "providers/dns/gandi", + "providers/dns/gandiv5", "providers/dns/godaddy", "providers/dns/googlecloud", "providers/dns/linode", @@ -1202,7 +1205,7 @@ "providers/dns/route53", "providers/dns/vultr" ] - revision = "b929aa5aab5ad2e197bb3d74ef99fac61bfa47bc" + revision = "06a8e7c475c03ef8d4773284ac63357d2810601b" [[projects]] branch = "master" diff --git a/docs/configuration/acme.md b/docs/configuration/acme.md index 3248ecc6b..f72b3c908 100644 --- a/docs/configuration/acme.md +++ b/docs/configuration/acme.md @@ -113,7 +113,7 @@ entryPoint = "https" # Required # entryPoint = "http" - + # Use a DNS-01 acme challenge rather than TLS-SNI-01 challenge # # Optional @@ -272,7 +272,7 @@ Use `DNS-01` challenge to generate/renew ACME certificates. # ... ``` -#### `provider` +#### `provider` Select the provider that matches the DNS domain that will host the challenge TXT record, and provide environment variables to enable setting it: @@ -281,6 +281,7 @@ Select the provider that matches the DNS domain that will host the challenge TXT | [Auroradns](https://www.pcextreme.com/aurora/dns) | `auroradns` | `AURORA_USER_ID`, `AURORA_KEY`, `AURORA_ENDPOINT` | | [Azure](https://azure.microsoft.com/services/dns/) | `azure` | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `AZURE_TENANT_ID`, `AZURE_RESOURCE_GROUP` | | [Cloudflare](https://www.cloudflare.com) | `cloudflare` | `CLOUDFLARE_EMAIL`, `CLOUDFLARE_API_KEY` - The Cloudflare `Global API Key` needs to be used and not the `Origin CA Key` | +| [CloudXNS](https://www.cloudxns.net) | `cloudxns` | `CLOUDXNS_API_KEY`, `CLOUDXNS_SECRET_KEY` | | [DigitalOcean](https://www.digitalocean.com) | `digitalocean` | `DO_AUTH_TOKEN` | | [DNSimple](https://dnsimple.com) | `dnsimple` | `DNSIMPLE_OAUTH_TOKEN`, `DNSIMPLE_BASE_URL` | | [DNS Made Easy](https://dnsmadeeasy.com) | `dnsmadeeasy` | `DNSMADEEASY_API_KEY`, `DNSMADEEASY_API_SECRET`, `DNSMADEEASY_SANDBOX` | @@ -288,6 +289,7 @@ Select the provider that matches the DNS domain that will host the challenge TXT | [Dyn](https://dyn.com) | `dyn` | `DYN_CUSTOMER_NAME`, `DYN_USER_NAME`, `DYN_PASSWORD` | | [Exoscale](https://www.exoscale.ch) | `exoscale` | `EXOSCALE_API_KEY`, `EXOSCALE_API_SECRET`, `EXOSCALE_ENDPOINT` | | [Gandi](https://www.gandi.net) | `gandi` | `GANDI_API_KEY` | +| [Gandi V5](http://doc.livedns.gandi.net) | `gandiv5` | `GANDIV5_API_KEY` | | [GoDaddy](https://godaddy.com/domains) | `godaddy` | `GODADDY_API_KEY`, `GODADDY_API_SECRET` | | [Google Cloud DNS](https://cloud.google.com/dns/docs/) | `gcloud` | `GCE_PROJECT`, `GCE_SERVICE_ACCOUNT_FILE` | | [Linode](https://www.linode.com) | `linode` | `LINODE_API_KEY` | @@ -330,7 +332,7 @@ This will request a certificate from Let's Encrypt during the first TLS handshak !!! warning TLS handshakes will be slow when requesting a hostname certificate for the first time, this can lead to DoS attacks. - + !!! warning Take note that Let's Encrypt have [rate limiting](https://letsencrypt.org/docs/rate-limits). diff --git a/vendor/github.com/exoscale/egoscale/accounts.go b/vendor/github.com/exoscale/egoscale/accounts.go new file mode 100644 index 000000000..1c8b4d115 --- /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 new file mode 100644 index 000000000..649c4acd8 --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/addresses.go @@ -0,0 +1,148 @@ +package egoscale + +import "net" + +// IPAddress represents an IP Address +type IPAddress struct { + ID string `json:"id"` + Account string `json:"account,omitempty"` + AllocatedAt string `json:"allocated,omitempty"` + AssociatedNetworkID string `json:"associatednetworkid,omitempty"` + AssociatedNetworkName string `json:"associatednetworkname,omitempty"` + DomainID string `json:"domainid,omitempty"` + DomainName string `json:"domainname,omitempty"` + ForDisplay bool `json:"fordisplay,omitempty"` + ForVirtualNetwork bool `json:"forvirtualnetwork,omitempty"` + IPAddress net.IP `json:"ipaddress"` + IsElastic bool `json:"iselastic,omitempty"` + IsPortable bool `json:"isportable,omitempty"` + IsSourceNat bool `json:"issourcenat,omitempty"` + IsSystem bool `json:"issystem,omitempty"` + NetworkID string `json:"networkid,omitempty"` + PhysicalNetworkID string `json:"physicalnetworkid,omitempty"` + Project string `json:"project,omitempty"` + ProjectID string `json:"projectid,omitempty"` + Purpose string `json:"purpose,omitempty"` + State string `json:"state,omitempty"` + VirtualMachineDisplayName string `json:"virtualmachinedisplayname,omitempty"` + VirtualMachineID string `json:"virtualmachineid,omitempty"` + VirtualMachineName string `json:"virtualmachineName,omitempty"` + VlanID string `json:"vlanid,omitempty"` + VlanName string `json:"vlanname,omitempty"` + VMIPAddress net.IP `json:"vmipaddress,omitempty"` + VpcID string `json:"vpcid,omitempty"` + ZoneID string `json:"zoneid,omitempty"` + ZoneName string `json:"zonename,omitempty"` + Tags []ResourceTag `json:"tags,omitempty"` + JobID string `json:"jobid,omitempty"` + 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"` + NetworkdID string `json:"networkid,omitempty"` + ProjectID string `json:"projectid,omitempty"` + RegionID string `json:"regionid,omitempty"` + VpcID string `json:"vpcid,omitempty"` + ZoneID string `json:"zoneid,omitempty"` +} + +func (*AssociateIPAddress) name() string { + return "associateIpAddress" +} + +func (*AssociateIPAddress) asyncResponse() interface{} { + return new(AssociateIPAddressResponse) +} + +// AssociateIPAddressResponse represents the response to the creation of an IPAddress +type AssociateIPAddressResponse struct { + IPAddress IPAddress `json:"ipaddress"` +} + +// DisassociateIPAddress (Async) represents the IP deletion +// +// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/disassociateIpAddress.html +type DisassociateIPAddress struct { + ID string `json:"id"` +} + +func (*DisassociateIPAddress) name() string { + return "disassociateIpAddress" +} +func (*DisassociateIPAddress) asyncResponse() interface{} { + return new(booleanAsyncResponse) +} + +// UpdateIPAddress (Async) represents the IP modification +// +// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/updateIpAddress.html +type UpdateIPAddress struct { + ID string `json:"id"` + CustomID string `json:"customid,omitempty"` // root only + ForDisplay bool `json:"fordisplay,omitempty"` +} + +func (*UpdateIPAddress) name() string { + return "updateIpAddress" +} +func (*UpdateIPAddress) asyncResponse() interface{} { + return new(UpdateIPAddressResponse) +} + +// UpdateIPAddressResponse represents the modified IP Address +type UpdateIPAddressResponse AssociateIPAddressResponse + +// ListPublicIPAddresses represents a search for public IP addresses +// +// 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"` + AllocatedNetworkID string `json:"allocatednetworkid,omitempty"` + DomainID string `json:"domainid,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"` + Keyword string `json:"keyword,omitempty"` + ListAll bool `json:"listall,omiempty"` + Page int `json:"page,omitempty"` + PageSize int `json:"pagesize,omitempty"` + PhysicalNetworkID string `json:"physicalnetworkid,omitempty"` + ProjectID string `json:"projectid,omitempty"` + Tags []ResourceTag `json:"tags,omitempty"` + VlanID string `json:"vlanid,omitempty"` + VpcID string `json:"vpcid,omitempty"` + ZoneID string `json:"zoneid,omitempty"` +} + +func (*ListPublicIPAddresses) name() string { + return "listPublicIpAddresses" +} + +func (*ListPublicIPAddresses) response() interface{} { + return new(ListPublicIPAddressesResponse) +} + +// ListPublicIPAddressesResponse represents a list of public IP addresses +type ListPublicIPAddressesResponse struct { + Count int `json:"count"` + PublicIPAddress []IPAddress `json:"publicipaddress"` +} diff --git a/vendor/github.com/exoscale/egoscale/affinity_groups.go b/vendor/github.com/exoscale/egoscale/affinity_groups.go new file mode 100644 index 000000000..eed628ce9 --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/affinity_groups.go @@ -0,0 +1,176 @@ +package egoscale + +import ( + "net/url" +) + +// AffinityGroup represents an (anti-)affinity group +type AffinityGroup struct { + ID string `json:"id,omitempty"` + Account string `json:"account,omitempty"` + Description string `json:"description,omitempty"` + Domain string `json:"domain,omitempty"` + 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 +} + +// AffinityGroupType represent an affinity group type +type AffinityGroupType struct { + Type string `json:"type"` +} + +// CreateAffinityGroup (Async) represents a new (anti-)affinity group +// +// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/createAffinityGroup.html +type CreateAffinityGroup struct { + Name string `json:"name"` + Type string `json:"type"` + Account string `json:"account,omitempty"` + Description string `json:"description,omitempty"` + DomainID string `json:"domainid,omitempty"` +} + +func (*CreateAffinityGroup) name() string { + return "createAffinityGroup" +} + +func (*CreateAffinityGroup) asyncResponse() interface{} { + return new(CreateAffinityGroupResponse) +} + +// CreateAffinityGroupResponse represents the response of the creation of an (anti-)affinity group +type CreateAffinityGroupResponse struct { + AffinityGroup AffinityGroup `json:"affinitygroup"` +} + +// UpdateVMAffinityGroup (Async) represents a modification of a (anti-)affinity group +// +// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/updateVMAffinityGroup.html +type UpdateVMAffinityGroup struct { + ID string `json:"id"` + AffinityGroupIDs []string `json:"affinitygroupids,omitempty"` // mutually exclusive with names + AffinityGroupNames []string `json:"affinitygroupnames,omitempty"` // mutually exclusive with ids +} + +func (*UpdateVMAffinityGroup) name() string { + return "updateVMAffinityGroup" +} + +func (*UpdateVMAffinityGroup) asyncResponse() interface{} { + return new(UpdateVMAffinityGroupResponse) +} + +func (req *UpdateVMAffinityGroup) onBeforeSend(params *url.Values) error { + // Either AffinityGroupIDs or AffinityGroupNames must be set + if len(req.AffinityGroupIDs) == 0 && len(req.AffinityGroupNames) == 0 { + params.Set("affinitygroupids", "") + } + return nil +} + +// UpdateVMAffinityGroupResponse represents the new VM +type UpdateVMAffinityGroupResponse VirtualMachineResponse + +// DeleteAffinityGroup (Async) represents an (anti-)affinity group to be deleted +// +// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/deleteAffinityGroup.html +type DeleteAffinityGroup struct { + ID string `json:"id,omitempty"` + Name string `json:"name,omitempty"` + Type string `json:"type,omitempty"` + Account string `json:"account,omitempty"` + Description string `json:"description,omitempty"` + DomainID string `json:"domainid,omitempty"` +} + +func (*DeleteAffinityGroup) name() string { + return "deleteAffinityGroup" +} + +func (*DeleteAffinityGroup) asyncResponse() interface{} { + return new(booleanAsyncResponse) +} + +// ListAffinityGroups represents an (anti-)affinity groups search +// +// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/listAffinityGroups.html +type ListAffinityGroups 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"` + Name string `json:"name,omitempty"` + Page int `json:"page,omitempty"` + PageSize int `json:"pagesize,omitempty"` + Type string `json:"type,omitempty"` + VirtualMachineID string `json:"virtualmachineid,omitempty"` +} + +func (*ListAffinityGroups) name() string { + return "listAffinityGroups" +} + +func (*ListAffinityGroups) response() interface{} { + return new(ListAffinityGroupsResponse) +} + +// ListAffinityGroupTypes represents an (anti-)affinity groups search +// +// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/listAffinityGroupTypes.html +type ListAffinityGroupTypes struct { + Keyword string `json:"keyword,omitempty"` + Page int `json:"page,omitempty"` + PageSize int `json:"pagesize,omitempty"` +} + +func (*ListAffinityGroupTypes) name() string { + return "listAffinityGroupTypes" +} + +func (*ListAffinityGroupTypes) response() interface{} { + return new(ListAffinityGroupTypesResponse) +} + +// ListAffinityGroupsResponse represents a list of (anti-)affinity groups +type ListAffinityGroupsResponse struct { + Count int `json:"count"` + AffinityGroup []AffinityGroup `json:"affinitygroup"` +} + +// ListAffinityGroupTypesResponse represents a list of (anti-)affinity group types +type ListAffinityGroupTypesResponse struct { + Count int `json:"count"` + AffinityGroupType []AffinityGroupType `json:"affinitygrouptype"` +} + +// Legacy methods + +// CreateAffinityGroup creates a group +// +// Deprecated: Use the API directly +func (exo *Client) CreateAffinityGroup(name string, async AsyncInfo) (*AffinityGroup, error) { + req := &CreateAffinityGroup{ + Name: name, + } + resp, err := exo.AsyncRequest(req, async) + if err != nil { + return nil, err + } + + ag := resp.(*CreateAffinityGroupResponse).AffinityGroup + return &ag, nil +} + +// DeleteAffinityGroup deletes a group +// +// Deprecated: Use the API directly +func (exo *Client) DeleteAffinityGroup(name string, async AsyncInfo) error { + req := &DeleteAffinityGroup{ + Name: name, + } + return exo.BooleanAsyncRequest(req, async) +} diff --git a/vendor/github.com/exoscale/egoscale/affinitygroup.go b/vendor/github.com/exoscale/egoscale/affinitygroup.go deleted file mode 100644 index 932f406ec..000000000 --- a/vendor/github.com/exoscale/egoscale/affinitygroup.go +++ /dev/null @@ -1,42 +0,0 @@ -package egoscale - -import ( - "encoding/json" - "net/url" -) - -func (exo *Client) CreateAffinityGroup(name string) (string, error) { - params := url.Values{} - params.Set("name", name) - params.Set("type", "host anti-affinity") - - resp, err := exo.Request("createAffinityGroup", params) - if err != nil { - return "", err - } - - var r CreateAffinityGroupResponse - if err := json.Unmarshal(resp, &r); err != nil { - return "", err - } - - return r.JobId, nil -} - -func (exo *Client) DeleteAffinityGroup(name string) (string, error) { - params := url.Values{} - params.Set("name", name) - - resp, err := exo.Request("deleteAffinityGroup", params) - if err != nil { - return "", err - } - - var r DeleteAffinityGroupResponse - if err := json.Unmarshal(resp, &r); err != nil { - return "", err - } - - return r.JobId, nil - -} diff --git a/vendor/github.com/exoscale/egoscale/apis.go b/vendor/github.com/exoscale/egoscale/apis.go new file mode 100644 index 000000000..18083d1a1 --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/apis.go @@ -0,0 +1,52 @@ +package egoscale + +// API represents an API service +type API struct { + Description string `json:"description"` + IsAsync bool `json:"isasync"` + Name string `json:"name"` + Related string `json:"related"` // comma separated + Since string `json:"since"` + Type string `json:"type"` + Params []APIParam `json:"params"` + Response []APIResponse `json:"responses"` +} + +// APIParam represents an API parameter field +type APIParam struct { + Description string `json:"description"` + Length int64 `json:"length"` + Name string `json:"name"` + Related string `json:"related"` // comma separated + Since string `json:"since"` + Type string `json:"type"` +} + +// APIResponse represents an API response field +type APIResponse struct { + Description string `json:"description"` + Name string `json:"name"` + Response []APIResponse `json:"response"` + Type string `json:"type"` +} + +// ListAPIs represents a query to list the api +// +// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/listApis.html +type ListAPIs struct { + Name string `json:"name,omitempty"` +} + +func (*ListAPIs) name() string { + return "listApis" +} + +func (*ListAPIs) response() interface{} { + return new(ListAPIsResponse) +} + +// ListAPIsResponse represents a list of API +type ListAPIsResponse struct { + Count int `json:"count"` + API []API `json:"api"` +} diff --git a/vendor/github.com/exoscale/egoscale/async.go b/vendor/github.com/exoscale/egoscale/async.go deleted file mode 100644 index 84401e7a4..000000000 --- a/vendor/github.com/exoscale/egoscale/async.go +++ /dev/null @@ -1,36 +0,0 @@ -package egoscale - -import ( - "encoding/json" - "net/url" -) - -func (exo *Client) PollAsyncJob(jobid string) (*QueryAsyncJobResultResponse, error) { - params := url.Values{} - - params.Set("jobid", jobid) - - resp, err := exo.Request("queryAsyncJobResult", params) - - if err != nil { - return nil, err - } - - var r QueryAsyncJobResultResponse - - if err := json.Unmarshal(resp, &r); err != nil { - return nil, err - } - - return &r, nil -} - -func (exo *Client) AsyncToVirtualMachine(resp QueryAsyncJobResultResponse) (*DeployVirtualMachineResponse, error) { - var r DeployVirtualMachineWrappedResponse - - if err := json.Unmarshal(resp.Jobresult, &r); err != nil { - return nil, err - } - - return &r.Wrapped, nil -} diff --git a/vendor/github.com/exoscale/egoscale/async_jobs.go b/vendor/github.com/exoscale/egoscale/async_jobs.go new file mode 100644 index 000000000..906fd2f38 --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/async_jobs.go @@ -0,0 +1,66 @@ +package egoscale + +import ( + "encoding/json" +) + +// AsyncJobResult represents an asynchronous job result +type AsyncJobResult struct { + AccountID string `json:"accountid"` + Cmd string `json:"cmd"` + Created string `json:"created"` + JobInstanceID string `json:"jobinstanceid"` + JobInstanceType string `json:"jobinstancetype"` + JobProcStatus int `json:"jobprocstatus"` + JobResult *json.RawMessage `json:"jobresult"` + JobResultCode int `json:"jobresultcode"` + JobResultType string `json:"jobresulttype"` + JobStatus JobStatusType `json:"jobstatus"` + UserID string `json:"userid"` + JobID string `json:"jobid"` +} + +// QueryAsyncJobResult represents a query to fetch the status of async job +// +// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/queryAsyncJobResult.html +type QueryAsyncJobResult struct { + JobID string `json:"jobid"` +} + +func (*QueryAsyncJobResult) name() string { + return "queryAsyncJobResult" +} + +func (*QueryAsyncJobResult) response() interface{} { + return new(QueryAsyncJobResultResponse) +} + +// QueryAsyncJobResultResponse represents the current status of an asynchronous job +type QueryAsyncJobResultResponse AsyncJobResult + +// ListAsyncJobs list the asynchronous jobs +// +// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/listAsyncJobs.html +type ListAsyncJobs struct { + Account string `json:"account,omitempty"` + DomainID string `json:"domainid,omitempty"` + IsRecursive bool `json:"isrecursive,omitempty"` + Keyword string `json:"keyword,omitempty"` + Page int `json:"page,omitempty"` + PageSize int `json:"pagesize,omitempty"` + StartDate string `json:"startdate,omitempty"` +} + +func (*ListAsyncJobs) name() string { + return "listAsyncJobs" +} + +func (*ListAsyncJobs) response() interface{} { + return new(ListAsyncJobsResponse) +} + +// ListAsyncJobsResponse represents a list of job results +type ListAsyncJobsResponse struct { + Count int `json:"count"` + AsyncJobs []AsyncJobResult `json:"asyncjobs"` +} diff --git a/vendor/github.com/exoscale/egoscale/dns.go b/vendor/github.com/exoscale/egoscale/dns.go index 3a341bf92..11cb52051 100644 --- a/vendor/github.com/exoscale/egoscale/dns.go +++ b/vendor/github.com/exoscale/egoscale/dns.go @@ -2,59 +2,118 @@ package egoscale import ( "encoding/json" + "fmt" + "io/ioutil" "net/http" "strconv" + "strings" ) +// DNSDomain represents a domain +type DNSDomain struct { + ID int64 `json:"id"` + AccountID int64 `json:"account_id,omitempty"` + UserID int64 `json:"user_id,omitempty"` + RegistrantID int64 `json:"registrant_id,omitempty"` + Name string `json:"name"` + UnicodeName string `json:"unicode_name"` + Token string `json:"token"` + State string `json:"state"` + Language string `json:"language,omitempty"` + Lockable bool `json:"lockable"` + AutoRenew bool `json:"auto_renew"` + WhoisProtected bool `json:"whois_protected"` + RecordCount int64 `json:"record_count"` + ServiceCount int64 `json:"service_count"` + ExpiresOn string `json:"expires_on,omitempty"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` +} + +// DNSDomainResponse represents a domain creation response +type DNSDomainResponse struct { + Domain *DNSDomain `json:"domain"` +} + +// DNSRecord represents a DNS record +type DNSRecord struct { + ID int64 `json:"id,omitempty"` + DomainID int64 `json:"domain_id,omitempty"` + Name string `json:"name"` + TTL int `json:"ttl,omitempty"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + Content string `json:"content"` + RecordType string `json:"record_type"` + Prio int `json:"prio,omitempty"` +} + +// DNSRecordResponse represents the creation of a DNS record +type DNSRecordResponse struct { + Record DNSRecord `json:"record"` +} + +// DNSErrorResponse represents an error in the API +type DNSErrorResponse struct { + Message string `json:"message,omitempty"` + Errors *DNSError `json:"errors"` +} + +// DNSError represents an error +type DNSError struct { + Name []string `json:"name"` +} + +// Error formats the DNSerror into a string +func (req *DNSErrorResponse) Error() error { + if req.Errors != nil { + return fmt.Errorf("DNS error: %s", strings.Join(req.Errors.Name, ", ")) + } + return fmt.Errorf("DNS error: %s", req.Message) +} + +// CreateDomain creates a DNS domain func (exo *Client) CreateDomain(name string) (*DNSDomain, error) { - var hdr = make(http.Header) - var domain DNSDomainCreateRequest - - hdr.Add("X-DNS-TOKEN", exo.apiKey+":"+exo.apiSecret) - domain.Domain.Name = name - m, err := json.Marshal(domain) + m, err := json.Marshal(DNSDomainResponse{ + Domain: &DNSDomain{ + Name: name, + }, + }) if err != nil { return nil, err } - resp, err := exo.DetailedRequest("/v1/domains", string(m), "POST", hdr) + resp, err := exo.dnsRequest("/v1/domains", string(m), "POST") if err != nil { return nil, err } - var d *DNSDomain + var d *DNSDomainResponse if err := json.Unmarshal(resp, &d); err != nil { return nil, err } - return d, nil + return d.Domain, nil } +// GetDomain gets a DNS domain func (exo *Client) GetDomain(name string) (*DNSDomain, error) { - var hdr = make(http.Header) - - hdr.Add("X-DNS-TOKEN", exo.apiKey+":"+exo.apiSecret) - hdr.Add("Accept", "application/json") - - resp, err := exo.DetailedRequest("/v1/domains/"+name, "", "GET", hdr) + resp, err := exo.dnsRequest("/v1/domains/"+name, "", "GET") if err != nil { return nil, err } - var d *DNSDomain + var d *DNSDomainResponse if err := json.Unmarshal(resp, &d); err != nil { return nil, err } - return d, nil + return d.Domain, nil } +// DeleteDomain delets a DNS domain func (exo *Client) DeleteDomain(name string) error { - var hdr = make(http.Header) - hdr.Add("X-DNS-TOKEN", exo.apiKey+":"+exo.apiSecret) - hdr.Add("Accept", "application/json") - - _, err := exo.DetailedRequest("/v1/domains/"+name, "", "DELETE", hdr) + _, err := exo.dnsRequest("/v1/domains/"+name, "", "DELETE") if err != nil { return err } @@ -62,92 +121,128 @@ func (exo *Client) DeleteDomain(name string) error { return nil } -func (exo *Client) GetRecords(name string) ([]*DNSRecordResponse, error) { - var hdr = make(http.Header) - hdr.Add("X-DNS-TOKEN", exo.apiKey+":"+exo.apiSecret) - hdr.Add("Accept", "application/json") - - resp, err := exo.DetailedRequest("/v1/domains/"+name+"/records", "", "GET", hdr) +// GetRecord returns a DNS record +func (exo *Client) GetRecord(domain string, recordID int64) (*DNSRecord, error) { + id := strconv.FormatInt(recordID, 10) + resp, err := exo.dnsRequest("/v1/domains/"+domain+"/records/"+id, "", "GET") if err != nil { return nil, err } - var r []*DNSRecordResponse + var r DNSRecordResponse if err = json.Unmarshal(resp, &r); err != nil { return nil, err } - return r, nil + return &(r.Record), nil } -func (exo *Client) CreateRecord(name string, rec DNSRecord) (*DNSRecordResponse, error) { - var hdr = make(http.Header) - hdr.Add("X-DNS-TOKEN", exo.apiKey+":"+exo.apiSecret) - hdr.Add("Accept", "application/json") - hdr.Add("Content-Type", "application/json") - - var rr DNSRecordResponse - rr.Record = rec - - body, err := json.Marshal(rr) +// GetRecords returns the DNS records +func (exo *Client) GetRecords(name string) ([]DNSRecord, error) { + resp, err := exo.dnsRequest("/v1/domains/"+name+"/records", "", "GET") if err != nil { return nil, err } - resp, err := exo.DetailedRequest("/v1/domains/"+name+"/records", string(body), "POST", hdr) - if err != nil { - return nil, err - } - - var r *DNSRecordResponse + var r []DNSRecordResponse if err = json.Unmarshal(resp, &r); err != nil { return nil, err } - return r, nil + records := make([]DNSRecord, 0, len(r)) + for _, rec := range r { + records = append(records, rec.Record) + } + + return records, nil } -func (exo *Client) UpdateRecord(name string, rec DNSRecord) (*DNSRecordResponse, error) { - var hdr = make(http.Header) - id := strconv.FormatInt(rec.Id, 10) - hdr.Add("X-DNS-TOKEN", exo.apiKey+":"+exo.apiSecret) - hdr.Add("Accept", "application/json") - hdr.Add("Content-Type", "application/json") - - var rr DNSRecordResponse - rr.Record = rec - - body, err := json.Marshal(rr) +// CreateRecord creates a DNS record +func (exo *Client) CreateRecord(name string, rec DNSRecord) (*DNSRecord, error) { + body, err := json.Marshal(DNSRecordResponse{ + Record: rec, + }) if err != nil { return nil, err } - resp, err := exo.DetailedRequest("/v1/domains/"+name+"/records/"+id, - string(body), "PUT", hdr) + resp, err := exo.dnsRequest("/v1/domains/"+name+"/records", string(body), "POST") if err != nil { return nil, err } - var r *DNSRecordResponse + var r DNSRecordResponse if err = json.Unmarshal(resp, &r); err != nil { return nil, err } - return r, nil + return &(r.Record), nil } -func (exo *Client) DeleteRecord(name string, rec DNSRecord) error { - var hdr = make(http.Header) - id := strconv.FormatInt(rec.Id, 10) - hdr.Add("X-DNS-TOKEN", exo.apiKey+":"+exo.apiSecret) - hdr.Add("Accept", "application/json") - hdr.Add("Content-Type", "application/json") - - _, err := exo.DetailedRequest("/v1/domains/"+name+"/records/"+id, - "", "DELETE", hdr) +// UpdateRecord updates a DNS record +func (exo *Client) UpdateRecord(name string, rec DNSRecord) (*DNSRecord, error) { + body, err := json.Marshal(DNSRecordResponse{ + Record: rec, + }) if err != nil { - return err + return nil, err } - return nil + id := strconv.FormatInt(rec.ID, 10) + resp, err := exo.dnsRequest("/v1/domains/"+name+"/records/"+id, string(body), "PUT") + if err != nil { + return nil, err + } + + var r DNSRecordResponse + if err = json.Unmarshal(resp, &r); err != nil { + return nil, err + } + + return &(r.Record), nil +} + +// DeleteRecord deletes a record +func (exo *Client) DeleteRecord(name string, recordID int64) error { + id := strconv.FormatInt(recordID, 10) + _, err := exo.dnsRequest("/v1/domains/"+name+"/records/"+id, "", "DELETE") + + return err +} + +func (exo *Client) dnsRequest(uri string, params string, method string) (json.RawMessage, error) { + url := exo.endpoint + uri + req, err := http.NewRequest(method, url, strings.NewReader(params)) + if err != nil { + return nil, err + } + + var hdr = make(http.Header) + hdr.Add("X-DNS-TOKEN", exo.apiKey+":"+exo.apiSecret) + hdr.Add("Accept", "application/json") + if params != "" { + hdr.Add("Content-Type", "application/json") + } + req.Header = hdr + + response, err := exo.client.Do(req) + if err != nil { + return nil, err + } + + defer response.Body.Close() + b, err := ioutil.ReadAll(response.Body) + if err != nil { + return nil, err + } + + if response.StatusCode >= 400 { + var e DNSErrorResponse + if err := json.Unmarshal(b, &e); err != nil { + return nil, err + } + return nil, e.Error() + } + + return b, nil } diff --git a/vendor/github.com/exoscale/egoscale/doc.go b/vendor/github.com/exoscale/egoscale/doc.go new file mode 100644 index 000000000..e53861ba0 --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/doc.go @@ -0,0 +1,93 @@ +/* + +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. + +Requests and Responses + +The paradigm used in this library is that CloudStack defines two types of requests synchronous (client.Request) and asynchronous (client.AsyncRequest). And when the expected responses is a success message, you may use the boolean requests variants (client.BooleanRequest, client.BooleanAsyncRequest). To build a request, construct the adequate struct. This library expects a pointer for efficiency reasons only. The response is a struct corresponding to the request itself. E.g. DeployVirtualMachine gives DeployVirtualMachineResponse, as a pointer as well to avoid big copies. + +Then everything within the struct is not a pointer. + +Affinity and Anti-Affinity groups + +Affinity and Anti-Affinity groups provide a way to influence where VMs should run. See: http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/stable/virtual_machines.html#affinity-groups + +APIs + +All the available APIs on the server and provided by the API Discovery plugin + + cs := egoscale.NewClient("https://api.exoscale.ch/compute", "EXO...", "...") + + resp, err := cs.Request(&egoscale.ListAPIs{}) + if err != nil { + panic(err) + } + + for _, api := range resp.(*egoscale.ListAPIsResponse).API { + fmt.Println("%s %s", api.Name, api.Description) + } + // Output: + // listNetworks Lists all available networks + // ... + + +Elastic IPs + +See: http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/latest/networking_and_traffic.html#about-elastic-ips + +Networks + +See: http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/4.8/networking_and_traffic.html + +NICs + +See: http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/latest/networking_and_traffic.html#configuring-multiple-ip-addresses-on-a-single-nic + + +Security Groups + +Security Groups provide a way to isolate traffic to VMs. + + resp, err := cs.Request(&egoscale.CreateSecurityGroup{ + Name: "Load balancer", + Description: "Opens HTTP/HTTPS ports from the outside world", + }) + securityGroup := resp.(*egoscale.CreateSecurityGroupResponse).SecurityGroup + // ... + err = client.BooleanRequest(&egoscale.DeleteSecurityGroup{ + ID: securityGroup.ID, + }) + // ... + +See: http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/stable/networking_and_traffic.html#security-groups + +Service Offerings + +A service offering correspond to some hardware features (CPU, RAM). + +See: http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/latest/service_offerings.html + +SSH Key Pairs + +In addition to username and password (disabled on Exoscale), SSH keys are used to log into the infrastructure. + +See: http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/stable/virtual_machines.html#creating-the-ssh-keypair + +Virtual Machines + +... todo ... + +See: http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/stable/virtual_machines.html + +Templates + +... todo ... + +See: http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/latest/templates.html + +Zones + +A Zone corresponds to a Data Center. + +*/ +package egoscale diff --git a/vendor/github.com/exoscale/egoscale/error.go b/vendor/github.com/exoscale/egoscale/error.go deleted file mode 100644 index 3e7d2c77a..000000000 --- a/vendor/github.com/exoscale/egoscale/error.go +++ /dev/null @@ -1,9 +0,0 @@ -package egoscale - -import ( - "fmt" -) - -func (e *Error) Error() error { - return fmt.Errorf("exoscale API error %d (internal code: %d): %s", e.ErrorCode, e.CSErrorCode, e.ErrorText) -} diff --git a/vendor/github.com/exoscale/egoscale/events.go b/vendor/github.com/exoscale/egoscale/events.go new file mode 100644 index 000000000..3eaaf48da --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/events.go @@ -0,0 +1,77 @@ +package egoscale + +// Event represents an event in the system +type Event struct { + ID string `json:"id"` + Account string `json:"account"` + Created string `json:"created"` + Description string `json:"description,omitempty"` + Domain string `json:"domain,omitempty"` + DomainID string `json:"domainid,omitempty"` + Level string `json:"level"` // INFO, WARN, ERROR + ParentID string `json:"parentid,omitempty"` + Project string `json:"project,omitempty"` + ProjectID string `json:"projectid,omitempty"` + State string `json:"state,omitempty"` + Type string `json:"type"` + UserName string `json:"username,omitempty"` +} + +// EventType represent a type of event +type EventType struct { + Name string `json:"name"` +} + +// ListEvents list the events +// +// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/listEvents.html +type ListEvents struct { + Account string `json:"account,omitempty"` + DomainID string `json:"domainid,omitempty"` + Duration int `json:"duration,omitempty"` + EndDate string `json:"enddate,omitempty"` + EntryTime int `json:"entrytime,omitempty"` + ID string `json:"id,omitempty"` + IsRecursive bool `json:"isrecursive,omitempty"` + Keyword string `json:"keyword,omitempty"` + Level string `json:"level,omitempty"` // INFO, WARN, ERROR + ListAll bool `json:"listall,omitempty"` + Page int `json:"page,omitempty"` + PageSize int `json:"pagesize,omitempty"` + ProjectID string `json:"projectid,omitempty"` + StartDate string `json:"startdate,omitempty"` + Type string `json:"type,omitempty"` +} + +func (*ListEvents) name() string { + return "listEvents" +} + +func (*ListEvents) response() interface{} { + return new(ListEventsResponse) +} + +// ListEventsResponse represents a response of a list query +type ListEventsResponse struct { + Count int `json:"count"` + Event []Event `json:"event"` +} + +// ListEventTypes list the event types +// +// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/listEventTypes.html +type ListEventTypes struct{} + +func (*ListEventTypes) name() string { + return "listEventTypes" +} + +func (*ListEventTypes) response() interface{} { + return new(ListEventTypesResponse) +} + +// ListEventTypesResponse represents a response of a list query +type ListEventTypesResponse struct { + Count int `json:"count"` + EventType []EventType `json:"eventtype"` +} diff --git a/vendor/github.com/exoscale/egoscale/groups.go b/vendor/github.com/exoscale/egoscale/groups.go deleted file mode 100644 index 278277264..000000000 --- a/vendor/github.com/exoscale/egoscale/groups.go +++ /dev/null @@ -1,144 +0,0 @@ -package egoscale - -import ( - "encoding/json" - "fmt" - "net/url" -) - -func (exo *Client) CreateEgressRule(rule SecurityGroupRule) (*AuthorizeSecurityGroupEgressResponse, error) { - - params := url.Values{} - params.Set("securitygroupid", rule.SecurityGroupId) - - if rule.Cidr != "" { - params.Set("cidrlist", rule.Cidr) - } else if len(rule.UserSecurityGroupList) > 0 { - usg, err := json.Marshal(rule.UserSecurityGroupList) - if err != nil { - return nil, err - } - params.Set("usersecuritygrouplist", string(usg)) - } else { - return nil, fmt.Errorf("No Egress rule CIDR or Security Group List provided") - } - - params.Set("protocol", rule.Protocol) - - if rule.Protocol == "ICMP" { - params.Set("icmpcode", fmt.Sprintf("%d", rule.IcmpCode)) - params.Set("icmptype", fmt.Sprintf("%d", rule.IcmpType)) - } else if rule.Protocol == "TCP" || rule.Protocol == "UDP" { - params.Set("startport", fmt.Sprintf("%d", rule.Port)) - params.Set("endport", fmt.Sprintf("%d", rule.Port)) - } else { - return nil, fmt.Errorf("Invalid Egress rule Protocol: %s", rule.Protocol) - } - - resp, err := exo.Request("authorizeSecurityGroupEgress", params) - if err != nil { - return nil, err - } - - var r AuthorizeSecurityGroupEgressResponse - if err := json.Unmarshal(resp, &r); err != nil { - return nil, err - } - - return &r, nil -} - -func (exo *Client) CreateIngressRule(rule SecurityGroupRule) (*AuthorizeSecurityGroupIngressResponse, error) { - - params := url.Values{} - params.Set("securitygroupid", rule.SecurityGroupId) - - if rule.Cidr != "" { - params.Set("cidrlist", rule.Cidr) - } else if len(rule.UserSecurityGroupList) > 0 { - for i := 0; i < len(rule.UserSecurityGroupList); i++ { - params.Set(fmt.Sprintf("usersecuritygrouplist[%d].account", i), rule.UserSecurityGroupList[i].Account) - params.Set(fmt.Sprintf("usersecuritygrouplist[%d].group", i), rule.UserSecurityGroupList[i].Group) - - } - } else { - return nil, fmt.Errorf("No Ingress rule CIDR or Security Group List provided") - } - - params.Set("protocol", rule.Protocol) - - if rule.Protocol == "ICMP" { - params.Set("icmpcode", fmt.Sprintf("%d", rule.IcmpCode)) - params.Set("icmptype", fmt.Sprintf("%d", rule.IcmpType)) - } else if rule.Protocol == "TCP" || rule.Protocol == "UDP" { - params.Set("startport", fmt.Sprintf("%d", rule.Port)) - params.Set("endport", fmt.Sprintf("%d", rule.Port)) - } else { - return nil, fmt.Errorf("Invalid Egress rule Protocol: %s", rule.Protocol) - } - - fmt.Printf("## params: %+v\n", params) - - resp, err := exo.Request("authorizeSecurityGroupIngress", params) - - if err != nil { - return nil, err - } - - var r AuthorizeSecurityGroupIngressResponse - if err := json.Unmarshal(resp, &r); err != nil { - return nil, err - } - - return &r, nil -} - -func (exo *Client) CreateSecurityGroupWithRules(name string, ingress []SecurityGroupRule, egress []SecurityGroupRule) (*CreateSecurityGroupResponse, error) { - - params := url.Values{} - params.Set("name", name) - - resp, err := exo.Request("createSecurityGroup", params) - - var r CreateSecurityGroupResponseWrapper - if err := json.Unmarshal(resp, &r); err != nil { - return nil, err - } - - if err != nil { - return nil, err - } - - sgid := r.Wrapped.Id - - for _, erule := range egress { - erule.SecurityGroupId = sgid - _, err = exo.CreateEgressRule(erule) - if err != nil { - return nil, err - } - } - - for _, inrule := range ingress { - inrule.SecurityGroupId = sgid - _, err = exo.CreateIngressRule(inrule) - if err != nil { - return nil, err - } - } - - return &r.Wrapped, nil -} - -func (exo *Client) DeleteSecurityGroup(name string) error { - params := url.Values{} - params.Set("name", name) - - resp, err := exo.Request("deleteSecurityGroup", params) - if err != nil { - return err - } - - fmt.Printf("## response: %+v\n", resp) - return nil -} diff --git a/vendor/github.com/exoscale/egoscale/init.go b/vendor/github.com/exoscale/egoscale/init.go index 77b46cf96..b0814cdeb 100644 --- a/vendor/github.com/exoscale/egoscale/init.go +++ b/vendor/github.com/exoscale/egoscale/init.go @@ -5,6 +5,7 @@ import ( "net/http" ) +// NewClient creates a CloudStack API client func NewClient(endpoint string, apiKey string, apiSecret string) *Client { cs := &Client{ client: &http.Client{ diff --git a/vendor/github.com/exoscale/egoscale/ip.go b/vendor/github.com/exoscale/egoscale/ip.go deleted file mode 100644 index 3c84d4ccc..000000000 --- a/vendor/github.com/exoscale/egoscale/ip.go +++ /dev/null @@ -1,40 +0,0 @@ -package egoscale - -import ( - "encoding/json" - "net/url" -) - -func (exo *Client) AddIpToNic(nic_id string, ip_address string) (string, error) { - params := url.Values{} - params.Set("nicid", nic_id) - params.Set("ipaddress", ip_address) - - resp, err := exo.Request("addIpToNic", params) - if err != nil { - return "", err - } - - var r AddIpToNicResponse - if err := json.Unmarshal(resp, &r); err != nil { - return "", err - } - - return r.Id, nil -} - -func (exo *Client) RemoveIpFromNic(nic_id string) (string, error) { - params := url.Values{} - params.Set("id", nic_id) - - resp, err := exo.Request("removeIpFromNic", params) - if err != nil { - return "", err - } - - var r RemoveIpFromNicResponse - if err := json.Unmarshal(resp, &r); err != nil { - return "", err - } - return r.JobID, nil -} diff --git a/vendor/github.com/exoscale/egoscale/keypair.go b/vendor/github.com/exoscale/egoscale/keypair.go deleted file mode 100644 index 0b16b49c4..000000000 --- a/vendor/github.com/exoscale/egoscale/keypair.go +++ /dev/null @@ -1,59 +0,0 @@ -package egoscale - -import ( - "encoding/json" - "net/url" -) - -func (exo *Client) CreateKeypair(name string) (*CreateSSHKeyPairResponse, error) { - params := url.Values{} - params.Set("name", name) - - resp, err := exo.Request("createSSHKeyPair", params) - if err != nil { - return nil, err - } - - var r CreateSSHKeyPairWrappedResponse - if err := json.Unmarshal(resp, &r); err != nil { - return nil, err - } - - return &r.Wrapped, nil -} - -func (exo *Client) DeleteKeypair(name string) (*StandardResponse, error) { - params := url.Values{} - params.Set("name", name) - - resp, err := exo.Request("deleteSSHKeyPair", params) - if err != nil { - return nil, err - } - - var r StandardResponse - if err := json.Unmarshal(resp, &r); err != nil { - return nil, err - } - - return &r, nil - -} - -func (exo *Client) RegisterKeypair(name string, key string) (*CreateSSHKeyPairResponse, error) { - params := url.Values{} - params.Set("name", name) - params.Set("publicKey", key) - - resp, err := exo.Request("registerSSHKeyPair", params) - if err != nil { - return nil, err - } - - var r CreateSSHKeyPairWrappedResponse - if err := json.Unmarshal(resp, &r); err != nil { - return nil, err - } - - return &r.Wrapped, nil -} diff --git a/vendor/github.com/exoscale/egoscale/keypairs.go b/vendor/github.com/exoscale/egoscale/keypairs.go new file mode 100644 index 000000000..63f3f9b61 --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/keypairs.go @@ -0,0 +1,171 @@ +package egoscale + +// SSHKeyPair represents an SSH key pair +type SSHKeyPair struct { + Account string `json:"account,omitempty"` + DomainID string `json:"domainid,omitempty"` + ProjectID string `json:"projectid,omitempty"` + Fingerprint string `json:"fingerprint,omitempty"` + Name string `json:"name,omitempty"` + PrivateKey string `json:"privatekey,omitempty"` +} + +// CreateSSHKeyPair represents a new keypair to be created +// +// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/createSSHKeyPair.html +type CreateSSHKeyPair struct { + Name string `json:"name"` + Account string `json:"account,omitempty"` + DomainID string `json:"domainid,omitempty"` + ProjectID string `json:"projectid,omitempty"` +} + +func (*CreateSSHKeyPair) name() string { + return "createSSHKeyPair" +} + +func (*CreateSSHKeyPair) response() interface{} { + return new(CreateSSHKeyPairResponse) +} + +// CreateSSHKeyPairResponse represents the creation of an SSH Key Pair +type CreateSSHKeyPairResponse struct { + KeyPair SSHKeyPair `json:"keypair"` +} + +// DeleteSSHKeyPair represents a new keypair to be created +// +// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/deleteSSHKeyPair.html +type DeleteSSHKeyPair struct { + Name string `json:"name"` + Account string `json:"account,omitempty"` + DomainID string `json:"domainid,omitempty"` + ProjectID string `json:"projectid,omitempty"` +} + +func (*DeleteSSHKeyPair) name() string { + return "deleteSSHKeyPair" +} + +func (*DeleteSSHKeyPair) response() interface{} { + return new(booleanSyncResponse) +} + +// RegisterSSHKeyPair represents a new registration of a public key in a keypair +// +// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/registerSSHKeyPair.html +type RegisterSSHKeyPair struct { + Name string `json:"name"` + PublicKey string `json:"publickey"` + Account string `json:"account,omitempty"` + DomainID string `json:"domainid,omitempty"` + ProjectID string `json:"projectid,omitempty"` +} + +func (*RegisterSSHKeyPair) name() string { + return "registerSSHKeyPair" +} + +func (*RegisterSSHKeyPair) response() interface{} { + return new(RegisterSSHKeyPairResponse) +} + +// RegisterSSHKeyPairResponse represents the creation of an SSH Key Pair +type RegisterSSHKeyPairResponse struct { + KeyPair SSHKeyPair `json:"keypair"` +} + +// ListSSHKeyPairs represents a query for a list of SSH KeyPairs +// +// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/listSSHKeyPairs.html +type ListSSHKeyPairs struct { + Account string `json:"account,omitempty"` + DomainID string `json:"domainid,omitempty"` + Fingerprint string `json:"fingerprint,omitempty"` + IsRecursive bool `json:"isrecursive,omitempty"` + Keyword string `json:"keyword,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"` +} + +func (*ListSSHKeyPairs) name() string { + return "listSSHKeyPairs" +} + +func (*ListSSHKeyPairs) response() interface{} { + return new(ListSSHKeyPairsResponse) +} + +// ListSSHKeyPairsResponse represents a list of SSH key pairs +type ListSSHKeyPairsResponse struct { + Count int `json:"count"` + SSHKeyPair []SSHKeyPair `json:"sshkeypair"` +} + +// ResetSSHKeyForVirtualMachine (Async) represents a change for the key pairs +// +// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/resetSSHKeyForVirtualMachine.html +type ResetSSHKeyForVirtualMachine struct { + ID string `json:"id"` + KeyPair string `json:"keypair"` + Account string `json:"account,omitempty"` + DomainID string `json:"domainid,omitempty"` + ProjectID string `json:"projectid,omitempty"` +} + +func (*ResetSSHKeyForVirtualMachine) name() string { + return "resetSSHKeyForVirtualMachine" +} + +func (*ResetSSHKeyForVirtualMachine) asyncResponse() interface{} { + return new(ResetSSHKeyForVirtualMachineResponse) +} + +// ResetSSHKeyForVirtualMachineResponse represents the modified VirtualMachine +type ResetSSHKeyForVirtualMachineResponse VirtualMachineResponse + +// CreateKeypair create a new SSH Key Pair +// +// Deprecated: will go away, use the API directly +func (exo *Client) CreateKeypair(name string) (*SSHKeyPair, error) { + req := &CreateSSHKeyPair{ + Name: name, + } + resp, err := exo.Request(req) + if err != nil { + return nil, err + } + + keypair := resp.(*CreateSSHKeyPairResponse).KeyPair + return &keypair, nil +} + +// DeleteKeypair deletes an SSH key pair +// +// Deprecated: will go away, use the API directly +func (exo *Client) DeleteKeypair(name string) error { + req := &DeleteSSHKeyPair{ + Name: name, + } + return exo.BooleanRequest(req) +} + +// RegisterKeypair registers a public key in a keypair +// +// Deprecated: will go away, use the API directly +func (exo *Client) RegisterKeypair(name string, publicKey string) (*SSHKeyPair, error) { + req := &RegisterSSHKeyPair{ + Name: name, + PublicKey: publicKey, + } + resp, err := exo.Request(req) + if err != nil { + return nil, err + } + + keypair := resp.(*RegisterSSHKeyPairResponse).KeyPair + return &keypair, nil +} diff --git a/vendor/github.com/exoscale/egoscale/limits.go b/vendor/github.com/exoscale/egoscale/limits.go new file mode 100644 index 000000000..8c788f6a5 --- /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/network_offerings.go b/vendor/github.com/exoscale/egoscale/network_offerings.go new file mode 100644 index 000000000..1fd0a0265 --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/network_offerings.go @@ -0,0 +1,68 @@ +package egoscale + +// NetworkOffering corresponds to the Compute Offerings +type NetworkOffering struct { + ID string `json:"id"` + Availability string `json:"availability,omitempty"` + ConserveMode bool `json:"conservemode,omitempty"` + Created string `json:"created"` + Details map[string]string `json:"details,omitempty"` + DisplayText string `json:"displaytext,omitempty"` + EgressDefaultPolicy bool `json:"egressdefaultpolicy,omitempty"` + ForVPC bool `json:"forvpc,omitempty"` + GuestIPType string `json:"guestiptype,omitempty"` + IsDefault bool `json:"isdefault,omitempty"` + IsPersistent bool `json:"ispersistent,omitempty"` + MaxConnections int `json:"maxconnections,omitempty"` + Name string `json:"name,omitempty"` + NetworkRate int `json:"networkrate,omitempty"` + ServiceOfferingID string `json:"serviceofferingid,omitempty"` + SpecifyIPRanges bool `json:"specifyipranges,omitempty"` + SpecifyVlan bool `json:"specifyvlan,omitempty"` + State string `json:"state"` // Disabled/Enabled/Inactive + SupportsPublicAccess bool `json:"supportspublicaccess,omitempty"` + SupportsStrechedL2Subnet bool `json:"supportsstrechedl2subnet,omitempty"` + Tags []ResourceTag `json:"tags,omitempty"` + TrafficType string `json:"traffictype,omitempty"` // Public, Management, Control, ... + Service []Service `json:"service,omitempty"` +} + +// ListNetworkOfferings represents a query for network offerings +// +// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/listNetworkOfferings.html +type ListNetworkOfferings struct { + Availability string `json:"availability,omitempty"` + DisplayText string `json:"displaytext,omitempty"` + ForVPC bool `json:"forvpc,omitempty"` + GuestIPType string `json:"guestiptype,omitempty"` // shared of isolated + ID string `json:"id,omitempty"` + IsDefault bool `json:"isdefault,omitempty"` + IsTagged bool `json:"istagged,omitempty"` + Keyword string `json:"keyword,omitempty"` + Name string `json:"name,omitempty"` + NetworkID string `json:"networkid,omitempty"` + Page int `json:"page,omitempty"` + PageSize int `json:"pagesize,omitempty"` + SourceNATSupported bool `json:"sourcenatsupported,omitempty"` + SpecifyIPRanges bool `json:"specifyipranges,omitempty"` + SpecifyVlan string `json:"specifyvlan,omitempty"` + State string `json:"state,omitempty"` + SupportedServices string `json:"supportedservices,omitempty"` + Tags []ResourceTag `json:"tags,omitempty"` + TrafficType string `json:"traffictype,omitempty"` + ZoneID string `json:"zoneid,omitempty"` +} + +func (*ListNetworkOfferings) name() string { + return "listNetworkOfferings" +} + +func (*ListNetworkOfferings) response() interface{} { + return new(ListNetworkOfferingsResponse) +} + +// ListNetworkOfferingsResponse represents a list of service offerings +type ListNetworkOfferingsResponse struct { + Count int `json:"count"` + NetworkOffering []NetworkOffering `json:"networkoffering"` +} diff --git a/vendor/github.com/exoscale/egoscale/networks.go b/vendor/github.com/exoscale/egoscale/networks.go new file mode 100644 index 000000000..2c4c8f0e8 --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/networks.go @@ -0,0 +1,249 @@ +package egoscale + +import ( + "net" + "net/url" +) + +// Network represents a network +type Network struct { + ID string `json:"id"` + Account string `json:"account"` + ACLID string `json:"aclid,omitempty"` + ACLType string `json:"acltype,omitempty"` + BroadcastDomainType string `json:"broadcastdomaintype,omitempty"` + BroadcastURI string `json:"broadcasturi,omitempty"` + CanUseForDeploy bool `json:"canusefordeploy,omitempty"` + Cidr string `json:"cidr,omitempty"` + DisplayNetwork bool `json:"diplaynetwork,omitempty"` + DisplayText string `json:"displaytext"` + DNS1 net.IP `json:"dns1,omitempty"` + DNS2 net.IP `json:"dns2,omitempty"` + Domain string `json:"domain,omitempty"` + DomainID string `json:"domainid,omitempty"` + Gateway net.IP `json:"gateway,omitempty"` + IP6Cidr string `json:"ip6cidr,omitempty"` + IP6Gateway net.IP `json:"ip6gateway,omitempty"` + IsDefault bool `json:"isdefault,omitempty"` + IsPersistent bool `json:"ispersistent,omitempty"` + Name string `json:"name"` + Netmask net.IP `json:"netmask,omitempty"` + NetworkCidr string `json:"networkcidr,omitempty"` + NetworkDomain string `json:"networkdomain,omitempty"` + NetworkOfferingAvailability string `json:"networkofferingavailability,omitempty"` + NetworkOfferingConserveMode bool `json:"networkofferingconservemode,omitempty"` + NetworkOfferingDisplayText string `json:"networkofferingdisplaytext,omitempty"` + NetworkOfferingID string `json:"networkofferingid,omitempty"` + NetworkOfferingName string `json:"networkofferingname,omitempty"` + PhysicalNetworkID string `json:"physicalnetworkid,omitempty"` + Project string `json:"project,omitempty"` + ProjectID string `json:"projectid,omitempty"` + Related string `json:"related,omitempty"` + ReserveIPRange string `json:"reserveiprange,omitempty"` + RestartRequired bool `json:"restartrequired,omitempty"` + SpecifyIPRanges bool `json:"specifyipranges,omitempty"` + State string `json:"state"` + StrechedL2Subnet bool `json:"strechedl2subnet,omitempty"` + SubdomainAccess bool `json:"subdomainaccess,omitempty"` + TrafficType string `json:"traffictype"` + Type string `json:"type"` + Vlan string `json:"vlan,omitemtpy"` // root only + VpcID string `json:"vpcid,omitempty"` + ZoneID string `json:"zoneid,omitempty"` + ZoneName string `json:"zonename,omitempty"` + ZonesNetworkSpans string `json:"zonesnetworkspans,omitempty"` + Service []Service `json:"service"` + 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"` + Capability []ServiceCapability `json:"capability,omitempty"` + Provider []ServiceProvider `json:"provider,omitempty"` +} + +// ServiceCapability represents optional capability of a service +type ServiceCapability struct { + CanChooseServiceCapability bool `json:"canchooseservicecapability"` + Name string `json:"name"` + Value string `json:"value"` +} + +// ServiceProvider represents the provider of the service +type ServiceProvider struct { + ID string `json:"id"` + CanEnableIndividualService bool `json:"canenableindividualservice"` + DestinationPhysicalNetworkID string `json:"destinationphysicalnetworkid"` + Name string `json:"name"` + PhysicalNetworkID string `json:"physicalnetworkid"` + ServiceList []string `json:"servicelist,omitempty"` +} + +// NetworkResponse represents a network +type NetworkResponse struct { + Network Network `json:"network"` +} + +// CreateNetwork creates a network +// +// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/createNetwork.html +type CreateNetwork struct { + DisplayText string `json:"displaytext,omitempty"` + Name string `json:"name,omitempty"` + NetworkOfferingID string `json:"networkofferingid"` + ZoneID string `json:"zoneid"` + 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 + DomainID string `json:"domainid,omitempty"` + EndIP net.IP `json:"endip,omitempty"` + EndIpv6 net.IP `json:"endipv6,omitempty"` + Gateway net.IP `json:"gateway,omitempty"` + IP6Cidr string `json:"ip6cidr,omitempty"` + IP6Gateway net.IP `json:"ip6gateway,omitempty"` + IsolatedPVlan string `json:"isolatedpvlan,omitempty"` + Netmask net.IP `json:"netmask,omitempty"` + NetworkDomain string `json:"networkdomain,omitempty"` + PhysicalNetworkID string `json:"physicalnetworkid,omitempty"` + ProjectID string `json:"projectid,omitempty"` + StartIP net.IP `json:"startip,omitempty"` + StartIpv6 net.IP `json:"startipv6,omitempty"` + SubdomainAccess string `json:"subdomainaccess,omitempty"` + Vlan string `json:"vlan,omitempty"` + VpcID string `json:"vpcid,omitempty"` +} + +func (*CreateNetwork) name() string { + return "createNetwork" +} + +func (*CreateNetwork) response() interface{} { + return new(CreateNetworkResponse) +} + +func (req *CreateNetwork) onBeforeSend(params *url.Values) error { + // Those fields are required but might be empty + if req.Name == "" { + params.Set("name", "") + } + if req.DisplayText == "" { + params.Set("displaytext", "") + } + return nil +} + +// CreateNetworkResponse represents a freshly created network +type CreateNetworkResponse NetworkResponse + +// UpdateNetwork updates a network +// +// 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"` + CustomID string `json:"customid,omitempty"` // root only + DisplayNetwork string `json:"displaynetwork,omitempty"` + DisplayText string `json:"displaytext,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"` +} + +func (*UpdateNetwork) name() string { + return "updateNetwork" +} + +func (*UpdateNetwork) asyncResponse() interface{} { + return new(UpdateNetworkResponse) +} + +// UpdateNetworkResponse represents a freshly created network +type UpdateNetworkResponse NetworkResponse + +// RestartNetwork updates a network +// +// 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"` +} + +func (*RestartNetwork) name() string { + return "restartNetwork" +} + +func (*RestartNetwork) asyncResponse() interface{} { + return new(RestartNetworkResponse) +} + +// RestartNetworkResponse represents a freshly created network +type RestartNetworkResponse NetworkResponse + +// DeleteNetwork deletes a network +// +// 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"` +} + +func (*DeleteNetwork) name() string { + return "deleteNetwork" +} + +func (*DeleteNetwork) asyncResponse() interface{} { + return new(booleanAsyncResponse) +} + +// ListNetworks represents a query to a network +// +// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/listNetworks.html +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 + 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"` + Keyword string `json:"keyword,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"` + SupportedServices []Service `json:"supportedservices,omitempty"` + Tags []ResourceTag `json:"resourcetag,omitempty"` + TrafficType string `json:"traffictype,omitempty"` + Type string `json:"type,omitempty"` + VpcID string `json:"vpcid,omitempty"` + ZoneID string `json:"zoneid,omitempty"` +} + +func (*ListNetworks) name() string { + return "listNetworks" +} + +func (*ListNetworks) response() interface{} { + return new(ListNetworksResponse) +} + +// ListNetworksResponse represents the list of networks +type ListNetworksResponse struct { + Count int `json:"count"` + Network []Network `json:"network"` +} diff --git a/vendor/github.com/exoscale/egoscale/nics.go b/vendor/github.com/exoscale/egoscale/nics.go new file mode 100644 index 000000000..b8b405b7f --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/nics.go @@ -0,0 +1,135 @@ +package egoscale + +import ( + "fmt" + "net" +) + +// Nic represents a Network Interface Controller (NIC) +type Nic struct { + ID string `json:"id,omitempty"` + BroadcastURI string `json:"broadcasturi,omitempty"` + Gateway net.IP `json:"gateway,omitempty"` + IP6Address net.IP `json:"ip6address,omitempty"` + IP6Cidr string `json:"ip6cidr,omitempty"` + IP6Gateway net.IP `json:"ip6gateway,omitempty"` + IPAddress net.IP `json:"ipaddress,omitempty"` + IsDefault bool `json:"isdefault,omitempty"` + IsolationURI string `json:"isolationuri,omitempty"` + MacAddress string `json:"macaddress,omitempty"` + Netmask net.IP `json:"netmask,omitempty"` + NetworkID string `json:"networkid,omitempty"` + NetworkName string `json:"networkname,omitempty"` + SecondaryIP []NicSecondaryIP `json:"secondaryip,omitempty"` + Traffictype string `json:"traffictype,omitempty"` + Type string `json:"type,omitempty"` + VirtualMachineID string `json:"virtualmachineid,omitempty"` +} + +// NicSecondaryIP represents a link between NicID and IPAddress +type NicSecondaryIP struct { + ID string `json:"id"` + IPAddress net.IP `json:"ipaddress"` + NetworkID string `json:"networkid"` + NicID string `json:"nicid"` + VirtualMachineID string `json:"virtualmachineid,omitempty"` +} + +// ListNics represents the NIC search +type ListNics struct { + VirtualMachineID string `json:"virtualmachineid"` + ForDisplay bool `json:"fordisplay,omitempty"` + Keyword string `json:"keyword,omitempty"` + NetworkID string `json:"networkid,omitempty"` + NicID string `json:"nicid,omitempty"` + Page int `json:"page,omitempty"` + PageSize int `json:"pagesize,omitempty"` +} + +func (*ListNics) name() string { + return "listNics" +} + +func (*ListNics) response() interface{} { + return new(ListNicsResponse) +} + +// ListNicsResponse represents a list of templates +type ListNicsResponse struct { + Count int `json:"count"` + Nic []Nic `json:"nic"` +} + +// AddIPToNic represents the assignation of a secondary IP +type AddIPToNic struct { + NicID string `json:"nicid"` + IPAddress net.IP `json:"ipaddress"` +} + +func (*AddIPToNic) name() string { + return "addIpToNic" +} +func (*AddIPToNic) asyncResponse() interface{} { + return new(AddIPToNicResponse) +} + +// AddIPToNicResponse represents the addition of an IP to a NIC +type AddIPToNicResponse struct { + NicSecondaryIP NicSecondaryIP `json:"nicsecondaryip"` +} + +// RemoveIPFromNic represents a deletion request +type RemoveIPFromNic struct { + ID string `json:"id"` +} + +func (*RemoveIPFromNic) name() string { + return "removeIpFromNic" +} + +func (*RemoveIPFromNic) asyncResponse() interface{} { + return new(booleanAsyncResponse) +} + +// ListNics lists the NIC of a VM +// +// Deprecated: use the API directly +func (exo *Client) ListNics(req *ListNics) ([]Nic, error) { + resp, err := exo.Request(req) + if err != nil { + return nil, err + } + + return resp.(*ListNicsResponse).Nic, nil +} + +// AddIPToNic adds an IP to a NIC +// +// Deprecated: use the API directly +func (exo *Client) AddIPToNic(nicID string, ipAddress string, async AsyncInfo) (*NicSecondaryIP, error) { + ip := net.ParseIP(ipAddress) + if ip == nil { + return nil, fmt.Errorf("%s is not a valid IP address", ipAddress) + } + req := &AddIPToNic{ + NicID: nicID, + IPAddress: ip, + } + resp, err := exo.AsyncRequest(req, async) + if err != nil { + return nil, err + } + + nic := resp.(AddIPToNicResponse).NicSecondaryIP + return &nic, nil +} + +// RemoveIPFromNic removes an IP from a NIC +// +// Deprecated: use the API directly +func (exo *Client) RemoveIPFromNic(secondaryNicID string, async AsyncInfo) error { + req := &RemoveIPFromNic{ + ID: secondaryNicID, + } + return exo.BooleanAsyncRequest(req, async) +} diff --git a/vendor/github.com/exoscale/egoscale/request.go b/vendor/github.com/exoscale/egoscale/request.go index fe08da754..641896552 100644 --- a/vendor/github.com/exoscale/egoscale/request.go +++ b/vendor/github.com/exoscale/egoscale/request.go @@ -1,20 +1,164 @@ package egoscale import ( + "bytes" "crypto/hmac" "crypto/sha1" "encoding/base64" "encoding/json" "fmt" "io/ioutil" + "log" + "net" "net/http" "net/url" + "reflect" "sort" + "strconv" "strings" + "time" ) +// Command represent a CloudStack request +type Command interface { + // CloudStack API command name + name() string + // Response interface to Unmarshal the JSON into + response() interface{} +} + +// AsyncCommand represents a async CloudStack request +type AsyncCommand interface { + // CloudStack API command name + name() string + // Response interface to Unmarshal the JSON into + asyncResponse() interface{} +} + +// Command represents an action to be done on the params before sending them +// +// This little took helps with issue of relying on JSON serialization logic only. +// `omitempty` may make sense in some cases but not all the time. +type onBeforeHook interface { + onBeforeSend(params *url.Values) error +} + +const ( + // Pending represents a job in progress + Pending JobStatusType = iota + // Success represents a successfully completed job + Success + // Failure represents a job that has failed to complete + Failure +) + +// JobStatusType represents the status of a Job +type JobStatusType int + +const ( + // Unauthorized represents ... (TODO) + Unauthorized ErrorCode = 401 + // MethodNotAllowed represents ... (TODO) + MethodNotAllowed = 405 + // UnsupportedActionError represents ... (TODO) + UnsupportedActionError = 422 + // APILimitExceeded represents ... (TODO) + APILimitExceeded = 429 + // MalformedParameterError represents ... (TODO) + MalformedParameterError = 430 + // ParamError represents ... (TODO) + ParamError = 431 + + // InternalError represents a server error + InternalError = 530 + // AccountError represents ... (TODO) + AccountError = 531 + // AccountResourceLimitError represents ... (TODO) + AccountResourceLimitError = 532 + // InsufficientCapacityError represents ... (TODO) + InsufficientCapacityError = 533 + // ResourceUnavailableError represents ... (TODO) + ResourceUnavailableError = 534 + // ResourceAllocationError represents ... (TODO) + ResourceAllocationError = 535 + // ResourceInUseError represents ... (TODO) + ResourceInUseError = 536 + // NetworkRuleConflictError represents ... (TODO) + NetworkRuleConflictError = 537 +) + +// ErrorCode represents the CloudStack ApiErrorCode enum +// +// See: https://github.com/apache/cloudstack/blob/master/api/src/org/apache/cloudstack/api/ApiErrorCode.java +type ErrorCode int + +// JobResultResponse represents a generic response to a job task +type JobResultResponse struct { + AccountID string `json:"accountid,omitempty"` + Cmd string `json:"cmd"` + Created string `json:"created"` + JobID string `json:"jobid"` + JobProcStatus int `json:"jobprocstatus"` + JobResult *json.RawMessage `json:"jobresult"` + JobStatus JobStatusType `json:"jobstatus"` + JobResultType string `json:"jobresulttype"` + UserID string `json:"userid,omitempty"` +} + +// 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 +} + +// Error formats a CloudStack error into a standard error +func (e *ErrorResponse) Error() string { + return fmt.Sprintf("API error %d (internal code: %d): %s", e.ErrorCode, e.CsErrorCode, e.ErrorText) +} + +// booleanAsyncResponse represents a boolean response (usually after a deletion) +type booleanAsyncResponse struct { + Success bool `json:"success"` + DisplayText string `json:"diplaytext,omitempty"` +} + +// Error formats a CloudStack job response into a standard error +func (e *booleanAsyncResponse) Error() error { + if e.Success { + return nil + } + return fmt.Errorf("API error: %s", e.DisplayText) +} + +// booleanAsyncResponse represents a boolean response for sync calls +type booleanSyncResponse struct { + Success string `json:"success"` + DisplayText string `json:"displaytext,omitempty"` +} + +func (e *booleanSyncResponse) Error() error { + if e.Success == "true" { + return nil + } + + 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 +} + func csQuotePlus(s string) string { - return strings.Replace(s, "+", "%20", -1) + s = strings.Replace(s, "+", "%20", -1) + s = strings.Replace(s, "%5B", "[", -1) + s = strings.Replace(s, "%5D", "]", -1) + return s } func csEncode(s string) string { @@ -43,7 +187,7 @@ func rawValues(b json.RawMessage) (json.RawMessage, error) { return i[0], nil } -func (exo *Client) ParseResponse(resp *http.Response) (json.RawMessage, error) { +func (exo *Client) parseResponse(resp *http.Response) (json.RawMessage, error) { b, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err @@ -59,75 +203,362 @@ func (exo *Client) ParseResponse(resp *http.Response) (json.RawMessage, error) { } if resp.StatusCode >= 400 { - var e Error + var e ErrorResponse if err := json.Unmarshal(b, &e); err != nil { return nil, err } - - /* Need to account for differet error types */ - if e.ErrorCode != 0 { - return nil, e.Error() - } else { - var de DNSError - if err := json.Unmarshal(b, &de); err != nil { - return nil, err - } - return nil, fmt.Errorf("Exoscale error (%d): %s", resp.StatusCode, strings.Join(de.Name, "\n")) - } + return b, &e } - return b, nil } -func (exo *Client) Request(command string, params url.Values) (json.RawMessage, error) { +// 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) + if err != nil { + return nil, err + } - mac := hmac.New(sha1.New, []byte(exo.apiSecret)) - keys := make([]string, 0) - unencoded := make([]string, 0) + // Is it a Job? + job := new(JobResultResponse) + if err := json.Unmarshal(body, &job); err != nil { + return nil, err + } + // 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 + } + + // 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) + + async.Retries-- + + req := &QueryAsyncJobResult{JobID: job.JobID} + resp, err := exo.Request(req) + if err != nil { + return nil, err + } + result = resp.(*QueryAsyncJobResultResponse) + } + + if result.JobStatus == Failure { + if err := json.Unmarshal(*result.JobResult, &errorResponse); err != nil { + return nil, err + } + return errorResponse, errorResponse + } + + if result.JobStatus == Pending { + return result, 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 { + return nil, err + } + return errorResponse, errorResponse + } + + return resp, nil +} + +// BooleanRequest performs a sync request on a boolean call +func (exo *Client) BooleanRequest(req Command) error { + resp, err := exo.Request(req) + if err != nil { + return err + } + + return resp.(*booleanSyncResponse).Error() +} + +// BooleanAsyncRequest performs a sync request on a boolean call +func (exo *Client) BooleanAsyncRequest(req AsyncCommand, async AsyncInfo) error { + resp, err := exo.AsyncRequest(req, async) + if err != nil { + return err + } + + return resp.(*booleanAsyncResponse).Error() +} + +// Request performs a sync request on a generic command +func (exo *Client) Request(req Command) (interface{}, error) { + body, err := exo.request(req.name(), req) + if err != nil { + return nil, err + } + + resp := req.response() + if err := json.Unmarshal(body, resp); err != nil { + r := new(ErrorResponse) + if e := json.Unmarshal(body, r); e != nil { + return nil, r + } + return nil, err + } + + return resp, nil +} + +// request makes a Request while being close to the metal +func (exo *Client) request(command string, req interface{}) (json.RawMessage, error) { + params := url.Values{} + err := prepareValues("", ¶ms, req) + if err != nil { + return nil, err + } + if hookReq, ok := req.(onBeforeHook); ok { + hookReq.onBeforeSend(¶ms) + } params.Set("apikey", exo.apiKey) params.Set("command", command) params.Set("response", "json") - for k, _ := range params { + // This code is borrowed from net/url/url.go + // The way it's encoded by net/url doesn't match + // how CloudStack works. + var buf bytes.Buffer + keys := make([]string, 0, len(params)) + for k := range params { keys = append(keys, k) } + sort.Strings(keys) for _, k := range keys { - arg := k + "=" + csEncode(params[k][0]) - unencoded = append(unencoded, arg) + prefix := csEncode(k) + "=" + for _, v := range params[k] { + if buf.Len() > 0 { + buf.WriteByte('&') + } + buf.WriteString(prefix) + buf.WriteString(csEncode(v)) + } } - sign_string := strings.ToLower(strings.Join(unencoded, "&")) - mac.Write([]byte(sign_string)) + query := buf.String() + + mac := hmac.New(sha1.New, []byte(exo.apiSecret)) + mac.Write([]byte(strings.ToLower(query))) signature := csEncode(base64.StdEncoding.EncodeToString(mac.Sum(nil))) - query := params.Encode() - url := exo.endpoint + "?" + csQuotePlus(query) + "&signature=" + signature - resp, err := exo.client.Get(url) + reader := strings.NewReader(fmt.Sprintf("%s&signature=%s", csQuotePlus(query), signature)) + resp, err := exo.client.Post(exo.endpoint, "application/x-www-form-urlencoded", reader) if err != nil { return nil, err } - defer resp.Body.Close() - return exo.ParseResponse(resp) -} -func (exo *Client) DetailedRequest(uri string, params string, method string, header http.Header) (json.RawMessage, error) { - url := exo.endpoint + uri - - req, err := http.NewRequest(method, url, strings.NewReader(params)) + body, err := exo.parseResponse(resp) if err != nil { return nil, err } - req.Header = header + return body, nil +} - response, err := exo.client.Do(req) - if err != nil { - return nil, err +// prepareValues uses a command to build a POST request +// +// command is not a Command so it's easier to Test +func prepareValues(prefix string, params *url.Values, command interface{}) error { + value := reflect.ValueOf(command) + typeof := reflect.TypeOf(command) + // Going up the pointer chain to find the underlying struct + for typeof.Kind() == reflect.Ptr { + typeof = typeof.Elem() + value = value.Elem() } - defer response.Body.Close() - return exo.ParseResponse(response) + for i := 0; i < typeof.NumField(); i++ { + field := typeof.Field(i) + val := value.Field(i) + tag := field.Tag + if json, ok := tag.Lookup("json"); ok { + n, required := extractJSONTag(field.Name, json) + name := prefix + n + + switch val.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + v := val.Int() + if v == 0 { + if required { + return fmt.Errorf("%s.%s (%v) is required, got 0", typeof.Name(), field.Name, val.Kind()) + } + } else { + (*params).Set(name, strconv.FormatInt(v, 10)) + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + v := val.Uint() + if v == 0 { + if required { + return fmt.Errorf("%s.%s (%v) is required, got 0", typeof.Name(), field.Name, val.Kind()) + } + } else { + (*params).Set(name, strconv.FormatUint(v, 10)) + } + case reflect.Float32, reflect.Float64: + v := val.Float() + if v == 0 { + if required { + return fmt.Errorf("%s.%s (%v) is required, got 0", typeof.Name(), field.Name, val.Kind()) + } + } else { + (*params).Set(name, strconv.FormatFloat(v, 'f', -1, 64)) + } + case reflect.String: + v := val.String() + if v == "" { + if required { + return fmt.Errorf("%s.%s (%v) is required, got \"\"", typeof.Name(), field.Name, val.Kind()) + } + } else { + (*params).Set(name, v) + } + case reflect.Bool: + v := val.Bool() + if v == false { + if required { + params.Set(name, "false") + } + } else { + (*params).Set(name, "true") + } + case reflect.Slice: + switch field.Type.Elem().Kind() { + case reflect.Uint8: + switch field.Type { + case reflect.TypeOf(net.IPv4zero): + ip := (net.IP)(val.Bytes()) + if ip == nil || ip.Equal(net.IPv4zero) { + if required { + return fmt.Errorf("%s.%s (%v) is required, got zero IPv4 address", typeof.Name(), field.Name, val.Kind()) + } + } else { + (*params).Set(name, ip.String()) + } + default: + if val.Len() == 0 { + if required { + return fmt.Errorf("%s.%s (%v) is required, got empty slice", typeof.Name(), field.Name, val.Kind()) + } + } else { + v := val.Bytes() + (*params).Set(name, base64.StdEncoding.EncodeToString(v)) + } + } + case reflect.String: + { + if val.Len() == 0 { + if required { + return fmt.Errorf("%s.%s (%v) is required, got empty slice", typeof.Name(), field.Name, val.Kind()) + } + } else { + elems := make([]string, 0, val.Len()) + for i := 0; i < val.Len(); i++ { + // XXX what if the value contains a comma? Double encode? + s := val.Index(i).String() + elems = append(elems, s) + } + (*params).Set(name, strings.Join(elems, ",")) + } + } + default: + if val.Len() == 0 { + if required { + return fmt.Errorf("%s.%s (%v) is required, got empty slice", typeof.Name(), field.Name, val.Kind()) + } + } else { + err := prepareList(name, params, val.Interface()) + if err != nil { + return err + } + } + } + case reflect.Map: + if val.Len() == 0 { + if required { + return fmt.Errorf("%s.%s (%v) is required, got empty map", typeof.Name(), field.Name, val.Kind()) + } + } else { + err := prepareMap(name, params, val.Interface()) + if err != nil { + return err + } + } + default: + if required { + return fmt.Errorf("Unsupported type %s.%s (%v)", typeof.Name(), field.Name, val.Kind()) + } + } + } else { + log.Printf("[SKIP] %s.%s no json label found", typeof.Name(), field.Name) + } + } + + return nil +} + +func prepareList(prefix string, params *url.Values, slice interface{}) error { + value := reflect.ValueOf(slice) + + for i := 0; i < value.Len(); i++ { + prepareValues(fmt.Sprintf("%s[%d].", prefix, i), params, value.Index(i).Interface()) + } + + return nil +} + +func prepareMap(prefix string, params *url.Values, m interface{}) error { + value := reflect.ValueOf(m) + + for i, key := range value.MapKeys() { + var keyName string + var keyValue string + + switch key.Kind() { + case reflect.String: + keyName = key.String() + default: + return fmt.Errorf("Only map[string]string are supported (XXX)") + } + + val := value.MapIndex(key) + switch val.Kind() { + case reflect.String: + keyValue = val.String() + default: + return fmt.Errorf("Only map[string]string are supported (XXX)") + } + params.Set(fmt.Sprintf("%s[%d].%s", prefix, i, keyName), keyValue) + } + return nil +} + +// extractJSONTag returns the variable name or defaultName as well as if the field is required (!omitempty) +func extractJSONTag(defaultName, jsonTag string) (string, bool) { + tags := strings.Split(jsonTag, ",") + name := tags[0] + required := true + for _, tag := range tags { + if tag == "omitempty" { + required = false + } + } + + if name == "" || name == "omitempty" { + name = defaultName + } + return name, required } diff --git a/vendor/github.com/exoscale/egoscale/security_groups.go b/vendor/github.com/exoscale/egoscale/security_groups.go new file mode 100644 index 000000000..452083725 --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/security_groups.go @@ -0,0 +1,302 @@ +package egoscale + +import ( + "net/url" + "strconv" +) + +// SecurityGroup represent a firewalling set of rules +type SecurityGroup struct { + ID string `json:"id"` + Account string `json:"account,omitempty"` + Description string `json:"description,omitempty"` + Domain string `json:"domain,omitempty"` + DomainID string `json:"domainid,omitempty"` + Name string `json:"name"` + Project string `json:"project,omitempty"` + ProjectID string `json:"projectid,omitempty"` + VirtualMachineCount int `json:"virtualmachinecount,omitempty"` // CloudStack 4.6+ + VirtualMachineIDs []string `json:"virtualmachineids,omitempty"` // CloudStack 4.6+ + IngressRule []IngressRule `json:"ingressrule"` + EgressRule []EgressRule `json:"egressrule"` + Tags []ResourceTag `json:"tags,omitempty"` + JobID string `json:"jobid,omitempty"` + 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"` + Account string `json:"account,omitempty"` + Cidr string `json:"cidr,omitempty"` + Description string `json:"description,omitempty"` + IcmpType int `json:"icmptype,omitempty"` + IcmpCode int `json:"icmpcode,omitempty"` + StartPort int `json:"startport,omitempty"` + EndPort int `json:"endport,omitempty"` + Protocol string `json:"protocol,omitempty"` + Tags []ResourceTag `json:"tags,omitempty"` + SecurityGroupID string `json:"securitygroupid,omitempty"` + SecurityGroupName string `json:"securitygroupname,omitempty"` + UserSecurityGroupList []UserSecurityGroup `json:"usersecuritygrouplist,omitempty"` + JobID string `json:"jobid,omitempty"` + JobStatus JobStatusType `json:"jobstatus,omitempty"` +} + +// EgressRule represents the ingress rule +type EgressRule IngressRule + +// UserSecurityGroup represents the traffic of another security group +type UserSecurityGroup struct { + Group string `json:"group,omitempty"` + Account string `json:"account,omitempty"` +} + +// SecurityGroupResponse represents a generic security group response +type SecurityGroupResponse struct { + SecurityGroup SecurityGroup `json:"securitygroup"` +} + +// CreateSecurityGroup represents a security group creation +// +// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/createSecurityGroup.html +type CreateSecurityGroup struct { + Name string `json:"name"` + Description string `json:"description,omitempty"` +} + +func (*CreateSecurityGroup) name() string { + return "createSecurityGroup" +} + +func (*CreateSecurityGroup) response() interface{} { + return new(CreateSecurityGroupResponse) +} + +// CreateSecurityGroupResponse represents a new security group +type CreateSecurityGroupResponse SecurityGroupResponse + +// DeleteSecurityGroup represents a security group deletion +// +// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/deleteSecurityGroup.html +type DeleteSecurityGroup struct { + Account string `json:"account,omitempty"` + DomainID string `json:"domainid,omitempty"` + ID string `json:"id,omitempty"` // Mutually exclusive with name + Name string `json:"name,omitempty"` // Mutually exclusive with id + ProjectID string `json:"project,omitempty"` +} + +func (*DeleteSecurityGroup) name() string { + return "deleteSecurityGroup" +} + +func (*DeleteSecurityGroup) response() interface{} { + return new(booleanSyncResponse) +} + +// AuthorizeSecurityGroupIngress (Async) represents the ingress rule creation +// +// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/authorizeSecurityGroupIngress.html +type AuthorizeSecurityGroupIngress struct { + Account string `json:"account,omitempty"` + CidrList []string `json:"cidrlist,omitempty"` + Description string `json:"description,omitempty"` + DomainID string `json:"domainid,omitempty"` + IcmpType int `json:"icmptype,omitempty"` + IcmpCode int `json:"icmpcode,omitempty"` + StartPort int `json:"startport,omitempty"` + EndPort int `json:"endport,omitempty"` + ProjectID string `json:"projectid,omitempty"` + Protocol string `json:"protocol,omitempty"` + SecurityGroupID string `json:"securitygroupid,omitempty"` + SecurityGroupName string `json:"securitygroupname,omitempty"` + UserSecurityGroupList []UserSecurityGroup `json:"usersecuritygrouplist,omitempty"` +} + +func (*AuthorizeSecurityGroupIngress) name() string { + return "authorizeSecurityGroupIngress" +} + +func (*AuthorizeSecurityGroupIngress) asyncResponse() interface{} { + return new(AuthorizeSecurityGroupIngressResponse) +} + +func (req *AuthorizeSecurityGroupIngress) onBeforeSend(params *url.Values) error { + // ICMP code and type may be zero but can also be omitted... + if req.Protocol == "ICMP" { + params.Set("icmpcode", strconv.FormatInt(int64(req.IcmpCode), 10)) + params.Set("icmptype", strconv.FormatInt(int64(req.IcmpType), 10)) + } + return nil +} + +// AuthorizeSecurityGroupIngressResponse represents the new egress rule +// /!\ the Cloud Stack API document is not fully accurate. /!\ +type AuthorizeSecurityGroupIngressResponse SecurityGroupResponse + +// AuthorizeSecurityGroupEgress (Async) represents the egress rule creation +// +// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/authorizeSecurityGroupEgress.html +type AuthorizeSecurityGroupEgress AuthorizeSecurityGroupIngress + +func (*AuthorizeSecurityGroupEgress) name() string { + return "authorizeSecurityGroupEgress" +} + +func (*AuthorizeSecurityGroupEgress) asyncResponse() interface{} { + return new(AuthorizeSecurityGroupEgressResponse) +} + +func (req *AuthorizeSecurityGroupEgress) onBeforeSend(params *url.Values) error { + return (*AuthorizeSecurityGroupIngress)(req).onBeforeSend(params) +} + +// AuthorizeSecurityGroupEgressResponse represents the new egress rule +// /!\ the Cloud Stack API document is not fully accurate. /!\ +type AuthorizeSecurityGroupEgressResponse CreateSecurityGroupResponse + +// RevokeSecurityGroupIngress (Async) represents the ingress/egress rule deletion +// +// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/revokeSecurityGroupIngress.html +type RevokeSecurityGroupIngress struct { + ID string `json:"id"` +} + +func (*RevokeSecurityGroupIngress) name() string { + return "revokeSecurityGroupIngress" +} + +func (*RevokeSecurityGroupIngress) asyncResponse() interface{} { + return new(booleanAsyncResponse) +} + +// RevokeSecurityGroupEgress (Async) represents the ingress/egress rule deletion +// +// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/revokeSecurityGroupEgress.html +type RevokeSecurityGroupEgress struct { + ID string `json:"id"` +} + +func (*RevokeSecurityGroupEgress) name() string { + return "revokeSecurityGroupEgress" +} + +func (*RevokeSecurityGroupEgress) asyncResponse() interface{} { + return new(booleanAsyncResponse) +} + +// ListSecurityGroups represents a search for security groups +// +// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/listSecurityGroups.html +type ListSecurityGroups 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"` + ProjectID string `json:"projectid,omitempty"` + Type string `json:"type,omitempty"` + SecurityGroupName string `json:"securitygroupname,omitempty"` + Tags []ResourceTag `json:"tags,omitempty"` + VirtualMachineID string `json:"virtualmachineid,omitempty"` +} + +func (*ListSecurityGroups) name() string { + return "listSecurityGroups" +} + +func (*ListSecurityGroups) response() interface{} { + return new(ListSecurityGroupsResponse) +} + +// ListSecurityGroupsResponse represents a list of security groups +type ListSecurityGroupsResponse struct { + Count int `json:"count"` + SecurityGroup []SecurityGroup `json:"securitygroup"` +} + +// CreateIngressRule creates a set of ingress rules +// +// Deprecated: use the API directly +func (exo *Client) CreateIngressRule(req *AuthorizeSecurityGroupIngress, async AsyncInfo) ([]IngressRule, error) { + resp, err := exo.AsyncRequest(req, async) + if err != nil { + return nil, err + } + return resp.(*AuthorizeSecurityGroupIngressResponse).SecurityGroup.IngressRule, nil +} + +// CreateEgressRule creates a set of egress rules +// +// Deprecated: use the API directly +func (exo *Client) CreateEgressRule(req *AuthorizeSecurityGroupEgress, async AsyncInfo) ([]EgressRule, error) { + resp, err := exo.AsyncRequest(req, async) + if err != nil { + return nil, err + } + return resp.(*AuthorizeSecurityGroupEgressResponse).SecurityGroup.EgressRule, nil +} + +// CreateSecurityGroupWithRules create a security group with its rules +// Warning: it doesn't rollback in case of a failure! +// +// Deprecated: use the API directly +func (exo *Client) CreateSecurityGroupWithRules(name string, ingress []AuthorizeSecurityGroupIngress, egress []AuthorizeSecurityGroupEgress, async AsyncInfo) (*SecurityGroup, error) { + req := &CreateSecurityGroup{ + Name: name, + } + resp, err := exo.Request(req) + if err != nil { + return nil, err + } + + sg := resp.(*SecurityGroupResponse).SecurityGroup + reqs := make([]AsyncCommand, 0, len(ingress)+len(egress)) + // Egress rules + for _, ereq := range egress { + ereq.SecurityGroupID = sg.ID + reqs = append(reqs, &ereq) + + } + // Ingress rules + for _, ireq := range ingress { + ireq.SecurityGroupID = sg.ID + reqs = append(reqs, &ireq) + } + + for _, r := range reqs { + _, err := exo.AsyncRequest(r, async) + if err != nil { + return nil, err + } + } + + r, err := exo.Request(&ListSecurityGroups{ + ID: sg.ID, + }) + if err != nil { + return nil, err + } + + sg = r.(*ListSecurityGroupsResponse).SecurityGroup[0] + return &sg, nil +} + +// DeleteSecurityGroup deletes a security group +// +// Deprecated: use the API directly +func (exo *Client) DeleteSecurityGroup(name string) error { + req := &DeleteSecurityGroup{ + Name: name, + } + return exo.BooleanRequest(req) +} diff --git a/vendor/github.com/exoscale/egoscale/service_offerings.go b/vendor/github.com/exoscale/egoscale/service_offerings.go new file mode 100644 index 000000000..3c9a0b4ab --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/service_offerings.go @@ -0,0 +1,65 @@ +package egoscale + +// ServiceOffering corresponds to the Compute Offerings +type ServiceOffering struct { + ID string `json:"id"` + CPUNumber int `json:"cpunumber"` + CPUSpeed int `json:"cpuspeed"` + Created string `json:"created"` + DefaultUse bool `json:"defaultuse,omitempty"` + DeploymentPlanner string `json:"deploymentplanner,omitempty"` + DiskBytesReadRate int64 `json:"diskBytesReadRate,omitempty"` + DiskBytesWriteRate int64 `json:"diskBytesWriteRate,omitempty"` + DiskIopsReadRate int64 `json:"diskIopsReadRate,omitempty"` + DiskIopsWriteRate int64 `json:"diskIopsWriteRate,omitempty"` + DisplayText string `json:"displaytext,omitempty"` + Domain string `json:"domain"` + DomainID string `json:"domainid"` + HostTags string `json:"hosttags,omitempty"` + HypervisorSnapshotReserve int `json:"hypervisorsnapshotreserve,omitempty"` + IsCustomized bool `json:"iscustomized,omitempty"` + IsCustomizedIops bool `json:"iscustomizediops,omitempty"` + IsSystem bool `json:"issystem,omitempty"` + IsVolatile bool `json:"isvolatile,omitempty"` + LimitCPUUse bool `json:"limitcpuuse,omitempty"` + MaxIops int64 `json:"maxiops,omitempty"` + Memory int `json:"memory,omitempty"` + MinIops int64 `json:"miniops,omitempty"` + Name string `json:"name,omitempty"` + NetworkRate int `json:"networkrate,omitempty"` + OfferHA bool `json:"offerha,omitempty"` + ServiceOfferingDetails map[string]string `json:"serviceofferingdetails,omitempty"` + StorageType string `json:"storagetype,omitempty"` + SystemVMType string `json:"systemvmtype,omitempty"` + Tags []ResourceTag `json:"tags,omitempty"` +} + +// ListServiceOfferings represents a query for service offerings +// +// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/listServiceOfferings.html +type ListServiceOfferings struct { + DomainID string `json:"domainid,omitempty"` + ID string `json:"id,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"` + PageSize int `json:"pagesize,omitempty"` + SystemVMType string `json:"systemvmtype,omitempty"` // consoleproxy, secondarystoragevm, or domainrouter + VirtualMachineID string `json:"virtualmachineid,omitempty"` +} + +func (*ListServiceOfferings) name() string { + return "listServiceOfferings" +} + +func (*ListServiceOfferings) response() interface{} { + return new(ListServiceOfferingsResponse) +} + +// ListServiceOfferingsResponse represents a list of service offerings +type ListServiceOfferingsResponse struct { + Count int `json:"count"` + ServiceOffering []ServiceOffering `json:"serviceoffering"` +} diff --git a/vendor/github.com/exoscale/egoscale/snapshots.go b/vendor/github.com/exoscale/egoscale/snapshots.go new file mode 100644 index 000000000..5766dc693 --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/snapshots.go @@ -0,0 +1,120 @@ +package egoscale + +// Snapshot represents a volume snapshot +type Snapshot struct { + ID string `json:"id"` + Account string `json:"account"` + Created string `json:"created,omitempty"` + Domain string `json:"domain"` + DomainID string `json:"domainid"` + IntervalType string `json:"intervaltype,omitempty"` // hourly, daily, weekly, monthly, ..., none + Name string `json:"name,omitempty"` + PhysicalSize int64 `json:"physicalsize"` + Project string `json:"project"` + ProjectID string `json:"projectid"` + Revertable bool `json:"revertable,omitempty"` + Size int64 `json:"size,omitempty"` + SnapshotType string `json:"snapshottype,omitempty"` + State string `json:"state"` // BackedUp, Creating, BackingUp, ... + VolumeID string `json:"volumeid"` + VolumeName string `json:"volumename,omitempty"` + VolumeType string `json:"volumetype,omitempty"` + ZoneID string `json:"zoneid"` + Tags []ResourceTag `json:"tags"` + JobID string `json:"jobid,omitempty"` + 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 +type CreateSnapshot struct { + VolumeID string `json:"volumeid"` + Account string `json:"account,omitempty"` + DomainID string `json:"domainid,omitempty"` + PolicyID string `json:"policyid,omitempty"` + QuiesceVM bool `json:"quiescevm,omitempty"` +} + +func (*CreateSnapshot) name() string { + return "createSnapshot" +} + +func (*CreateSnapshot) asyncResponse() interface{} { + return new(CreateSnapshotResponse) +} + +// CreateSnapshotResponse represents a freshly created snapshot +type CreateSnapshotResponse struct { + Snapshot Snapshot `json:"snapshot"` +} + +// ListSnapshots lists the volume snapshots +// +// CloudStackAPI: http://cloudstack.apache.org/api/apidocs-4.10/apis/listSnapshots.html +type ListSnapshots struct { + Account string `json:"account,omitempty"` + DomainID string `json:"domainid,omitempty"` + ID string `json:"id,omitempty"` + IntervalType string `json:"intervaltype,omitempty"` + IsRecursive bool `json:"isrecursive,omitempty"` + Keyword string `json:"keyword,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"` + SnapshotType string `json:"snapshottype,omitempty"` + Tags []ResourceTag `json:"tags,omitempty"` + VolumeID string `json:"volumeid,omitempty"` + ZoneID string `json:"zoneid,omitempty"` +} + +func (*ListSnapshots) name() string { + return "listSnapshots" +} + +func (*ListSnapshots) response() interface{} { + return new(ListSnapshotsResponse) +} + +// ListSnapshotsResponse represents a list of volume snapshots +type ListSnapshotsResponse struct { + Count int `json:"count"` + Snapshot []Snapshot `json:"snapshot"` +} + +// DeleteSnapshot represents the deletion of a volume snapshot +// +// CloudStackAPI: http://cloudstack.apache.org/api/apidocs-4.10/apis/deleteSnapshot.html +type DeleteSnapshot struct { + ID string `json:"id"` +} + +func (*DeleteSnapshot) name() string { + return "deleteSnapshot" +} + +func (*DeleteSnapshot) asyncResponse() interface{} { + return new(booleanAsyncResponse) +} + +// RevertSnapshot revert a volume snapshot +// +// CloudStackAPI: http://cloudstack.apache.org/api/apidocs-4.10/apis/revertSnapshot.html +type RevertSnapshot struct { + ID string `json:"id"` +} + +func (*RevertSnapshot) name() string { + return "revertSnapshot" +} + +func (*RevertSnapshot) asyncResponse() interface{} { + return new(booleanAsyncResponse) +} diff --git a/vendor/github.com/exoscale/egoscale/tags.go b/vendor/github.com/exoscale/egoscale/tags.go new file mode 100644 index 000000000..d0ed616e0 --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/tags.go @@ -0,0 +1,93 @@ +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 +type ResourceTag struct { + Account string `json:"account,omitempty"` + Customer string `json:"customer,omitempty"` + Domain string `json:"domain,omitempty"` + DomainID string `json:"domainid,omitempty"` + Key string `json:"key"` + Project string `json:"project,omitempty"` + ProjectID string `json:"projectid,omitempty"` + ResourceID string `json:"resourceid,omitempty"` + ResourceType string `json:"resourcetype,omitempty"` + Value string `json:"value"` +} + +// CreateTags (Async) creates resource tag(s) +// +// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/createTags.html +type CreateTags struct { + ResourceIDs []string `json:"resourceids"` + ResourceType string `json:"resourcetype"` + Tags []ResourceTag `json:"tags"` + Customer string `json:"customer,omitempty"` +} + +func (*CreateTags) name() string { + return "createTags" +} + +func (*CreateTags) asyncResponse() interface{} { + return new(booleanAsyncResponse) +} + +// DeleteTags (Async) deletes the resource tag(s) +// +// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/deleteTags.html +type DeleteTags struct { + ResourceIDs []string `json:"resourceids"` + ResourceType string `json:"resourcetype"` + Tags []ResourceTag `json:"tags,omitempty"` +} + +func (*DeleteTags) name() string { + return "deleteTags" +} + +func (*DeleteTags) asyncResponse() interface{} { + return new(booleanAsyncResponse) +} + +// ListTags list resource tag(s) +// +// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/listTags.html +type ListTags struct { + Account string `json:"account,omitempty"` + Customer string `json:"customer,omitempty"` + DomainID string `json:"domainid,omitempty"` + IsRecursive bool `json:"isrecursive,omitempty"` + Key string `json:"key,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"` + ResourceID string `json:"resourceid,omitempty"` + ResourceType string `json:"resourcetype,omitempty"` + Value string `json:"value,omitempty"` +} + +func (*ListTags) name() string { + return "listTags" +} + +func (*ListTags) response() interface{} { + return new(ListTagsResponse) +} + +// ListTagsResponse represents a list of resource tags +type ListTagsResponse struct { + Count int `json:"count"` + Tag []ResourceTag `json:"tag"` +} diff --git a/vendor/github.com/exoscale/egoscale/templates.go b/vendor/github.com/exoscale/egoscale/templates.go new file mode 100644 index 000000000..4c129bcce --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/templates.go @@ -0,0 +1,76 @@ +package egoscale + +// Template represents a machine to be deployed +type Template struct { + Account string `json:"account,omitempty"` + AccountID string `json:"accountid,omitempty"` + Bootable bool `json:"bootable,omitempty"` + Checksum string `json:"checksum,omitempty"` + Created string `json:"created,omitempty"` + CrossZones bool `json:"crossZones,omitempty"` + Details map[string]string `json:"details,omitempty"` + DisplayText string `json:"displaytext,omitempty"` + Domain string `json:"domain,omitempty"` + DomainID string `json:"domainid,omitempty"` + Format string `json:"format,omitempty"` + HostID string `json:"hostid,omitempty"` + HostName string `json:"hostname,omitempty"` + Hypervisor string `json:"hypervisor,omitempty"` + ID string `json:"id,omitempty"` + IsDynamicallyScalable bool `json:"isdynamicallyscalable,omitempty"` + IsExtractable bool `json:"isextractable,omitempty"` + IsFeatured bool `json:"isfeatured,omitempty"` + IsPublic bool `json:"ispublic,omitempty"` + IsReady bool `json:"isready,omitempty"` + Name string `json:"name,omitempty"` + OsTypeID string `json:"ostypeid,omitempty"` + OsTypeName string `json:"ostypename,omitempty"` + PasswordEnabled bool `json:"passwordenabled,omitempty"` + Project string `json:"project,omitempty"` + ProjectID string `json:"projectid,omitempty"` + Removed string `json:"removed,omitempty"` + Size int64 `json:"size,omitempty"` + SourceTemplateID string `json:"sourcetemplateid,omitempty"` + SSHKeyEnabled bool `json:"sshkeyenabled,omitempty"` + Status string `json:"status,omitempty"` + Zoneid string `json:"zoneid,omitempty"` + 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. + Account string `json:"account,omitempty"` + DomainID string `json:"domainid,omitempty"` + Hypervisor string `json:"hypervisor,omitempty"` + ID string `json:"id,omitempty"` + IsRecursive bool `json:"isrecursive,omitempty"` + Keyword string `json:"keyword,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"` + Tags []ResourceTag `json:"tags,omitempty"` + ZoneID string `json:"zoneid,omitempty"` +} + +func (*ListTemplates) name() string { + return "listTemplates" +} + +func (*ListTemplates) response() interface{} { + return new(ListTemplatesResponse) +} + +// ListTemplatesResponse represents a list of templates +type ListTemplatesResponse struct { + Count int `json:"count"` + Template []Template `json:"template"` +} diff --git a/vendor/github.com/exoscale/egoscale/topology.go b/vendor/github.com/exoscale/egoscale/topology.go index ad004dfe3..9ee270681 100644 --- a/vendor/github.com/exoscale/egoscale/topology.go +++ b/vendor/github.com/exoscale/egoscale/topology.go @@ -1,186 +1,163 @@ package egoscale import ( - "encoding/json" "fmt" - "net/url" "regexp" "strings" ) +// GetSecurityGroups returns all security groups +// +// Deprecated: do it yourself func (exo *Client) GetSecurityGroups() (map[string]SecurityGroup, error) { var sgs map[string]SecurityGroup - params := url.Values{} - resp, err := exo.Request("listSecurityGroups", params) - + resp, err := exo.Request(&ListSecurityGroups{}) if err != nil { return nil, err } - var r ListSecurityGroupsResponse - if err := json.Unmarshal(resp, &r); err != nil { - return nil, err - } - sgs = make(map[string]SecurityGroup) - for _, sg := range r.SecurityGroups { - sgs[sg.Name] = *sg + for _, sg := range resp.(*ListSecurityGroupsResponse).SecurityGroup { + sgs[sg.Name] = sg } return sgs, nil } -func (exo *Client) GetSecurityGroupId(name string) (string, error) { - params := url.Values{} - resp, err := exo.Request("listSecurityGroups", params) +// GetSecurityGroupID returns security group by name +// +// Deprecated: do it yourself +func (exo *Client) GetSecurityGroupID(name string) (string, error) { + resp, err := exo.Request(&ListSecurityGroups{SecurityGroupName: name}) if err != nil { return "", err } - var r ListSecurityGroupsResponse - err = json.Unmarshal(resp, &r) - if err != nil { - return "", err - } - - for _, sg := range r.SecurityGroups { + for _, sg := range resp.(*ListSecurityGroupsResponse).SecurityGroup { if sg.Name == name { - return sg.Id, nil + return sg.ID, nil } } return "", nil } -func (exo *Client) GetZones() (map[string]string, error) { +// GetAllZones returns all the zone id by name +// +// Deprecated: do it yourself +func (exo *Client) GetAllZones() (map[string]string, error) { var zones map[string]string - params := url.Values{} - resp, err := exo.Request("listZones", params) - + resp, err := exo.Request(&ListZones{}) if err != nil { - return nil, err - } - - var r ListZonesResponse - if err := json.Unmarshal(resp, &r); err != nil { - return nil, err + return zones, err } zones = make(map[string]string) - for _, zone := range r.Zones { - zones[zone.Name] = zone.Id + for _, zone := range resp.(*ListZonesResponse).Zone { + zones[strings.ToLower(zone.Name)] = zone.ID } return zones, nil } +// GetProfiles returns a mapping of the service offerings by name +// +// Deprecated: do it yourself func (exo *Client) GetProfiles() (map[string]string, error) { - - var profiles map[string]string - params := url.Values{} - resp, err := exo.Request("listServiceOfferings", params) - + profiles := make(map[string]string) + resp, err := exo.Request(&ListServiceOfferings{}) if err != nil { - return nil, err + return profiles, nil } - var r ListServiceOfferingsResponse - if err := json.Unmarshal(resp, &r); err != nil { - return nil, err - } - - profiles = make(map[string]string) - for _, offering := range r.ServiceOfferings { - profiles[strings.ToLower(offering.Name)] = offering.Id + for _, offering := range resp.(*ListServiceOfferingsResponse).ServiceOffering { + profiles[strings.ToLower(offering.Name)] = offering.ID } return profiles, nil } +// GetKeypairs returns the list of SSH keyPairs +// +// Deprecated: do it yourself func (exo *Client) GetKeypairs() ([]SSHKeyPair, error) { - params := url.Values{} - - resp, err := exo.Request("listSSHKeyPairs", params) + var keypairs []SSHKeyPair + resp, err := exo.Request(&ListSSHKeyPairs{}) if err != nil { - return nil, err + return keypairs, err } - var r ListSSHKeyPairsResponse - if err := json.Unmarshal(resp, &r); err != nil { - return nil, err - } - - var keypairs = make([]SSHKeyPair, r.Count, r.Count) - for i, keypair := range r.SSHKeyPairs { - keypairs[i] = *keypair + r := resp.(*ListSSHKeyPairsResponse) + keypairs = make([]SSHKeyPair, r.Count) + for i, keypair := range r.SSHKeyPair { + keypairs[i] = keypair } return keypairs, nil } +// GetAffinityGroups returns a mapping of the (anti-)affinity groups +// +// Deprecated: do it yourself func (exo *Client) GetAffinityGroups() (map[string]string, error) { var affinitygroups map[string]string - params := url.Values{} - - resp, err := exo.Request("listAffinityGroups", params) + resp, err := exo.Request(&ListAffinityGroups{}) if err != nil { - return nil, err - } - - var r ListAffinityGroupsResponse - if err := json.Unmarshal(resp, &r); err != nil { - return nil, err + return affinitygroups, err } affinitygroups = make(map[string]string) - for _, affinitygroup := range r.AffinityGroups { - affinitygroups[affinitygroup.Name] = affinitygroup.Id + for _, affinitygroup := range resp.(*ListAffinityGroupsResponse).AffinityGroup { + affinitygroups[affinitygroup.Name] = affinitygroup.ID } return affinitygroups, nil - } -func (exo *Client) GetImages() (map[string]map[int]string, error) { - var images map[string]map[int]string - images = make(map[string]map[int]string) - - params := url.Values{} - params.Set("templatefilter", "featured") - - resp, err := exo.Request("listTemplates", params) +// GetImages list the available featured images and group them by name, then size. +// +// Deprecated: do it yourself +func (exo *Client) GetImages() (map[string]map[int64]string, error) { + var images map[string]map[int64]string + images = make(map[string]map[int64]string) + re := regexp.MustCompile(`^Linux (?P.+?) (?P[0-9.]+)\b`) + resp, err := exo.Request(&ListTemplates{ + TemplateFilter: "featured", + ZoneID: "1", // XXX: Hack to list only CH-GVA + }) if err != nil { - return nil, err + return images, err } - var r ListTemplatesResponse - if err := json.Unmarshal(resp, &r); err != nil { - return nil, err - } + for _, template := range resp.(*ListTemplatesResponse).Template { + size := int64(template.Size >> 30) // B to GiB + + fullname := strings.ToLower(template.Name) + + if _, present := images[fullname]; !present { + images[fullname] = make(map[int64]string) + } + images[fullname][size] = template.ID - re := regexp.MustCompile(`^Linux (?P.+?) (?P[0-9.]+).*$`) - for _, template := range r.Templates { - size := int(template.Size / (1024 * 1024 * 1024)) submatch := re.FindStringSubmatch(template.Name) if len(submatch) > 0 { name := strings.Replace(strings.ToLower(submatch[1]), " ", "-", -1) version := submatch[2] image := fmt.Sprintf("%s-%s", name, version) - _, present := images[image] - if !present { - images[image] = make(map[int]string) + if _, present := images[image]; !present { + images[image] = make(map[int64]string) } - images[image][size] = template.Id - - images[fmt.Sprintf("%s-%s", name, version)][size] = template.Id + images[image][size] = template.ID } } return images, nil } +// GetTopology returns an big, yet incomplete view of the world +// +// Deprecated: will go away in the future func (exo *Client) GetTopology() (*Topology, error) { - - zones, err := exo.GetZones() + zones, err := exo.GetAllZones() if err != nil { return nil, err } @@ -194,7 +171,7 @@ func (exo *Client) GetTopology() (*Topology, error) { } groups := make(map[string]string) for k, v := range securityGroups { - groups[k] = v.Id + groups[k] = v.ID } keypairs, err := exo.GetKeypairs() @@ -212,6 +189,7 @@ func (exo *Client) GetTopology() (*Topology, error) { if err != nil { return nil, err } + profiles, err := exo.GetProfiles() if err != nil { return nil, err @@ -219,9 +197,9 @@ func (exo *Client) GetTopology() (*Topology, error) { topo := &Topology{ Zones: zones, - Profiles: profiles, Images: images, Keypairs: keynames, + Profiles: profiles, AffinityGroups: affinitygroups, SecurityGroups: groups, } diff --git a/vendor/github.com/exoscale/egoscale/types.go b/vendor/github.com/exoscale/egoscale/types.go index 0ba51b0b7..484dc31f5 100644 --- a/vendor/github.com/exoscale/egoscale/types.go +++ b/vendor/github.com/exoscale/egoscale/types.go @@ -1,10 +1,10 @@ package egoscale import ( - "encoding/json" "net/http" ) +// Client represents the CloudStack API client type Client struct { client *http.Client endpoint string @@ -12,519 +12,12 @@ type Client struct { apiSecret string } -type Error struct { - ErrorCode int `json:"errorcode"` - CSErrorCode int `json:"cserrorcode"` - ErrorText string `json:"errortext"` -} - -type StandardResponse struct { - Success string `json:"success"` - DisplayText string `json:"displaytext"` -} - +// Topology represents a view of the servers type Topology struct { Zones map[string]string - Images map[string]map[int]string + Images map[string]map[int64]string Profiles map[string]string Keypairs []string SecurityGroups map[string]string AffinityGroups map[string]string } - -type SecurityGroupRule struct { - Cidr string - IcmpType int - IcmpCode int - Port int - Protocol string - SecurityGroupId string - UserSecurityGroupList []UserSecurityGroup `json:"usersecuritygrouplist,omitempty"` -} - -type UserSecurityGroup struct { - Group string `json:"group,omitempty"` - Account string `json:"account,omitempty"` -} - -type MachineProfile struct { - Name string - SecurityGroups []string - Keypair string - Userdata string - ServiceOffering string - Template string - Zone string - AffinityGroups []string -} - -type ListZonesResponse struct { - Count int `json:"count"` - Zones []*Zone `json:"zone"` -} - -type Zone struct { - Allocationstate string `json:"allocationstate,omitempty"` - Description string `json:"description,omitempty"` - Displaytext string `json:"displaytext,omitempty"` - Domain string `json:"domain,omitempty"` - Domainid string `json:"domainid,omitempty"` - Domainname string `json:"domainname,omitempty"` - Id string `json:"id,omitempty"` - Internaldns1 string `json:"internaldns1,omitempty"` - Internaldns2 string `json:"internaldns2,omitempty"` - Ip6dns1 string `json:"ip6dns1,omitempty"` - Ip6dns2 string `json:"ip6dns2,omitempty"` - Localstorageenabled bool `json:"localstorageenabled,omitempty"` - Name string `json:"name,omitempty"` - Networktype string `json:"networktype,omitempty"` - Resourcedetails map[string]string `json:"resourcedetails,omitempty"` - Securitygroupsenabled bool `json:"securitygroupsenabled,omitempty"` - Vlan string `json:"vlan,omitempty"` - Zonetoken string `json:"zonetoken,omitempty"` -} - -type ListServiceOfferingsResponse struct { - Count int `json:"count"` - ServiceOfferings []*ServiceOffering `json:"serviceoffering"` -} - -type ServiceOffering struct { - Cpunumber int `json:"cpunumber,omitempty"` - Cpuspeed int `json:"cpuspeed,omitempty"` - Displaytext string `json:"displaytext,omitempty"` - Domain string `json:"domain,omitempty"` - Domainid string `json:"domainid,omitempty"` - Hosttags string `json:"hosttags,omitempty"` - Id string `json:"id,omitempty"` - Iscustomized bool `json:"iscustomized,omitempty"` - Issystem bool `json:"issystem,omitempty"` - Isvolatile bool `json:"isvolatile,omitempty"` - Memory int `json:"memory,omitempty"` - Name string `json:"name,omitempty"` - Networkrate int `json:"networkrate,omitempty"` - Serviceofferingdetails map[string]string `json:"serviceofferingdetails,omitempty"` -} - -type ListTemplatesResponse struct { - Count int `json:"count"` - Templates []*Template `json:"template"` -} - -type Template struct { - Account string `json:"account,omitempty"` - Accountid string `json:"accountid,omitempty"` - Bootable bool `json:"bootable,omitempty"` - Checksum string `json:"checksum,omitempty"` - Created string `json:"created,omitempty"` - CrossZones bool `json:"crossZones,omitempty"` - Details map[string]string `json:"details,omitempty"` - Displaytext string `json:"displaytext,omitempty"` - Domain string `json:"domain,omitempty"` - Domainid string `json:"domainid,omitempty"` - Format string `json:"format,omitempty"` - Hostid string `json:"hostid,omitempty"` - Hostname string `json:"hostname,omitempty"` - Hypervisor string `json:"hypervisor,omitempty"` - Id string `json:"id,omitempty"` - Isdynamicallyscalable bool `json:"isdynamicallyscalable,omitempty"` - Isextractable bool `json:"isextractable,omitempty"` - Isfeatured bool `json:"isfeatured,omitempty"` - Ispublic bool `json:"ispublic,omitempty"` - Isready bool `json:"isready,omitempty"` - Name string `json:"name,omitempty"` - Ostypeid string `json:"ostypeid,omitempty"` - Ostypename string `json:"ostypename,omitempty"` - Passwordenabled bool `json:"passwordenabled,omitempty"` - Project string `json:"project,omitempty"` - Projectid string `json:"projectid,omitempty"` - Removed string `json:"removed,omitempty"` - Size int64 `json:"size,omitempty"` - Sourcetemplateid string `json:"sourcetemplateid,omitempty"` - Sshkeyenabled bool `json:"sshkeyenabled,omitempty"` - Status string `json:"status,omitempty"` - Zoneid string `json:"zoneid,omitempty"` - Zonename string `json:"zonename,omitempty"` -} - -type ListSSHKeyPairsResponse struct { - Count int `json:"count"` - SSHKeyPairs []*SSHKeyPair `json:"sshkeypair"` -} - -type SSHKeyPair struct { - Fingerprint string `json:"fingerprint,omitempty"` - Name string `json:"name,omitempty"` -} - -type ListAffinityGroupsResponse struct { - Count int `json:"count"` - AffinityGroups []*AffinityGroup `json:"affinitygroup"` -} - -type AffinityGroup struct { - Name string `json:"name,omitempty"` - Type string `json:"type,omitempty"` - Description string `json:"description,omitempty"` - Id string `json:"id,omitempty"` - Domainid string `json:"domainid,omitempty"` - Domain string `json:"domain,omitempty"` - Account string `json:"account,omitempty"` -} - -type CreateAffinityGroupResponseWrapper struct { - Wrapped AffinityGroup `json:"affinitygroup"` -} - -type ListSecurityGroupsResponse struct { - Count int `json:"count"` - SecurityGroups []*SecurityGroup `json:"securitygroup"` -} - -type SecurityGroup struct { - Account string `json:"account,omitempty"` - Description string `json:"description,omitempty"` - Domain string `json:"domain,omitempty"` - Domainid string `json:"domainid,omitempty"` - Id string `json:"id,omitempty"` - Name string `json:"name,omitempty"` - Project string `json:"project,omitempty"` - Projectid string `json:"projectid,omitempty"` - IngressRules []struct { - RuleId string `json:"ruleid,omitempty"` - Protocol string `json:"protocol,omitempty"` - StartPort int `json:"startport,omitempty"` - EndPort int `json:"endport,omitempty"` - Cidr string `json:"cidr,omitempty"` - IcmpCode int `json:"icmpcode,omitempty"` - IcmpType int `json:"icmptype,omitempty"` - Tags []string `json:"tags,omitempty"` - } `json:"ingressrule,omitempty"` - EgressRules []struct { - RuleId string `json:"ruleid,omitempty"` - Protocol string `json:"protocol,omitempty"` - StartPort int `json:"startport,omitempty"` - EndPort int `json:"endport,omitempty"` - Cidr string `json:"cidr,omitempty"` - IcmpCode int `json:"icmpcode,omitempty"` - IcmpType int `json:"icmptype,omitempty"` - Tags []string `json:"tags,omitempty"` - } `json:"egressrule,omitempty"` - Tags []string `json:"tags,omitempty"` -} - -type CreateSecurityGroupResponseWrapper struct { - Wrapped CreateSecurityGroupResponse `json:"securitygroup"` -} -type CreateSecurityGroupResponse struct { - Account string `json:"account,omitempty"` - Description string `json:"description,omitempty"` - Domain string `json:"domain,omitempty"` - Domainid string `json:"domainid,omitempty"` - Id string `json:"id,omitempty"` - Name string `json:"name,omitempty"` - Project string `json:"project,omitempty"` - Projectid string `json:"projectid,omitempty"` -} - -type AuthorizeSecurityGroupIngressResponse struct { - JobID string `json:"jobid,omitempty"` - Account string `json:"account,omitempty"` - Cidr string `json:"cidr,omitempty"` - Endport int `json:"endport,omitempty"` - Icmpcode int `json:"icmpcode,omitempty"` - Icmptype int `json:"icmptype,omitempty"` - Protocol string `json:"protocol,omitempty"` - Ruleid string `json:"ruleid,omitempty"` - Securitygroupname string `json:"securitygroupname,omitempty"` - Startport int `json:"startport,omitempty"` -} - -type AuthorizeSecurityGroupEgressResponse struct { - JobID string `json:"jobid,omitempty"` - Account string `json:"account,omitempty"` - Cidr string `json:"cidr,omitempty"` - Endport int `json:"endport,omitempty"` - Icmpcode int `json:"icmpcode,omitempty"` - Icmptype int `json:"icmptype,omitempty"` - Protocol string `json:"protocol,omitempty"` - Ruleid string `json:"ruleid,omitempty"` - Securitygroupname string `json:"securitygroupname,omitempty"` - Startport int `json:"startport,omitempty"` -} - -type DeployVirtualMachineWrappedResponse struct { - Wrapped DeployVirtualMachineResponse `json:"virtualmachine"` -} - -type DeployVirtualMachineResponse struct { - JobID string `json:"jobid,omitempty"` - Account string `json:"account,omitempty"` - Affinitygroup []struct { - Account string `json:"account,omitempty"` - Description string `json:"description,omitempty"` - Domain string `json:"domain,omitempty"` - Domainid string `json:"domainid,omitempty"` - Id string `json:"id,omitempty"` - Name string `json:"name,omitempty"` - Type string `json:"type,omitempty"` - VirtualmachineIds []string `json:"virtualmachineIds,omitempty"` - } `json:"affinitygroup,omitempty"` - Cpunumber int `json:"cpunumber,omitempty"` - Cpuspeed int `json:"cpuspeed,omitempty"` - Cpuused string `json:"cpuused,omitempty"` - Created string `json:"created,omitempty"` - Details map[string]string `json:"details,omitempty"` - Diskioread int64 `json:"diskioread,omitempty"` - Diskiowrite int64 `json:"diskiowrite,omitempty"` - Diskkbsread int64 `json:"diskkbsread,omitempty"` - Diskkbswrite int64 `json:"diskkbswrite,omitempty"` - Displayname string `json:"displayname,omitempty"` - Displayvm bool `json:"displayvm,omitempty"` - Domain string `json:"domain,omitempty"` - Domainid string `json:"domainid,omitempty"` - Forvirtualnetwork bool `json:"forvirtualnetwork,omitempty"` - Group string `json:"group,omitempty"` - Groupid string `json:"groupid,omitempty"` - Guestosid string `json:"guestosid,omitempty"` - Haenable bool `json:"haenable,omitempty"` - Hostid string `json:"hostid,omitempty"` - Hostname string `json:"hostname,omitempty"` - Hypervisor string `json:"hypervisor,omitempty"` - Id string `json:"id,omitempty"` - Instancename string `json:"instancename,omitempty"` - Isdynamicallyscalable bool `json:"isdynamicallyscalable,omitempty"` - Isodisplaytext string `json:"isodisplaytext,omitempty"` - Isoid string `json:"isoid,omitempty"` - Isoname string `json:"isoname,omitempty"` - Keypair string `json:"keypair,omitempty"` - Memory int `json:"memory,omitempty"` - Name string `json:"name,omitempty"` - Networkkbsread int64 `json:"networkkbsread,omitempty"` - Networkkbswrite int64 `json:"networkkbswrite,omitempty"` - Nic []struct { - Broadcasturi string `json:"broadcasturi,omitempty"` - Gateway string `json:"gateway,omitempty"` - Id string `json:"id,omitempty"` - Ipaddress string `json:"ipaddress,omitempty"` - Isdefault bool `json:"isdefault,omitempty"` - Isolationuri string `json:"isolationuri,omitempty"` - Macaddress string `json:"macaddress,omitempty"` - Netmask string `json:"netmask,omitempty"` - Networkid string `json:"networkid,omitempty"` - Networkname string `json:"networkname,omitempty"` - Secondaryip []string `json:"secondaryip,omitempty"` - Traffictype string `json:"traffictype,omitempty"` - Type string `json:"type,omitempty"` - } `json:"nic,omitempty"` - Password string `json:"password,omitempty"` - Passwordenabled bool `json:"passwordenabled,omitempty"` - Project string `json:"project,omitempty"` - Projectid string `json:"projectid,omitempty"` - Publicip string `json:"publicip,omitempty"` - Publicipid string `json:"publicipid,omitempty"` - Rootdeviceid int64 `json:"rootdeviceid,omitempty"` - Rootdevicetype string `json:"rootdevicetype,omitempty"` - Serviceofferingid string `json:"serviceofferingid,omitempty"` - Serviceofferingname string `json:"serviceofferingname,omitempty"` - Servicestate string `json:"servicestate,omitempty"` - State string `json:"state,omitempty"` - Templatedisplaytext string `json:"templatedisplaytext,omitempty"` - Templateid string `json:"templateid,omitempty"` - Templatename string `json:"templatename,omitempty"` - Zoneid string `json:"zoneid,omitempty"` - Zonename string `json:"zonename,omitempty"` -} - -type QueryAsyncJobResultResponse struct { - Accountid string `json:"accountid,omitempty"` - Cmd string `json:"cmd,omitempty"` - Created string `json:"created,omitempty"` - Jobinstanceid string `json:"jobinstanceid,omitempty"` - Jobinstancetype string `json:"jobinstancetype,omitempty"` - Jobprocstatus int `json:"jobprocstatus,omitempty"` - Jobresult json.RawMessage `json:"jobresult,omitempty"` - Jobresultcode int `json:"jobresultcode,omitempty"` - Jobresulttype string `json:"jobresulttype,omitempty"` - Jobstatus int `json:"jobstatus,omitempty"` - Userid string `json:"userid,omitempty"` -} - -type ListVirtualMachinesResponse struct { - Count int `json:"count"` - VirtualMachines []*VirtualMachine `json:"virtualmachine"` -} - -type VirtualMachine struct { - Account string `json:"account,omitempty"` - Cpunumber int `json:"cpunumber,omitempty"` - Cpuspeed int `json:"cpuspeed,omitempty"` - Cpuused string `json:"cpuused,omitempty"` - Created string `json:"created,omitempty"` - Details map[string]string `json:"details,omitempty"` - Diskioread int64 `json:"diskioread,omitempty"` - Diskiowrite int64 `json:"diskiowrite,omitempty"` - Diskkbsread int64 `json:"diskkbsread,omitempty"` - Diskkbswrite int64 `json:"diskkbswrite,omitempty"` - Displayname string `json:"displayname,omitempty"` - Displayvm bool `json:"displayvm,omitempty"` - Domain string `json:"domain,omitempty"` - Domainid string `json:"domainid,omitempty"` - Forvirtualnetwork bool `json:"forvirtualnetwork,omitempty"` - Group string `json:"group,omitempty"` - Groupid string `json:"groupid,omitempty"` - Guestosid string `json:"guestosid,omitempty"` - Haenable bool `json:"haenable,omitempty"` - Hostid string `json:"hostid,omitempty"` - Hostname string `json:"hostname,omitempty"` - Hypervisor string `json:"hypervisor,omitempty"` - Id string `json:"id,omitempty"` - Instancename string `json:"instancename,omitempty"` - Isdynamicallyscalable bool `json:"isdynamicallyscalable,omitempty"` - Isodisplaytext string `json:"isodisplaytext,omitempty"` - Isoid string `json:"isoid,omitempty"` - Isoname string `json:"isoname,omitempty"` - Keypair string `json:"keypair,omitempty"` - Memory int `json:"memory,omitempty"` - Name string `json:"name,omitempty"` - Networkkbsread int64 `json:"networkkbsread,omitempty"` - Networkkbswrite int64 `json:"networkkbswrite,omitempty"` - Nic []struct { - Broadcasturi string `json:"broadcasturi,omitempty"` - Gateway string `json:"gateway,omitempty"` - Id string `json:"id,omitempty"` - Ip6address string `json:"ip6address,omitempty"` - Ip6cidr string `json:"ip6cidr,omitempty"` - Ip6gateway string `json:"ip6gateway,omitempty"` - Ipaddress string `json:"ipaddress,omitempty"` - Isdefault bool `json:"isdefault,omitempty"` - Isolationuri string `json:"isolationuri,omitempty"` - Macaddress string `json:"macaddress,omitempty"` - Netmask string `json:"netmask,omitempty"` - Networkid string `json:"networkid,omitempty"` - Networkname string `json:"networkname,omitempty"` - Secondaryip []struct { - Id string `json:"id,omitempty"` - IpAddress string `json:"ipaddress,omitempty"` - } `json:"secondaryip,omitempty"` - Traffictype string `json:"traffictype,omitempty"` - Type string `json:"type,omitempty"` - } `json:"nic,omitempty"` - Password string `json:"password,omitempty"` - Passwordenabled bool `json:"passwordenabled,omitempty"` - Project string `json:"project,omitempty"` - Projectid string `json:"projectid,omitempty"` - Publicip string `json:"publicip,omitempty"` - Publicipid string `json:"publicipid,omitempty"` - Rootdeviceid int64 `json:"rootdeviceid,omitempty"` - Rootdevicetype string `json:"rootdevicetype,omitempty"` - SecurityGroups []struct { - Account string `json:"account,omitempty"` - Description string `json:"description,omitempty"` - Id string `json:"id,omitempty"` - Name string `json:"name,omitemtpy"` - Tags []string `json:"tags,omitempty"` - } `json:"securitygroup,omitempty"` - Serviceofferingid string `json:"serviceofferingid,omitempty"` - Serviceofferingname string `json:"serviceofferingname,omitempty"` - Servicestate string `json:"servicestate,omitempty"` - State string `json:"state,omitempty"` - Templatedisplaytext string `json:"templatedisplaytext,omitempty"` - Templateid string `json:"templateid,omitempty"` - Templatename string `json:"templatename,omitempty"` - Zoneid string `json:"zoneid,omitempty"` - Zonename string `json:"zonename,omitempty"` -} - -type StartVirtualMachineResponse struct { - JobID string `json:"jobid,omitempty"` -} - -type StopVirtualMachineResponse struct { - JobID string `json:"jobid,omitempty"` -} - -type DestroyVirtualMachineResponse struct { - JobID string `json:"jobid,omitempty"` -} - -type RebootVirtualMachineResponse struct { - JobID string `json:"jobid,omitempty"` -} - -type CreateSSHKeyPairWrappedResponse struct { - Wrapped CreateSSHKeyPairResponse `json:"keypair,omitempty"` -} - -type CreateSSHKeyPairResponse struct { - Privatekey string `json:"privatekey,omitempty"` -} - -type RemoveIpFromNicResponse struct { - JobID string `json:"jobid,omitempty"` -} - -type AddIpToNicResponse struct { - Id string `json:"id"` - IpAddress string `json:"ipaddress"` - NetworkId string `json:"networkid"` - NicId string `json:"nicid"` - VmId string `json:"virtualmachineid"` -} - -type CreateAffinityGroupResponse struct { - JobId string `json:"jobid,omitempty"` -} - -type DeleteAffinityGroupResponse struct { - JobId string `json:"jobid,omitempty"` -} - -type DeleteSSHKeyPairResponse struct { - Privatekey string `json:"privatekey,omitempty"` -} - -type DNSDomain struct { - Id int64 `json:"id"` - UserId int64 `json:"user_id"` - RegistrantId int64 `json:"registrant_id,omitempty"` - Name string `json:"name"` - UnicodeName string `json:"unicode_name"` - Token string `json:"token"` - State string `json:"state"` - Language string `json:"language,omitempty"` - Lockable bool `json:"lockable"` - AutoRenew bool `json:"auto_renew"` - WhoisProtected bool `json:"whois_protected"` - RecordCount int64 `json:"record_count"` - ServiceCount int64 `json:"service_count"` - ExpiresOn string `json:"expires_on,omitempty"` - CreatedAt string `json:"created_at"` - UpdatedAt string `json:"updated_at"` -} - -type DNSDomainCreateRequest struct { - Domain struct { - Name string `json:"name"` - } `json:"domain"` -} - -type DNSRecord struct { - Id int64 `json:"id,omitempty"` - DomainId int64 `json:"domain_id,omitempty"` - Name string `json:"name"` - Ttl int `json:"ttl,omitempty"` - CreatedAt string `json:"created_at,omitempty"` - UpdatedAt string `json:"updated_at,omitempty"` - Content string `json:"content"` - RecordType string `json:"record_type"` - Prio int `json:"prio,omitempty"` -} - -type DNSRecordResponse struct { - Record DNSRecord `json:"record"` -} - -type DNSError struct { - Name []string `json:"name"` -} diff --git a/vendor/github.com/exoscale/egoscale/users.go b/vendor/github.com/exoscale/egoscale/users.go new file mode 100644 index 000000000..353e4b6ba --- /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 new file mode 100644 index 000000000..168aa07c0 --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/virtual_machines.go @@ -0,0 +1,514 @@ +package egoscale + +import "net" + +// VirtualMachine reprents a virtual machine +type VirtualMachine struct { + ID string `json:"id,omitempty"` + Account string `json:"account,omitempty"` + ClusterID string `json:"clusterid,omitempty"` + ClusterName string `json:"clustername,omitempty"` + CPUNumber int64 `json:"cpunumber,omitempty"` + CPUSpeed int64 `json:"cpuspeed,omitempty"` + CPUUsed string `json:"cpuused,omitempty"` + Created string `json:"created,omitempty"` + Details map[string]string `json:"details,omitempty"` + DiskIoRead int64 `json:"diskioread,omitempty"` + DiskIoWrite int64 `json:"diskiowrite,omitempty"` + DiskKbsRead int64 `json:"diskkbsread,omitempty"` + DiskKbsWrite int64 `json:"diskkbswrite,omitempty"` + DiskOfferingID string `json:"diskofferingid,omitempty"` + DiskOfferingName string `json:"diskofferingname,omitempty"` + DisplayName string `json:"displayname,omitempty"` + DisplayVM bool `json:"displayvm,omitempty"` + Domain string `json:"domain,omitempty"` + DomainID string `json:"domainid,omitempty"` + ForVirtualNetwork bool `json:"forvirtualnetwork,omitempty"` + Group string `json:"group,omitempty"` + GroupID string `json:"groupid,omitempty"` + GuestOsID string `json:"guestosid,omitempty"` + HaEnable bool `json:"haenable,omitempty"` + HostID string `json:"hostid,omitempty"` + HostName string `json:"hostname,omitempty"` + Hypervisor string `json:"hypervisor,omitempty"` + InstanceName string `json:"instancename,omitempty"` // root only + IsDynamicallyScalable bool `json:"isdynamicallyscalable,omitempty"` + IsoDisplayText string `json:"isodisplaytext,omitempty"` + IsoID string `json:"isoid,omitempty"` + IsoName string `json:"isoname,omitempty"` + KeyPair string `json:"keypair,omitempty"` + Memory int64 `json:"memory,omitempty"` + MemoryIntFreeKbs int64 `json:"memoryintfreekbs,omitempty"` + MemoryKbs int64 `json:"memorykbs,omitempty"` + MemoryTargetKbs int64 `json:"memorytargetkbs,omitempty"` + Name string `json:"name,omitempty"` + NetworkKbsRead int64 `json:"networkkbsread,omitempty"` + NetworkKbsWrite int64 `json:"networkkbswrite,omitempty"` + OsCategoryID string `json:"oscategoryid,omitempty"` + OsTypeID int64 `json:"ostypeid,omitempty"` + Password string `json:"password,omitempty"` + PasswordEnabled bool `json:"passwordenabled,omitempty"` + PCIDevices string `json:"pcidevices,omitempty"` // not in the doc + PodID string `json:"podid,omitempty"` + PodName string `json:"podname,omitempty"` + Project string `json:"project,omitempty"` + ProjectID string `json:"projectid,omitempty"` + PublicIP string `json:"publicip,omitempty"` + PublicIPID string `json:"publicipid,omitempty"` + RootDeviceID int64 `json:"rootdeviceid,omitempty"` + RootDeviceType string `json:"rootdevicetype,omitempty"` + ServiceOfferingID string `json:"serviceofferingid,omitempty"` + ServiceOfferingName string `json:"serviceofferingname,omitempty"` + ServiceState string `json:"servicestate,omitempty"` + State string `json:"state,omitempty"` + TemplateDisplayText string `json:"templatedisplaytext,omitempty"` + TemplateID string `json:"templateid,omitempty"` + TemplateName string `json:"templatename,omitempty"` + UserID string `json:"userid,omitempty"` // not in the doc + UserName string `json:"username,omitempty"` // not in the doc + Vgpu string `json:"vgpu,omitempty"` // not in the doc + ZoneID string `json:"zoneid,omitempty"` + ZoneName string `json:"zonename,omitempty"` + AffinityGroup []AffinityGroup `json:"affinitygroup,omitempty"` + Nic []Nic `json:"nic,omitempty"` + SecurityGroup []SecurityGroup `json:"securitygroup,omitempty"` + Tags []ResourceTag `json:"tags,omitempty"` + JobID string `json:"jobid,omitempty"` + JobStatus JobStatusType `json:"jobstatus,omitempty"` +} + +// ResourceType returns the type of the resource +func (*VirtualMachine) ResourceType() string { + return "UserVM" +} + +// NicsByType returns the corresponding interfaces base on the given type +func (vm *VirtualMachine) NicsByType(nicType string) []Nic { + nics := make([]Nic, 0) + for _, nic := range vm.Nic { + if nic.Type == nicType { + // XXX The CloudStack API forgets to specify it + nic.VirtualMachineID = vm.ID + nics = append(nics, nic) + } + } + return nics +} + +// NicByNetworkID returns the corresponding interface based on the given NetworkID +func (vm *VirtualMachine) NicByNetworkID(networkID string) *Nic { + for _, nic := range vm.Nic { + if nic.NetworkID == networkID { + nic.VirtualMachineID = vm.ID + return &nic + } + } + return nil +} + +// NicByID returns the corresponding interface base on its ID +func (vm *VirtualMachine) NicByID(nicID string) *Nic { + for _, nic := range vm.Nic { + if nic.ID == nicID { + nic.VirtualMachineID = vm.ID + return &nic + } + } + + return nil +} + +// IPToNetwork represents a mapping between ip and networks +type IPToNetwork struct { + IP string `json:"ip,omitempty"` + IPV6 string `json:"ipv6,omitempty"` + NetworkID string `json:"networkid,omitempty"` +} + +// VirtualMachineResponse represents a generic Virtual Machine response +type VirtualMachineResponse struct { + VirtualMachine VirtualMachine `json:"virtualmachine"` +} + +// DeployVirtualMachine (Async) represents the machine creation +// +// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/deployVirtualMachine.html +type DeployVirtualMachine struct { + ServiceOfferingID string `json:"serviceofferingid"` + TemplateID string `json:"templateid"` + ZoneID string `json:"zoneid"` + Account string `json:"account,omitempty"` + AffinityGroupIDs []string `json:"affinitygroupids,omitempty"` + AffinityGroupNames []string `json:"affinitygroupnames,omitempty"` + CustomID string `json:"customid,omitempty"` // root only + DeploymentPlanner string `json:"deploymentplanner,omitempty"` // root only + Details map[string]string `json:"details,omitempty"` + DiskOfferingID string `json:"diskofferingid,omitempty"` + DisplayName string `json:"displayname,omitempty"` + DisplayVM bool `json:"displayvm,omitempty"` + DomainID string `json:"domainid,omitempty"` + Group string `json:"group,omitempty"` + HostID string `json:"hostid,omitempty"` + Hypervisor string `json:"hypervisor,omitempty"` + IP6Address net.IP `json:"ip6address,omitempty"` + IPAddress net.IP `json:"ipaddress,omitempty"` + IPToNetworkList []IPToNetwork `json:"iptonetworklist,omitempty"` + Keyboard string `json:"keyboard,omitempty"` + KeyPair string `json:"keypair,omitempty"` + Name string `json:"name,omitempty"` + NetworkIDs []string `json:"networkids,omitempty"` // mutually exclusive with IPToNetworkList + ProjectID string `json:"projectid,omitempty"` + RootDiskSize int64 `json:"rootdisksize,omitempty"` // in GiB + 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"` + UserData string `json:"userdata,omitempty"` // the client is responsible to base64/gzip it +} + +func (*DeployVirtualMachine) name() string { + return "deployVirtualMachine" +} + +func (*DeployVirtualMachine) asyncResponse() interface{} { + return new(DeployVirtualMachineResponse) +} + +// DeployVirtualMachineResponse represents a deployed VM instance +type DeployVirtualMachineResponse VirtualMachineResponse + +// StartVirtualMachine (Async) represents the creation of the virtual machine +// +// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/startVirtualMachine.html +type StartVirtualMachine struct { + ID string `json:"id"` + DeploymentPlanner string `json:"deploymentplanner,omitempty"` // root only + HostID string `json:"hostid,omitempty"` // root only +} + +func (*StartVirtualMachine) name() string { + return "startVirtualMachine" +} +func (*StartVirtualMachine) asyncResponse() interface{} { + return new(StartVirtualMachineResponse) +} + +// StartVirtualMachineResponse represents a started VM instance +type StartVirtualMachineResponse VirtualMachineResponse + +// StopVirtualMachine (Async) represents the stopping of the virtual machine +// +// 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"` +} + +func (*StopVirtualMachine) name() string { + return "stopVirtualMachine" +} + +func (*StopVirtualMachine) asyncResponse() interface{} { + return new(StopVirtualMachineResponse) +} + +// StopVirtualMachineResponse represents a stopped VM instance +type StopVirtualMachineResponse VirtualMachineResponse + +// RebootVirtualMachine (Async) represents the rebooting of the virtual machine +// +// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/rebootVirtualMachine.html +type RebootVirtualMachine struct { + ID string `json:"id"` +} + +func (*RebootVirtualMachine) name() string { + return "rebootVirtualMachine" +} + +func (*RebootVirtualMachine) asyncResponse() interface{} { + return new(RebootVirtualMachineResponse) +} + +// RebootVirtualMachineResponse represents a rebooted VM instance +type RebootVirtualMachineResponse VirtualMachineResponse + +// RestoreVirtualMachine (Async) represents the restoration of the virtual machine +// +// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/restoreVirtualMachine.html +type RestoreVirtualMachine struct { + VirtualMachineID string `json:"virtualmachineid"` + TemplateID string `json:"templateid,omitempty"` +} + +func (*RestoreVirtualMachine) name() string { + return "restoreVirtualMachine" +} + +func (*RestoreVirtualMachine) asyncResponse() interface{} { + return new(RestoreVirtualMachineResponse) +} + +// RestoreVirtualMachineResponse represents a restored VM instance +type RestoreVirtualMachineResponse VirtualMachineResponse + +// RecoverVirtualMachine represents the restoration of the virtual machine +// +// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/recoverVirtualMachine.html +type RecoverVirtualMachine struct { + ID string `json:"virtualmachineid"` +} + +func (*RecoverVirtualMachine) name() string { + return "recoverVirtualMachine" +} + +func (*RecoverVirtualMachine) response() interface{} { + return new(RecoverVirtualMachineResponse) +} + +// RecoverVirtualMachineResponse represents a recovered VM instance +type RecoverVirtualMachineResponse VirtualMachineResponse + +// DestroyVirtualMachine (Async) represents the destruction of the virtual machine +// +// 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"` +} + +func (*DestroyVirtualMachine) name() string { + return "destroyVirtualMachine" +} + +func (*DestroyVirtualMachine) asyncResponse() interface{} { + return new(DestroyVirtualMachineResponse) +} + +// DestroyVirtualMachineResponse represents a destroyed VM instance +type DestroyVirtualMachineResponse VirtualMachineResponse + +// UpdateVirtualMachine represents the update of the virtual machine +// +// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/updateVirtualMachine.html +type UpdateVirtualMachine struct { + ID string `json:"id"` + CustomID string `json:"customid,omitempty"` // root only + Details map[string]string `json:"details,omitempty"` + DisplayName string `json:"displayname,omitempty"` + DisplayVM bool `json:"displayvm,omitempty"` + Group string `json:"group,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"` + UserData string `json:"userdata,omitempty"` +} + +func (*UpdateVirtualMachine) name() string { + return "updateVirtualMachine" +} + +func (*UpdateVirtualMachine) response() interface{} { + return new(UpdateVirtualMachineResponse) +} + +// UpdateVirtualMachineResponse represents an updated VM instance +type UpdateVirtualMachineResponse VirtualMachineResponse + +// ExpungeVirtualMachine represents the annihilation of a VM +type ExpungeVirtualMachine struct { + ID string `json:"id"` +} + +func (*ExpungeVirtualMachine) name() string { + return "expungeVirtualMachine" +} + +func (*ExpungeVirtualMachine) asyncResponse() interface{} { + return new(booleanAsyncResponse) +} + +// ScaleVirtualMachine (Async) represents the scaling of a VM +// +// ChangeServiceForVirtualMachine does the same thing but returns the +// new Virtual Machine which is more consistent with the rest of the API. +// +// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/scaleVirtualMachine.html +type ScaleVirtualMachine struct { + ID string `json:"id"` + ServiceOfferingID string `json:"serviceofferingid"` + Details map[string]string `json:"details,omitempty"` +} + +func (*ScaleVirtualMachine) name() string { + return "scaleVirtualMachine" +} + +func (*ScaleVirtualMachine) asyncResponse() interface{} { + return new(booleanAsyncResponse) +} + +// ChangeServiceForVirtualMachine represents the scaling of a VM +// +// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/changeServiceForVirtualMachine.html +type ChangeServiceForVirtualMachine ScaleVirtualMachine + +func (*ChangeServiceForVirtualMachine) name() string { + return "changeServiceForVirtualMachine" +} + +func (*ChangeServiceForVirtualMachine) response() interface{} { + return new(ChangeServiceForVirtualMachineResponse) +} + +// ChangeServiceForVirtualMachineResponse represents an changed VM instance +type ChangeServiceForVirtualMachineResponse VirtualMachineResponse + +// ResetPasswordForVirtualMachine (Async) represents the scaling of a VM +// +// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/resetPasswordForVirtualMachine.html +type ResetPasswordForVirtualMachine ScaleVirtualMachine + +func (*ResetPasswordForVirtualMachine) name() string { + return "resetPasswordForVirtualMachine" +} + +func (*ResetPasswordForVirtualMachine) asyncResponse() interface{} { + return new(ResetPasswordForVirtualMachineResponse) +} + +// ResetPasswordForVirtualMachineResponse represents the updated vm +type ResetPasswordForVirtualMachineResponse VirtualMachineResponse + +// GetVMPassword asks for an encrypted password +// +// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/getVMPassword.html +type GetVMPassword struct { + ID string `json:"id"` +} + +func (*GetVMPassword) name() string { + return "getVMPassword" +} + +func (*GetVMPassword) response() interface{} { + return new(GetVMPasswordResponse) +} + +// GetVMPasswordResponse represents the encrypted password +type GetVMPasswordResponse struct { + // Base64 encrypted password for the VM + EncryptedPassword string `json:"encryptedpassword"` +} + +// ListVirtualMachines represents a search for a VM +// +// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/listVirtualMachine.html +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 + DomainID string `json:"domainid,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"` + KeyPair string `json:"keypair,omitempty"` + Keyword string `json:"keyword,omitempty"` + ListAll bool `json:"listall,omitempty"` + Name string `json:"name,omitempty"` + NetworkID string `json:"networkid,omitempty"` + Page int `json:"page,omitempty"` + PageSize int `json:"pagesize,omitempty"` + PodID string `json:"podid,omitempty"` + ProjectID string `json:"projectid,omitempty"` + ServiceOfferindID string `json:"serviceofferingid,omitempty"` + State string `json:"state,omitempty"` // Running, Stopped, Present, ... + StorageID string `json:"storageid,omitempty"` + Tags []ResourceTag `json:"tags,omitempty"` + TemplateID string `json:"templateid,omitempty"` + UserID string `json:"userid,omitempty"` + VpcID string `json:"vpcid,omitempty"` + ZoneID string `json:"zoneid,omitempty"` +} + +func (*ListVirtualMachines) name() string { + return "listVirtualMachines" +} + +func (*ListVirtualMachines) response() interface{} { + return new(ListVirtualMachinesResponse) +} + +// ListVirtualMachinesResponse represents a list of virtual machines +type ListVirtualMachinesResponse struct { + Count int `json:"count"` + VirtualMachine []VirtualMachine `json:"virtualmachine"` +} + +// AddNicToVirtualMachine (Async) adds a NIC to a VM +// +// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/addNicToVirtualMachine.html +type AddNicToVirtualMachine struct { + NetworkID string `json:"networkid"` + VirtualMachineID string `json:"virtualmachineid"` + IPAddress net.IP `json:"ipaddress,omitempty"` +} + +func (*AddNicToVirtualMachine) name() string { + return "addNicToVirtualMachine" +} + +func (*AddNicToVirtualMachine) asyncResponse() interface{} { + return new(AddNicToVirtualMachineResponse) +} + +// AddNicToVirtualMachineResponse represents the modified VM +type AddNicToVirtualMachineResponse VirtualMachineResponse + +// RemoveNicFromVirtualMachine (Async) removes a NIC from a VM +// +// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/removeNicFromVirtualMachine.html +type RemoveNicFromVirtualMachine struct { + NicID string `json:"nicid"` + VirtualMachineID string `json:"virtualmachineid"` +} + +func (*RemoveNicFromVirtualMachine) name() string { + return "removeNicFromVirtualMachine" +} + +func (*RemoveNicFromVirtualMachine) asyncResponse() interface{} { + return new(RemoveNicFromVirtualMachineResponse) +} + +// RemoveNicFromVirtualMachineResponse represents the modified VM +type RemoveNicFromVirtualMachineResponse VirtualMachineResponse + +// UpdateDefaultNicForVirtualMachine (Async) adds a NIC to a VM +// +// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/updateDefaultNicForVirtualMachine.html +type UpdateDefaultNicForVirtualMachine struct { + NetworkID string `json:"networkid"` + VirtualMachineID string `json:"virtualmachineid"` + IPAddress net.IP `json:"ipaddress,omitempty"` +} + +func (*UpdateDefaultNicForVirtualMachine) name() string { + return "updateDefaultNicForVirtualMachine" +} + +func (*UpdateDefaultNicForVirtualMachine) asyncResponse() interface{} { + return new(UpdateDefaultNicForVirtualMachineResponse) +} + +// UpdateDefaultNicForVirtualMachineResponse represents the modified VM +type UpdateDefaultNicForVirtualMachineResponse VirtualMachineResponse diff --git a/vendor/github.com/exoscale/egoscale/vm.go b/vendor/github.com/exoscale/egoscale/vm.go deleted file mode 100644 index 659cbce8a..000000000 --- a/vendor/github.com/exoscale/egoscale/vm.go +++ /dev/null @@ -1,159 +0,0 @@ -package egoscale - -import ( - "encoding/base64" - "encoding/json" - "fmt" - "net/url" - "strings" -) - -func (exo *Client) CreateVirtualMachine(p MachineProfile) (string, error) { - - params := url.Values{} - params.Set("serviceofferingid", p.ServiceOffering) - params.Set("templateid", p.Template) - params.Set("zoneid", p.Zone) - - params.Set("displayname", p.Name) - if len(p.Userdata) > 0 { - params.Set("userdata", base64.StdEncoding.EncodeToString([]byte(p.Userdata))) - } - if len(p.Keypair) > 0 { - params.Set("keypair", p.Keypair) - } - if len(p.AffinityGroups) > 0 { - params.Set("affinitygroupnames", strings.Join(p.AffinityGroups, ",")) - } - - params.Set("securitygroupids", strings.Join(p.SecurityGroups, ",")) - - resp, err := exo.Request("deployVirtualMachine", params) - - if err != nil { - return "", err - } - - var r DeployVirtualMachineResponse - - if err := json.Unmarshal(resp, &r); err != nil { - return "", err - } - - return r.JobID, nil -} - -func (exo *Client) StartVirtualMachine(id string) (string, error) { - params := url.Values{} - params.Set("id", id) - - resp, err := exo.Request("startVirtualMachine", params) - - if err != nil { - return "", err - } - - var r StartVirtualMachineResponse - - if err := json.Unmarshal(resp, &r); err != nil { - return "", err - } - - return r.JobID, nil -} - -func (exo *Client) StopVirtualMachine(id string) (string, error) { - params := url.Values{} - params.Set("id", id) - - resp, err := exo.Request("stopVirtualMachine", params) - - if err != nil { - return "", err - } - - var r StopVirtualMachineResponse - - if err := json.Unmarshal(resp, &r); err != nil { - return "", err - } - - return r.JobID, nil -} - -func (exo *Client) RebootVirtualMachine(id string) (string, error) { - params := url.Values{} - params.Set("id", id) - - resp, err := exo.Request("rebootVirtualMachine", params) - - if err != nil { - return "", err - } - - var r RebootVirtualMachineResponse - - if err := json.Unmarshal(resp, &r); err != nil { - return "", err - } - - return r.JobID, nil -} - -func (exo *Client) DestroyVirtualMachine(id string) (string, error) { - params := url.Values{} - params.Set("id", id) - - resp, err := exo.Request("destroyVirtualMachine", params) - - if err != nil { - return "", err - } - - var r DestroyVirtualMachineResponse - - if err := json.Unmarshal(resp, &r); err != nil { - return "", err - } - - return r.JobID, nil -} - -func (exo *Client) GetVirtualMachine(id string) (*VirtualMachine, error) { - - params := url.Values{} - params.Set("id", id) - - resp, err := exo.Request("listVirtualMachines", params) - if err != nil { - return nil, err - } - - var r ListVirtualMachinesResponse - - if err := json.Unmarshal(resp, &r); err != nil { - return nil, err - } - - if len(r.VirtualMachines) == 1 { - machine := r.VirtualMachines[0] - return machine, nil - } else { - return nil, fmt.Errorf("cannot retrieve virtualmachine with id %s", id) - } -} - -func (exo *Client) ListVirtualMachines() ([]*VirtualMachine, error) { - - resp, err := exo.Request("listVirtualMachines", url.Values{}) - if err != nil { - return nil, err - } - - var r ListVirtualMachinesResponse - if err := json.Unmarshal(resp, &r); err != nil { - return nil, err - } - - return r.VirtualMachines, nil -} 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 000000000..511d707d0 --- /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 new file mode 100644 index 000000000..63d8b47f1 --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/volumes.go @@ -0,0 +1,121 @@ +package egoscale + +import ( + "fmt" +) + +// Volume represents a volume linked to a VM +type Volume struct { + ID string `json:"id"` + Account string `json:"account,omitempty"` + Attached string `json:"attached,omitempty"` + ChainInfo string `json:"chaininfo,omitempty"` + Created string `json:"created,omitempty"` + Destroyed bool `json:"destroyed,omitempty"` + DisplayVolume bool `json:"displayvolume,omitempty"` + Domain string `json:"domain,omitempty"` + DomainID string `json:"domainid,omitempty"` + Name string `json:"name,omitempty"` + QuiesceVM bool `json:"quiescevm,omitempty"` + ServiceOfferingDisplayText string `json:"serviceofferingdisplaytext,omitempty"` + ServiceOfferingID string `json:"serviceofferingid,omitempty"` + ServiceOfferingName string `json:"serviceofferingname,omitempty"` + Size uint64 `json:"size,omitempty"` + State string `json:"state,omitempty"` + Type string `json:"type,omitempty"` + VirtualMachineID string `json:"virtualmachineid,omitempty"` + VMName string `json:"vmname,omitempty"` + VMState string `json:"vmstate,omitempty"` + ZoneID string `json:"zoneid,omitempty"` + ZoneName string `json:"zonename,omitempty"` + Tags []ResourceTag `json:"tags,omitempty"` + JobID string `json:"jobid,omitempty"` + 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"` + Size int64 `json:"size,omitempty"` // in GiB +} + +func (*ResizeVolume) name() string { + return "resizeVolume" +} + +func (*ResizeVolume) asyncResponse() interface{} { + return new(ResizeVolumeResponse) +} + +// ResizeVolumeResponse represents the new Volume +type ResizeVolumeResponse struct { + Volume Volume `json:"volume"` +} + +// ListVolumes represents a query listing volumes +// +// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/listVolumes.html +type ListVolumes struct { + Account string `json:"account,omitempty"` + DiskOfferingID string `json:"diskoffering,omitempty"` + DisplayVolume string `json:"displayvolume,omitempty"` // root only + DomainID string `json:"domainid,omitempty"` + HostID string `json:"hostid,omitempty"` + ID string `json:"id,omitempty"` + IsRecursive bool `json:"isrecursive,omitempty"` + Keyword string `json:"keyword,omitempty"` + ListAll bool `json:"listall,omitempty"` + Name string `json:"name,omitempty"` + Page int `json:"page,omitempty"` + PageSize int `json:"pagesize,omitempty"` + PodID string `json:"podid,omitempty"` + ProjectID string `json:"projectid,omitempty"` + StorageID string `json:"storageid,omitempty"` + Tags []ResourceTag `json:"tags,omitempty"` + Type string `json:"type,omitempty"` + VirtualMachineID string `json:"virtualmachineid,omitempty"` + ZoneID string `json:"zoneid,omitempty"` +} + +func (*ListVolumes) name() string { + return "listVolumes" +} + +func (*ListVolumes) response() interface{} { + return new(ListVolumesResponse) +} + +// ListVolumesResponse represents a list of volumes +type ListVolumesResponse struct { + Count int `json:"count"` + Volume []Volume `json:"volume"` +} + +// GetRootVolumeForVirtualMachine returns the root volume of a VM +// +// Deprecated: helper function shouldn't be used +func (exo *Client) GetRootVolumeForVirtualMachine(virtualMachineID string) (*Volume, error) { + resp, err := exo.Request(&ListVolumes{ + VirtualMachineID: virtualMachineID, + Type: "ROOT", + }) + if err != nil { + return nil, err + } + + r := resp.(*ListVolumesResponse) + if r.Count != 1 { + return nil, fmt.Errorf("Expected exactly one volume for %v, got %d", virtualMachineID, r.Count) + } + + return &(r.Volume[0]), nil +} diff --git a/vendor/github.com/exoscale/egoscale/zones.go b/vendor/github.com/exoscale/egoscale/zones.go new file mode 100644 index 000000000..228a7e369 --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/zones.go @@ -0,0 +1,60 @@ +package egoscale + +import "net" + +// Zone represents a data center +type Zone struct { + ID string `json:"id"` + AllocationState string `json:"allocationstate,omitempty"` + Capacity string `json:"capacity,omitempty"` + Description string `json:"description,omitempty"` + DhcpProvider string `json:"dhcpprovider,omitempty"` + DisplayText string `json:"displaytext,omitempty"` + DNS1 net.IP `json:"dns1,omitempty"` + DNS2 net.IP `json:"dns2,omitempty"` + Domain string `json:"domain,omitempty"` + DomainID string `json:"domainid,omitempty"` + DomainName string `json:"domainname,omitempty"` + GuestCidrAddress string `json:"guestcidraddress,omitempty"` + InternalDNS1 net.IP `json:"internaldns1,omitempty"` + InternalDNS2 net.IP `json:"internaldns2,omitempty"` + IP6DNS1 net.IP `json:"ip6dns1,omitempty"` + IP6DNS2 net.IP `json:"ip6dns2,omitempty"` + LocalStorageEnabled bool `json:"localstorageenabled,omitempty"` + Name string `json:"name,omitempty"` + NetworkType string `json:"networktype,omitempty"` + ResourceDetails map[string]string `json:"resourcedetails,omitempty"` + SecurityGroupsEnabled bool `json:"securitygroupsenabled,omitempty"` + Vlan string `json:"vlan,omitempty"` + ZoneToken string `json:"zonetoken,omitempty"` + Tags []ResourceTag `json:"tags,omitempty"` +} + +// ListZones represents a query for zones +// +// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/listZones.html +type ListZones struct { + 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"` + Tags []ResourceTag `json:"tags,omitempty"` +} + +func (*ListZones) name() string { + return "listZones" +} + +func (*ListZones) response() interface{} { + return new(ListZonesResponse) +} + +// ListZonesResponse represents a list of zones +type ListZonesResponse struct { + Count int `json:"count"` + Zone []Zone `json:"zone"` +} diff --git a/vendor/github.com/xenolf/lego/acme/http.go b/vendor/github.com/xenolf/lego/acme/http.go index fd6018a10..e469e0de2 100644 --- a/vendor/github.com/xenolf/lego/acme/http.go +++ b/vendor/github.com/xenolf/lego/acme/http.go @@ -18,6 +18,7 @@ var UserAgent string // HTTPClient is an HTTP client with a reasonable timeout value. var HTTPClient = http.Client{ Transport: &http.Transport{ + Proxy: http.ProxyFromEnvironment, Dial: (&net.Dialer{ Timeout: 30 * time.Second, KeepAlive: 30 * time.Second, diff --git a/vendor/github.com/xenolf/lego/providers/dns/cloudxns/cloudxns.go b/vendor/github.com/xenolf/lego/providers/dns/cloudxns/cloudxns.go new file mode 100644 index 000000000..57efdeaec --- /dev/null +++ b/vendor/github.com/xenolf/lego/providers/dns/cloudxns/cloudxns.go @@ -0,0 +1,214 @@ +// Package cloudxns implements a DNS provider for solving the DNS-01 challenge +// using cloudxns DNS. +package cloudxns + +import ( + "bytes" + "crypto/md5" + "encoding/hex" + "encoding/json" + "fmt" + "net/http" + "os" + "strconv" + "time" + + "github.com/xenolf/lego/acme" +) + +const cloudXNSBaseURL = "https://www.cloudxns.net/api2/" + +// DNSProvider is an implementation of the acme.ChallengeProvider interface +type DNSProvider struct { + apiKey string + secretKey string +} + +// NewDNSProvider returns a DNSProvider instance configured for cloudxns. +// Credentials must be passed in the environment variables: CLOUDXNS_API_KEY +// and CLOUDXNS_SECRET_KEY. +func NewDNSProvider() (*DNSProvider, error) { + apiKey := os.Getenv("CLOUDXNS_API_KEY") + secretKey := os.Getenv("CLOUDXNS_SECRET_KEY") + return NewDNSProviderCredentials(apiKey, secretKey) +} + +// NewDNSProviderCredentials uses the supplied credentials to return a +// DNSProvider instance configured for cloudxns. +func NewDNSProviderCredentials(apiKey, secretKey string) (*DNSProvider, error) { + if apiKey == "" || secretKey == "" { + return nil, fmt.Errorf("CloudXNS credentials missing") + } + + return &DNSProvider{ + apiKey: apiKey, + secretKey: secretKey, + }, nil +} + +// Present creates a TXT record to fulfil the dns-01 challenge. +func (c *DNSProvider) Present(domain, token, keyAuth string) error { + fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) + zoneID, err := c.getHostedZoneID(fqdn) + if err != nil { + return err + } + + return c.addTxtRecord(zoneID, fqdn, value, ttl) +} + +// CleanUp removes the TXT record matching the specified parameters. +func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { + fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + zoneID, err := c.getHostedZoneID(fqdn) + if err != nil { + return err + } + + recordID, err := c.findTxtRecord(zoneID, fqdn) + if err != nil { + return err + } + + return c.delTxtRecord(recordID, zoneID) +} + +func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) { + type Data struct { + ID string `json:"id"` + Domain string `json:"domain"` + } + + authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) + if err != nil { + return "", err + } + + result, err := c.makeRequest("GET", "domain", nil) + if err != nil { + return "", err + } + + var domains []Data + err = json.Unmarshal(result, &domains) + if err != nil { + return "", err + } + + for _, data := range domains { + if data.Domain == authZone { + return data.ID, nil + } + } + + return "", fmt.Errorf("Zone %s not found in cloudxns for domain %s", authZone, fqdn) +} + +func (c *DNSProvider) findTxtRecord(zoneID, fqdn string) (string, error) { + result, err := c.makeRequest("GET", fmt.Sprintf("record/%s?host_id=0&offset=0&row_num=2000", zoneID), nil) + if err != nil { + return "", err + } + + var records []cloudXNSRecord + err = json.Unmarshal(result, &records) + if err != nil { + return "", err + } + + for _, record := range records { + if record.Host == acme.UnFqdn(fqdn) && record.Type == "TXT" { + return record.RecordID, nil + } + } + + return "", fmt.Errorf("No existing record found for %s", fqdn) +} + +func (c *DNSProvider) addTxtRecord(zoneID, fqdn, value string, ttl int) error { + id, err := strconv.Atoi(zoneID) + if err != nil { + return err + } + + payload := cloudXNSRecord{ + ID: id, + Host: acme.UnFqdn(fqdn), + Value: value, + Type: "TXT", + LineID: 1, + TTL: ttl, + } + + body, err := json.Marshal(payload) + if err != nil { + return err + } + + _, err = c.makeRequest("POST", "record", body) + if err != nil { + return err + } + + return nil +} + +func (c *DNSProvider) delTxtRecord(recordID, zoneID string) error { + _, err := c.makeRequest("DELETE", fmt.Sprintf("record/%s/%s", recordID, zoneID), nil) + return err +} + +func (c *DNSProvider) hmac(url, date, body string) string { + sum := md5.Sum([]byte(c.apiKey + url + body + date + c.secretKey)) + return hex.EncodeToString(sum[:]) +} + +func (c *DNSProvider) makeRequest(method, uri string, body []byte) (json.RawMessage, error) { + type APIResponse struct { + Code int `json:"code"` + Message string `json:"message"` + Data json.RawMessage `json:"data,omitempty"` + } + + url := cloudXNSBaseURL + uri + req, err := http.NewRequest(method, url, bytes.NewReader(body)) + if err != nil { + return nil, err + } + + requestDate := time.Now().Format(time.RFC1123Z) + + req.Header.Set("API-KEY", c.apiKey) + req.Header.Set("API-REQUEST-DATE", requestDate) + req.Header.Set("API-HMAC", c.hmac(url, requestDate, string(body))) + req.Header.Set("API-FORMAT", "json") + + resp, err := acme.HTTPClient.Do(req) + if err != nil { + return nil, err + } + + defer resp.Body.Close() + + var r APIResponse + err = json.NewDecoder(resp.Body).Decode(&r) + if err != nil { + return nil, err + } + + if r.Code != 1 { + return nil, fmt.Errorf("CloudXNS API Error: %s", r.Message) + } + return r.Data, nil +} + +type cloudXNSRecord struct { + ID int `json:"domain_id,omitempty"` + RecordID string `json:"record_id,omitempty"` + + Host string `json:"host"` + Value string `json:"value"` + Type string `json:"type"` + LineID int `json:"line_id,string"` + TTL int `json:"ttl,string"` +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/dns_providers.go b/vendor/github.com/xenolf/lego/providers/dns/dns_providers.go index d7530f788..062353098 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/dns_providers.go +++ b/vendor/github.com/xenolf/lego/providers/dns/dns_providers.go @@ -8,6 +8,7 @@ import ( "github.com/xenolf/lego/providers/dns/auroradns" "github.com/xenolf/lego/providers/dns/azure" "github.com/xenolf/lego/providers/dns/cloudflare" + "github.com/xenolf/lego/providers/dns/cloudxns" "github.com/xenolf/lego/providers/dns/digitalocean" "github.com/xenolf/lego/providers/dns/dnsimple" "github.com/xenolf/lego/providers/dns/dnsmadeeasy" @@ -15,8 +16,9 @@ import ( "github.com/xenolf/lego/providers/dns/dyn" "github.com/xenolf/lego/providers/dns/exoscale" "github.com/xenolf/lego/providers/dns/gandi" - "github.com/xenolf/lego/providers/dns/godaddy" + "github.com/xenolf/lego/providers/dns/gandiv5" "github.com/xenolf/lego/providers/dns/googlecloud" + "github.com/xenolf/lego/providers/dns/godaddy" "github.com/xenolf/lego/providers/dns/linode" "github.com/xenolf/lego/providers/dns/namecheap" "github.com/xenolf/lego/providers/dns/ns1" @@ -39,6 +41,8 @@ func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error) provider, err = auroradns.NewDNSProvider() case "cloudflare": provider, err = cloudflare.NewDNSProvider() + case "cloudxns": + provider, err = cloudxns.NewDNSProvider() case "digitalocean": provider, err = digitalocean.NewDNSProvider() case "dnsimple": @@ -53,6 +57,8 @@ func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error) provider, err = exoscale.NewDNSProvider() case "gandi": provider, err = gandi.NewDNSProvider() + case "gandiv5": + provider, err = gandiv5.NewDNSProvider() case "gcloud": provider, err = googlecloud.NewDNSProvider() case "godaddy": diff --git a/vendor/github.com/xenolf/lego/providers/dns/exoscale/exoscale.go b/vendor/github.com/xenolf/lego/providers/dns/exoscale/exoscale.go index 7b2fccc98..4b125e8df 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/exoscale/exoscale.go +++ b/vendor/github.com/xenolf/lego/providers/dns/exoscale/exoscale.go @@ -48,25 +48,25 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { return err } - recordId, err := c.FindExistingRecordId(zone, recordName) + recordID, err := c.FindExistingRecordId(zone, recordName) if err != nil { return err } record := egoscale.DNSRecord{ Name: recordName, - Ttl: ttl, + TTL: ttl, Content: value, RecordType: "TXT", } - if recordId == 0 { + if recordID == 0 { _, err := c.client.CreateRecord(zone, record) if err != nil { return errors.New("Error while creating DNS record: " + err.Error()) } } else { - record.Id = recordId + record.ID = recordID _, err := c.client.UpdateRecord(zone, record) if err != nil { return errors.New("Error while updating DNS record: " + err.Error()) @@ -84,17 +84,13 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { return err } - recordId, err := c.FindExistingRecordId(zone, recordName) + recordID, err := c.FindExistingRecordId(zone, recordName) if err != nil { return err } - if recordId != 0 { - record := egoscale.DNSRecord{ - Id: recordId, - } - - err = c.client.DeleteRecord(zone, record) + if recordID != 0 { + err = c.client.DeleteRecord(zone, recordID) if err != nil { return errors.New("Error while deleting DNS record: " + err.Error()) } @@ -106,13 +102,13 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { // Query Exoscale to find an existing record for this name. // Returns nil if no record could be found func (c *DNSProvider) FindExistingRecordId(zone, recordName string) (int64, error) { - responses, err := c.client.GetRecords(zone) + records, err := c.client.GetRecords(zone) if err != nil { return -1, errors.New("Error while retrievening DNS records: " + err.Error()) } - for _, response := range responses { - if response.Record.Name == recordName { - return response.Record.Id, nil + for _, record := range records { + if record.Name == recordName { + return record.ID, nil } } return 0, nil diff --git a/vendor/github.com/xenolf/lego/providers/dns/gandiv5/gandiv5.go b/vendor/github.com/xenolf/lego/providers/dns/gandiv5/gandiv5.go new file mode 100644 index 000000000..86cc7bf3f --- /dev/null +++ b/vendor/github.com/xenolf/lego/providers/dns/gandiv5/gandiv5.go @@ -0,0 +1,203 @@ +// Package gandiv5 implements a DNS provider for solving the DNS-01 +// challenge using Gandi LiveDNS api. +package gandiv5 + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "os" + "strings" + "sync" + "time" + + "github.com/xenolf/lego/acme" +) + +// Gandi API reference: http://doc.livedns.gandi.net/ + +var ( + // endpoint is the Gandi API endpoint used by Present and + // CleanUp. It is overridden during tests. + endpoint = "https://dns.api.gandi.net/api/v5" + // findZoneByFqdn determines the DNS zone of an fqdn. It is overridden + // during tests. + findZoneByFqdn = acme.FindZoneByFqdn +) + +// inProgressInfo contains information about an in-progress challenge +type inProgressInfo struct { + fieldName string + authZone string +} + +// DNSProvider is an implementation of the +// acme.ChallengeProviderTimeout interface that uses Gandi's LiveDNS +// API to manage TXT records for a domain. +type DNSProvider struct { + apiKey string + inProgressFQDNs map[string]inProgressInfo + inProgressMu sync.Mutex +} + +// NewDNSProvider returns a DNSProvider instance configured for Gandi. +// Credentials must be passed in the environment variable: GANDIV5_API_KEY. +func NewDNSProvider() (*DNSProvider, error) { + apiKey := os.Getenv("GANDIV5_API_KEY") + return NewDNSProviderCredentials(apiKey) +} + +// NewDNSProviderCredentials uses the supplied credentials to return a +// DNSProvider instance configured for Gandi. +func NewDNSProviderCredentials(apiKey string) (*DNSProvider, error) { + if apiKey == "" { + return nil, fmt.Errorf("Gandi DNS: No Gandi API Key given") + } + return &DNSProvider{ + apiKey: apiKey, + inProgressFQDNs: make(map[string]inProgressInfo), + }, nil +} + +// Present creates a TXT record using the specified parameters. +func (d *DNSProvider) Present(domain, token, keyAuth string) error { + fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) + if ttl < 300 { + ttl = 300 // 300 is gandi minimum value for ttl + } + // find authZone + authZone, err := findZoneByFqdn(fqdn, acme.RecursiveNameservers) + if err != nil { + return fmt.Errorf("Gandi DNS: findZoneByFqdn failure: %v", err) + } + // determine name of TXT record + if !strings.HasSuffix( + strings.ToLower(fqdn), strings.ToLower("."+authZone)) { + return fmt.Errorf( + "Gandi DNS: unexpected authZone %s for fqdn %s", authZone, fqdn) + } + name := fqdn[:len(fqdn)-len("."+authZone)] + // acquire lock and check there is not a challenge already in + // progress for this value of authZone + d.inProgressMu.Lock() + defer d.inProgressMu.Unlock() + // add TXT record into authZone + err = d.addTXTRecord(acme.UnFqdn(authZone), name, value, ttl) + if err != nil { + return err + } + // save data necessary for CleanUp + d.inProgressFQDNs[fqdn] = inProgressInfo{ + authZone: authZone, + fieldName: name, + } + return nil +} + +// CleanUp removes the TXT record matching the specified parameters. +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { + fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + // acquire lock and retrieve authZone + d.inProgressMu.Lock() + defer d.inProgressMu.Unlock() + if _, ok := d.inProgressFQDNs[fqdn]; !ok { + // if there is no cleanup information then just return + return nil + } + fieldName := d.inProgressFQDNs[fqdn].fieldName + authZone := d.inProgressFQDNs[fqdn].authZone + delete(d.inProgressFQDNs, fqdn) + // delete TXT record from authZone + err := d.deleteTXTRecord(acme.UnFqdn(authZone), fieldName) + if err != nil { + return err + } + return nil +} + +// Timeout returns the values (20*time.Minute, 20*time.Second) which +// are used by the acme package as timeout and check interval values +// when checking for DNS record propagation with Gandi. +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return 20 * time.Minute, 20 * time.Second +} + +// types for JSON method calls and parameters + +type addFieldRequest struct { + RRSetTTL int `json:"rrset_ttl"` + RRSetValues []string `json:"rrset_values"` +} + +type deleteFieldRequest struct { + Delete bool `json:"delete"` +} + +// types for JSON responses + +type responseStruct struct { + Message string `json:"message"` +} + +// POSTing/Marshalling/Unmarshalling + +func (d *DNSProvider) sendRequest(method string, resource string, payload interface{}) (*responseStruct, error) { + url := fmt.Sprintf("%s/%s", endpoint, resource) + + body, err := json.Marshal(payload) + if err != nil { + return nil, err + } + req, err := http.NewRequest(method, url, bytes.NewReader(body)) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", "application/json") + if len(d.apiKey) > 0 { + req.Header.Set("X-Api-Key", d.apiKey) + } + + client := &http.Client{Timeout: time.Duration(10 * time.Second)} + resp, err := client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode >= 400 { + return nil, fmt.Errorf("Gandi DNS: request failed with HTTP status code %d", resp.StatusCode) + } + var response responseStruct + err = json.NewDecoder(resp.Body).Decode(&response) + if err != nil && method != "DELETE" { + return nil, err + } + + return &response, nil +} + +// functions to perform API actions + +func (d *DNSProvider) addTXTRecord(domain string, name string, value string, ttl int) error { + target := fmt.Sprintf("domains/%s/records/%s/TXT", domain, name) + response, err := d.sendRequest("PUT", target, addFieldRequest{ + RRSetTTL: ttl, + RRSetValues: []string{value}, + }) + if response != nil { + fmt.Printf("Gandi DNS: %s\n", response.Message) + } + return err +} + +func (d *DNSProvider) deleteTXTRecord(domain string, name string) error { + target := fmt.Sprintf("domains/%s/records/%s/TXT", domain, name) + response, err := d.sendRequest("DELETE", target, deleteFieldRequest{ + Delete: true, + }) + if response != nil && response.Message == "" { + fmt.Printf("Gandi DNS: Zone record deleted\n") + } + return err +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/pdns/pdns.go b/vendor/github.com/xenolf/lego/providers/dns/pdns/pdns.go index a4fd22b0c..a7856e6d0 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/pdns/pdns.go +++ b/vendor/github.com/xenolf/lego/providers/dns/pdns/pdns.go @@ -257,12 +257,11 @@ func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawM if c.host.Path != "/" { path = c.host.Path } - if c.apiVersion > 0 { - if !strings.HasPrefix(uri, "api/v") { - uri = "/api/v" + strconv.Itoa(c.apiVersion) + uri - } else { - uri = "/" + uri - } + if !strings.HasPrefix(uri, "/") { + uri = "/" + uri + } + if c.apiVersion > 0 && !strings.HasPrefix(uri, "/api/v") { + uri = "/api/v" + strconv.Itoa(c.apiVersion) + uri } url := c.host.Scheme + "://" + c.host.Host + path + uri req, err := http.NewRequest(method, url, body)