2019-04-15 20:42:02 -04:00
|
|
|
package linodego
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"text/template"
|
|
|
|
|
2020-06-12 05:36:54 -04:00
|
|
|
"github.com/go-resty/resty/v2"
|
2019-04-15 20:42:02 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2020-06-12 05:36:54 -04:00
|
|
|
accountName = "account"
|
|
|
|
accountSettingsName = "accountsettings"
|
|
|
|
domainRecordsName = "records"
|
|
|
|
domainsName = "domains"
|
|
|
|
eventsName = "events"
|
|
|
|
firewallsName = "firewalls"
|
2019-04-15 20:42:02 -04:00
|
|
|
imagesName = "images"
|
|
|
|
instanceConfigsName = "configs"
|
2020-06-12 05:36:54 -04:00
|
|
|
instanceDisksName = "disks"
|
2019-04-15 20:42:02 -04:00
|
|
|
instanceIPsName = "ips"
|
|
|
|
instanceSnapshotsName = "snapshots"
|
2020-06-12 05:36:54 -04:00
|
|
|
instanceStatsName = "instancestats"
|
2019-04-15 20:42:02 -04:00
|
|
|
instanceVolumesName = "instancevolumes"
|
2020-06-12 05:36:54 -04:00
|
|
|
instancesName = "instances"
|
|
|
|
invoiceItemsName = "invoiceitems"
|
|
|
|
invoicesName = "invoices"
|
2019-04-15 20:42:02 -04:00
|
|
|
ipaddressesName = "ipaddresses"
|
|
|
|
ipv6poolsName = "ipv6pools"
|
|
|
|
ipv6rangesName = "ipv6ranges"
|
|
|
|
kernelsName = "kernels"
|
2020-06-12 05:36:54 -04:00
|
|
|
lkeClustersName = "lkeclusters"
|
|
|
|
lkeClusterPoolsName = "lkeclusterpools"
|
|
|
|
lkeVersionsName = "lkeversions"
|
2019-04-15 20:42:02 -04:00
|
|
|
longviewName = "longview"
|
|
|
|
longviewclientsName = "longviewclients"
|
|
|
|
longviewsubscriptionsName = "longviewsubscriptions"
|
2020-06-12 05:36:54 -04:00
|
|
|
managedName = "managed"
|
2019-04-15 20:42:02 -04:00
|
|
|
nodebalancerconfigsName = "nodebalancerconfigs"
|
|
|
|
nodebalancernodesName = "nodebalancernodes"
|
2020-06-12 05:36:54 -04:00
|
|
|
nodebalancerStatsName = "nodebalancerstats"
|
|
|
|
nodebalancersName = "nodebalancers"
|
2019-04-15 20:42:02 -04:00
|
|
|
notificationsName = "notifications"
|
2020-06-12 05:36:54 -04:00
|
|
|
oauthClientsName = "oauthClients"
|
|
|
|
objectStorageBucketsName = "objectstoragebuckets"
|
|
|
|
objectStorageClustersName = "objectstorageclusters"
|
|
|
|
objectStorageKeysName = "objectstoragekeys"
|
|
|
|
paymentsName = "payments"
|
|
|
|
profileName = "profile"
|
|
|
|
regionsName = "regions"
|
2019-04-15 20:42:02 -04:00
|
|
|
sshkeysName = "sshkeys"
|
2020-06-12 05:36:54 -04:00
|
|
|
stackscriptsName = "stackscripts"
|
|
|
|
tagsName = "tags"
|
2019-04-15 20:42:02 -04:00
|
|
|
ticketsName = "tickets"
|
|
|
|
tokensName = "tokens"
|
2020-06-12 05:36:54 -04:00
|
|
|
typesName = "types"
|
2019-04-15 20:42:02 -04:00
|
|
|
usersName = "users"
|
2020-06-12 05:36:54 -04:00
|
|
|
volumesName = "volumes"
|
2019-04-15 20:42:02 -04:00
|
|
|
|
2020-06-12 05:36:54 -04:00
|
|
|
accountEndpoint = "account"
|
|
|
|
accountSettingsEndpoint = "account/settings"
|
|
|
|
domainRecordsEndpoint = "domains/{{ .ID }}/records"
|
|
|
|
domainsEndpoint = "domains"
|
|
|
|
eventsEndpoint = "account/events"
|
|
|
|
firewallsEndpoint = "networking/firewalls"
|
2019-04-15 20:42:02 -04:00
|
|
|
imagesEndpoint = "images"
|
|
|
|
instanceConfigsEndpoint = "linode/instances/{{ .ID }}/configs"
|
|
|
|
instanceDisksEndpoint = "linode/instances/{{ .ID }}/disks"
|
|
|
|
instanceIPsEndpoint = "linode/instances/{{ .ID }}/ips"
|
2020-06-12 05:36:54 -04:00
|
|
|
instanceSnapshotsEndpoint = "linode/instances/{{ .ID }}/backups"
|
|
|
|
instanceStatsEndpoint = "linode/instances/{{ .ID }}/stats"
|
2019-04-15 20:42:02 -04:00
|
|
|
instanceVolumesEndpoint = "linode/instances/{{ .ID }}/volumes"
|
2020-06-12 05:36:54 -04:00
|
|
|
instancesEndpoint = "linode/instances"
|
|
|
|
invoiceItemsEndpoint = "account/invoices/{{ .ID }}/items"
|
|
|
|
invoicesEndpoint = "account/invoices"
|
2019-04-15 20:42:02 -04:00
|
|
|
ipaddressesEndpoint = "networking/ips"
|
|
|
|
ipv6poolsEndpoint = "networking/ipv6/pools"
|
|
|
|
ipv6rangesEndpoint = "networking/ipv6/ranges"
|
|
|
|
kernelsEndpoint = "linode/kernels"
|
2020-06-12 05:36:54 -04:00
|
|
|
lkeClustersEndpoint = "lke/clusters"
|
|
|
|
lkeClusterPoolsEndpoint = "lke/clusters/{{ .ID }}/pools"
|
|
|
|
lkeVersionsEndpoint = "lke/versions"
|
2019-04-15 20:42:02 -04:00
|
|
|
longviewEndpoint = "longview"
|
|
|
|
longviewclientsEndpoint = "longview/clients"
|
|
|
|
longviewsubscriptionsEndpoint = "longview/subscriptions"
|
2020-06-12 05:36:54 -04:00
|
|
|
managedEndpoint = "managed"
|
2019-04-15 20:42:02 -04:00
|
|
|
// @TODO we can't use these nodebalancer endpoints unless we include these templated fields
|
|
|
|
// The API seems inconsistent about including parent IDs in objects, (compare instance configs to nb configs)
|
|
|
|
// Parent IDs would be immutable for updates and are ignored in create requests ..
|
|
|
|
// Should we include these fields in CreateOpts and UpdateOpts?
|
2020-06-12 05:36:54 -04:00
|
|
|
nodebalancerconfigsEndpoint = "nodebalancers/{{ .ID }}/configs"
|
|
|
|
nodebalancernodesEndpoint = "nodebalancers/{{ .ID }}/configs/{{ .SecondID }}/nodes"
|
|
|
|
nodebalancerStatsEndpoint = "nodebalancers/{{ .ID }}/stats"
|
|
|
|
nodebalancersEndpoint = "nodebalancers"
|
|
|
|
notificationsEndpoint = "account/notifications"
|
|
|
|
oauthClientsEndpoint = "account/oauth-clients"
|
|
|
|
objectStorageBucketsEndpoint = "object-storage/buckets"
|
|
|
|
objectStorageClustersEndpoint = "object-storage/clusters"
|
|
|
|
objectStorageKeysEndpoint = "object-storage/keys"
|
|
|
|
paymentsEndpoint = "account/payments"
|
|
|
|
profileEndpoint = "profile"
|
|
|
|
regionsEndpoint = "regions"
|
|
|
|
sshkeysEndpoint = "profile/sshkeys"
|
|
|
|
stackscriptsEndpoint = "linode/stackscripts"
|
|
|
|
tagsEndpoint = "tags"
|
|
|
|
ticketsEndpoint = "support/tickets"
|
|
|
|
tokensEndpoint = "profile/tokens"
|
|
|
|
typesEndpoint = "linode/types"
|
|
|
|
usersEndpoint = "account/users"
|
|
|
|
volumesEndpoint = "volumes"
|
2019-04-15 20:42:02 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
// Resource represents a linode API resource
|
|
|
|
type Resource struct {
|
|
|
|
name string
|
|
|
|
endpoint string
|
|
|
|
isTemplate bool
|
|
|
|
endpointTemplate *template.Template
|
|
|
|
R func(ctx context.Context) *resty.Request
|
|
|
|
PR func(ctx context.Context) *resty.Request
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewResource is the factory to create a new Resource struct. If it has a template string the useTemplate bool must be set.
|
|
|
|
func NewResource(client *Client, name string, endpoint string, useTemplate bool, singleType interface{}, pagedType interface{}) *Resource {
|
|
|
|
var tmpl *template.Template
|
|
|
|
|
|
|
|
if useTemplate {
|
|
|
|
tmpl = template.Must(template.New(name).Parse(endpoint))
|
|
|
|
}
|
|
|
|
|
|
|
|
r := func(ctx context.Context) *resty.Request {
|
|
|
|
return client.R(ctx).SetResult(singleType)
|
|
|
|
}
|
|
|
|
|
|
|
|
pr := func(ctx context.Context) *resty.Request {
|
|
|
|
return client.R(ctx).SetResult(pagedType)
|
|
|
|
}
|
|
|
|
|
|
|
|
return &Resource{name, endpoint, useTemplate, tmpl, r, pr}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r Resource) render(data ...interface{}) (string, error) {
|
|
|
|
if data == nil {
|
|
|
|
return "", NewError("Cannot template endpoint with <nil> data")
|
|
|
|
}
|
|
|
|
out := ""
|
|
|
|
buf := bytes.NewBufferString(out)
|
|
|
|
|
|
|
|
var substitutions interface{}
|
2020-06-12 05:36:54 -04:00
|
|
|
|
|
|
|
switch len(data) {
|
|
|
|
case 1:
|
2019-04-15 20:42:02 -04:00
|
|
|
substitutions = struct{ ID interface{} }{data[0]}
|
2020-06-12 05:36:54 -04:00
|
|
|
case 2:
|
2019-04-15 20:42:02 -04:00
|
|
|
substitutions = struct {
|
|
|
|
ID interface{}
|
|
|
|
SecondID interface{}
|
|
|
|
}{data[0], data[1]}
|
2020-06-12 05:36:54 -04:00
|
|
|
default:
|
2019-04-15 20:42:02 -04:00
|
|
|
return "", NewError("Too many arguments to render template (expected 1 or 2)")
|
|
|
|
}
|
2020-06-12 05:36:54 -04:00
|
|
|
|
2019-04-15 20:42:02 -04:00
|
|
|
if err := r.endpointTemplate.Execute(buf, substitutions); err != nil {
|
|
|
|
return "", NewError(err)
|
|
|
|
}
|
|
|
|
return buf.String(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// endpointWithID will return the rendered endpoint string for the resource with provided id
|
|
|
|
func (r Resource) endpointWithID(id ...int) (string, error) {
|
|
|
|
if !r.isTemplate {
|
|
|
|
return r.endpoint, nil
|
|
|
|
}
|
|
|
|
data := make([]interface{}, len(id))
|
2020-06-12 05:36:54 -04:00
|
|
|
|
2019-04-15 20:42:02 -04:00
|
|
|
for i, v := range id {
|
|
|
|
data[i] = v
|
|
|
|
}
|
|
|
|
return r.render(data...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Endpoint will return the non-templated endpoint string for resource
|
|
|
|
func (r Resource) Endpoint() (string, error) {
|
|
|
|
if r.isTemplate {
|
|
|
|
return "", NewError(fmt.Sprintf("Tried to get endpoint for %s without providing data for template", r.name))
|
|
|
|
}
|
|
|
|
return r.endpoint, nil
|
|
|
|
}
|