Documentation
¶
Overview ¶
Package clcgo is an API wrapper for the CenturyLink Cloud (CLC) API V2. While not a complete implementation of the API, it is capable of fulfilling common use cases around provisioning and querying servers.
It will be invaluable for you to read the API documentation to understand what is available:
https://t3n.zendesk.com/categories/20067994-API-v2-0-Beta-
This library attempts to follow the resource and JSON attribute names exactly in naming its structs and fields.
Usage ¶
All API interactions require authentication. You should begin by instantiating a Client with the NewClient function. You can then authenticate with your CLC username and password using the GetAPICredentials function. You can read more about the details of authentication in the documentation for the APICredentials struct and the GetAPICredentials function.
Once authenticated, the Client provides a function for fetching resources, GetEntity, and one for saving resource, SaveEntity. Both of those functions have example documentation around their use. Each individual resource has documentation about its capabilities and requirements.
Development and Extension ¶
If you want to use clcgo with an API resource that has not yet been implemented, you should look at the documentation for the Entity, SaveEntity, and CreationStatusProvidingEntity interfaces. Implementing one or more of those interfaces, depending on the resource, will allow you to write your own resources and interact with them in the standard fashion.
Index ¶
- Variables
- type APICredentials
- type Client
- type CreationStatusProvidingEntity
- type Credentials
- type DataCenter
- type DataCenterCapabilities
- type DataCenterGroup
- type DataCenters
- type DeletionStatusProvidingEntity
- type DeployableNetwork
- type Entity
- type Group
- type Link
- type OperationType
- type Port
- type PublicIPAddress
- type RequestError
- type SavableEntity
- type Server
- type ServerOperation
- type Status
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var DefaultHTTPClient = &http.Client{}
DefaultHTTPClient can be overridden in the same fashion as the DefaultClient for http, if you would like to implement some behavioral change around the requests that we could not anticipate.
Functions ¶
This section is empty.
Types ¶
type APICredentials ¶
type APICredentials struct { BearerToken string `json:"bearerToken"` AccountAlias string `json:"accountAlias"` Username string `json:"username"` // TODO: nonexistent in get, extract to creation params? Password string `json:"password"` // TODO: nonexistent in get, extract to creation params? }
APICredentials are used by the Client to identify you to CenturyLink Cloud in any request. The BearerToken value will be good for a set period of time, defined in the API documentation.
If you anticipate making many API requests in several runs of your application, it may be a good idea to save the BearerToken and AccountAlias values somewhere so that they can be recalled when they are needed again, rather than having to re-authenticate with the API. You can build a APICredentials object with those two fields, then assign it to the Client's APICredentials value.
The Username and Password values will be present only if you've used GetAPICredentials, and can otherwise be ignored.
func (APICredentials) RequestForSave ¶
func (c APICredentials) RequestForSave(a string) (request, error)
type Client ¶
type Client struct { APICredentials APICredentials Requestor requestor }
The Client stores your current credentials and uses them to set or fetch data from the API. It should be instantiated with the NewClient function.
func NewClient ¶
func NewClient() *Client
NewClient returns a Client that has been configured to communicate to CenturyLink Cloud. Before making any requests, the Client must be authorized by either setting its APICredentials field or calling the GetAPICredentials function.
func (*Client) DeleteEntity ¶
DeleteEntity is used to tell CenturyLink Cloud to remove a resource.
The method returns a Status, which can be used as described in the SaveEntity documentation to determine when the work completes. The Entity object will not be modified.
func (*Client) GetAPICredentials ¶
GetAPICredentials accepts username and password strings to populate the Client instance with valid APICredentials.
CenturyLink Cloud requires a BearerToken to authorize all requests, and this method will fetch one for the user and associate it with the Client. Any further requests made by the Client will include that BearerToken.
Example (Failed) ¶
package main import ( "fmt" "net/http" "net/http/httptest" "net/url" "github.com/CenturyLinkLabs/clcgo" "github.com/CenturyLinkLabs/clcgo/fakeapi" ) var ( exampleDefaultHTTPClient *http.Client fakeAPIServer *httptest.Server ) type exampleDomainRewriter struct { RewriteURL *url.URL } func (r exampleDomainRewriter) RoundTrip(req *http.Request) (resp *http.Response, err error) { req.URL.Scheme = "http" req.URL.Host = r.RewriteURL.Host t := http.Transport{} return t.RoundTrip(req) } func setupExample() { fakeAPIServer = fakeapi.CreateFakeServer() exampleDefaultHTTPClient = clcgo.DefaultHTTPClient u, _ := url.Parse(fakeAPIServer.URL) clcgo.DefaultHTTPClient = &http.Client{Transport: exampleDomainRewriter{RewriteURL: u}} } func teardownExample() { fakeAPIServer.Close() clcgo.DefaultHTTPClient = exampleDefaultHTTPClient } func main() { // Some test-related setup code which you can safely ignore. setupExample() defer teardownExample() c := clcgo.NewClient() err := c.GetAPICredentials("bad", "bad") fmt.Printf("Error: %s", err) }
Output: Error: there was a problem with your credentials
Example (Successful) ¶
package main import ( "fmt" "net/http" "net/http/httptest" "net/url" "github.com/CenturyLinkLabs/clcgo" "github.com/CenturyLinkLabs/clcgo/fakeapi" ) var ( exampleDefaultHTTPClient *http.Client fakeAPIServer *httptest.Server ) type exampleDomainRewriter struct { RewriteURL *url.URL } func (r exampleDomainRewriter) RoundTrip(req *http.Request) (resp *http.Response, err error) { req.URL.Scheme = "http" req.URL.Host = r.RewriteURL.Host t := http.Transport{} return t.RoundTrip(req) } func setupExample() { fakeAPIServer = fakeapi.CreateFakeServer() exampleDefaultHTTPClient = clcgo.DefaultHTTPClient u, _ := url.Parse(fakeAPIServer.URL) clcgo.DefaultHTTPClient = &http.Client{Transport: exampleDomainRewriter{RewriteURL: u}} } func teardownExample() { fakeAPIServer.Close() clcgo.DefaultHTTPClient = exampleDefaultHTTPClient } func main() { // Some test-related setup code which you can safely ignore. setupExample() defer teardownExample() c := clcgo.NewClient() c.GetAPICredentials("user", "pass") fmt.Printf("Account Alias: %s", c.APICredentials.AccountAlias) }
Output: Account Alias: ACME
func (*Client) GetEntity ¶
GetEntity is used to fetch a summary of a resource. When you pass a pointer to your resource, GetEntity will set its fields appropriately.
Different resources have different field requirements before their summaries can be fetched successfully. If you omit an ID field from a Server, for instance, GetEntity will return an error informing you of the missing field. An error from GetEntity likely means that your passed Entity was not modified.
Example (ExpiredToken) ¶
package main import ( "fmt" "net/http" "net/http/httptest" "net/url" "github.com/CenturyLinkLabs/clcgo" "github.com/CenturyLinkLabs/clcgo/fakeapi" ) var ( exampleDefaultHTTPClient *http.Client fakeAPIServer *httptest.Server ) type exampleDomainRewriter struct { RewriteURL *url.URL } func (r exampleDomainRewriter) RoundTrip(req *http.Request) (resp *http.Response, err error) { req.URL.Scheme = "http" req.URL.Host = r.RewriteURL.Host t := http.Transport{} return t.RoundTrip(req) } func setupExample() { fakeAPIServer = fakeapi.CreateFakeServer() exampleDefaultHTTPClient = clcgo.DefaultHTTPClient u, _ := url.Parse(fakeAPIServer.URL) clcgo.DefaultHTTPClient = &http.Client{Transport: exampleDomainRewriter{RewriteURL: u}} } func teardownExample() { fakeAPIServer.Close() clcgo.DefaultHTTPClient = exampleDefaultHTTPClient } func main() { // Some test-related setup code which you can safely ignore. setupExample() defer teardownExample() c := clcgo.NewClient() // You are caching this Bearer Token value and it has either expired or for // some other reason become invalid. c.APICredentials = clcgo.APICredentials{BearerToken: "expired", AccountAlias: "ACME"} s := clcgo.Server{ID: "server1"} err := c.GetEntity(&s) rerr, _ := err.(clcgo.RequestError) fmt.Printf("Error: %s, Status Code: %d", rerr, rerr.StatusCode) }
Output: Error: your bearer token was rejected, Status Code: 401
Example (Successful) ¶
package main import ( "fmt" "net/http" "net/http/httptest" "net/url" "github.com/CenturyLinkLabs/clcgo" "github.com/CenturyLinkLabs/clcgo/fakeapi" ) var ( exampleDefaultHTTPClient *http.Client fakeAPIServer *httptest.Server ) type exampleDomainRewriter struct { RewriteURL *url.URL } func (r exampleDomainRewriter) RoundTrip(req *http.Request) (resp *http.Response, err error) { req.URL.Scheme = "http" req.URL.Host = r.RewriteURL.Host t := http.Transport{} return t.RoundTrip(req) } func setupExample() { fakeAPIServer = fakeapi.CreateFakeServer() exampleDefaultHTTPClient = clcgo.DefaultHTTPClient u, _ := url.Parse(fakeAPIServer.URL) clcgo.DefaultHTTPClient = &http.Client{Transport: exampleDomainRewriter{RewriteURL: u}} } func teardownExample() { fakeAPIServer.Close() clcgo.DefaultHTTPClient = exampleDefaultHTTPClient } func main() { // Some test-related setup code which you can safely ignore. setupExample() defer teardownExample() c := clcgo.NewClient() c.GetAPICredentials("user", "pass") s := clcgo.Server{ID: "server1"} c.GetEntity(&s) fmt.Printf("Server Name: %s", s.Name) }
Output: Server Name: Test Name
func (*Client) SaveEntity ¶
func (c *Client) SaveEntity(e SavableEntity) (Status, error)
SaveEntity is used to persist a changed resource to CenturyLink Cloud.
Beyond the fields absolutely required to form valid URLs, the presence or format of the fields on your resources are not validated before they are submitted. You should check the CenturyLink Cloud API documentation for this information. If your submission was unsuccessful, it is likely that the error returned is a RequestError, which may contain helpful error messages you can use to determine what went wrong.
Calling HasSucceeded on the returned Status will tell you if the resource is ready. Some resources are available immediately, but most are not. If the resource implements CreationStatusProvidingEntity it will likely take time, but regardless you can check with the returned Status to be sure.
The Status does not update itself, and you will need to call GetEntity on it periodically to determine when its resource is ready if it was not immediately successful.
Never ignore the error result from this call! Even if your code is perfect (congratulations, by the way), errors can still occur due to unexpected server states or networking issues.
Example (Successful) ¶
package main import ( "fmt" "net/http" "net/http/httptest" "net/url" "github.com/CenturyLinkLabs/clcgo" "github.com/CenturyLinkLabs/clcgo/fakeapi" ) var ( exampleDefaultHTTPClient *http.Client fakeAPIServer *httptest.Server ) type exampleDomainRewriter struct { RewriteURL *url.URL } func (r exampleDomainRewriter) RoundTrip(req *http.Request) (resp *http.Response, err error) { req.URL.Scheme = "http" req.URL.Host = r.RewriteURL.Host t := http.Transport{} return t.RoundTrip(req) } func setupExample() { fakeAPIServer = fakeapi.CreateFakeServer() exampleDefaultHTTPClient = clcgo.DefaultHTTPClient u, _ := url.Parse(fakeAPIServer.URL) clcgo.DefaultHTTPClient = &http.Client{Transport: exampleDomainRewriter{RewriteURL: u}} } func teardownExample() { fakeAPIServer.Close() clcgo.DefaultHTTPClient = exampleDefaultHTTPClient } func main() { // Some test-related setup code which you can safely ignore. setupExample() defer teardownExample() c := clcgo.NewClient() c.GetAPICredentials("user", "pass") // Build a Server resource. In reality there are many more required fields. s := clcgo.Server{Name: "My Server"} // Request the Server be provisioned, returning a Status. In your code you // must NOT ignore the possibility of an error here. st, _ := c.SaveEntity(&s) // Refresh the Status until it has completed. In your code you should put a // delay between requests, as this can take a while. if !st.HasSucceeded() { c.GetEntity(&st) } // The Status says that the server is provisioned. You can now request its // details. Again, your code should not ignore errors as this is doing. c.GetEntity(&s) fmt.Printf("Server ID: %s", s.ID) }
Output: Server ID: test-id
type CreationStatusProvidingEntity ¶
CreationStatusProvidingEntity will be implemented by some SavableEntity resources when information about the created resource is not immediately available. For instance, a Server or PublicIPAddress must first be provisioned, so a Status object is returned so that you can query it until the work has been successfully completed.
All StatusProvidingEntites must be SavableEntities, but not every SavableEntity is a CreationStatusProvidingEntity.
type Credentials ¶
type Credentials struct { Server Server `json:"-"` Username string `json:"userName"` Password string `json:"password"` }
Credentials can be used to fetch the username and password for a Server. You must supply the associated Server.
This uses an undocumented API endpoint and could be changed or removed.
Example (Persisted) ¶
package main import ( "github.com/CenturyLinkLabs/clcgo" ) func main() { client := clcgo.NewClient() creds := clcgo.APICredentials{BearerToken: "TOKEN", AccountAlias: "ACME"} client.APICredentials = creds // Client now ready for authenticated requests. }
Output:
type DataCenter ¶
A DataCenter resource can either be returned by the DataCenters resource, or built manually. It should be used in conjunction with the DataCenterCapabilities resource to request information about it.
You must supply the ID if you are building this object manually.
type DataCenterCapabilities ¶
type DataCenterCapabilities struct { DataCenter DataCenter `json:"-"` DeployableNetworks []DeployableNetwork `json:"deployableNetworks"` Templates []struct { Name string `json:"name"` Description string `json:"description"` } `json:"templates"` }
DataCenterCapabilities gets more information about a specific DataCenter. You must supply the associated DataCenter object.
type DataCenterGroup ¶
type DataCenterGroup struct { DataCenter DataCenter `json:"-"` ID string `json:"id"` Name string `json:"name"` Links []Link `json:"links"` }
DataCenterGroup can be used to find associated groups for a datacenter and your account. The linked hardware group can be used with the Group object to query for your account's groups within that datacenter.
You must supply the associated DataCenter object.
type DataCenters ¶
type DataCenters []DataCenter
The DataCenters resource can retrieve a list of available DataCenters.
type DeployableNetwork ¶
type DeployableNetwork struct { Name string `json:"name"` NetworkID string `json:"networkId"` Type string `json:"type"` AccountID string `json:"accountID"` }
A DeployableNetwork describes a private network that is scoped to an Account and DataCenter. It can be fetched via the DataCenterCapabilities and can optionally be used to put a Server in a specific network.
type Entity ¶
The Entity interface is implemented by any resource type that can be asked for its details via GetEntity. This seems basic and is implemented in most cases, but a few - PublicIPAddress for instance - can only be sent and not subsequently read.
type Group ¶
type Group struct { ParentGroup *Group `json:"-"` ID string `json:"id"` ParentGroupID string `json:"parentGroupId"` // TODO: not in get, extract to creation params? Name string `json:"name"` Description string `json:"description"` Type string `json:"type"` Groups []Group `json:"groups"` }
A Group resource can be used to discover the hierarchy of the created groups for your account for a given datacenter. The Group's ID can either be for one you have created, or for a datacenter's hardware group (which can be determined via the DataCenterGroup resource).
When creating a new group, the ParentGroup field must be set before you attempt to save.
func (*Group) RequestForSave ¶
type Link ¶
A Link is a meta object returned in many API responses to help find resources related to the one you've requested.
type OperationType ¶
type OperationType string
An OperationType should be used in conjunction with a ServerOperation to instruct a server instance to perform one of several operations.
const ( PauseServer OperationType = "pause" ShutDownServer OperationType = "shutDown" RebootServer OperationType = "reboot" ResetServer OperationType = "reset" PowerOnServer OperationType = "powerOn" PowerOffServer OperationType = "powerOff" )
More information on precisely what each operation does can be found in the CenturyLink Cloud V2 API documentation.
type Port ¶
A Port object specifies a network port that should be made available on a PublicIPAddress. It can only be used in conjunction with the PublicIPAddress resource.
type PublicIPAddress ¶
type PublicIPAddress struct { Server Server Ports []Port `json:"ports"` InternalIPAddress string `json:"internalIPAddress"` }
A PublicIPAddress can be created and associated with an existing, provisioned Server. You must supply the associated Server object.
You must supply a slice of Port objects that will make the specified ports accessible at the address.
func (PublicIPAddress) RequestForSave ¶
func (i PublicIPAddress) RequestForSave(a string) (request, error)
func (PublicIPAddress) StatusFromCreateResponse ¶
func (i PublicIPAddress) StatusFromCreateResponse(r []byte) (Status, error)
type RequestError ¶
A RequestError can be returned from GetEntity and SaveEntity calls and contain specific information about the unexpected error from your request. It can be especially helpful on SaveEntity validation failures, for instance if you omitted a required field.
func (RequestError) Error ¶
func (r RequestError) Error() string
type SavableEntity ¶
The SavableEntity interface is implemented on any resources that can be saved via SaveEntity.
type Server ¶
type Server struct { ID string `json:"id"` Name string `json:"name"` GroupID string `json:"groupId"` Status string `json:"status"` SourceServerID string `json:"sourceServerId"` // TODO: nonexistent in get, extract to creation params? CPU int `json:"cpu"` MemoryGB int `json:"memoryGB"` // TODO: memoryMB in get, extract to creation params? Type string `json:"type"` DeployableNetwork DeployableNetwork `json:"-"` NetworkID string `json:"networkId"` Password string `json:"password"` Details struct { PowerState string `json:"powerState"` IPAddresses []struct { Public string `json:"public"` Internal string `json:"internal"` } `json:"ipAddresses"` } `json:"details"` // contains filtered or unexported fields }
A Server can be used to either fetch an existing Server or provision and new one. To fetch, you must supply an ID value. For creation, there are numerous required values. The API documentation should be consulted.
The SourceServerID is a required field that allows multiple values which are documented in the API. One of the allowed values is a Template ID, which can be retrieved with the DataCenterCapabilities resource.
To make your server a member of a specific network, you can set the DeployableNetwork field. This is optional. The Server will otherwise be a member of the default network. DeployableNetworks exist per account and DataCenter and can be retrieved via the DataCenterCapabilities resource. If you know the NetworkID, you can supply it instead.
A Password field can be set for a Server you are saving, but fetching the username and password for an existing Server can only be done via the Credentials resource.
func (Server) IsActive ¶
IsActive will, unsurprisingly, tell you if the Server is both active and not paused.
func (*Server) RequestForSave ¶
func (*Server) StatusFromCreateResponse ¶
type ServerOperation ¶
type ServerOperation struct { OperationType OperationType Server Server }
A ServerOperation is used to perform many tasks around starting/stopping/pausing server instances.
You are required to pass both a Server reference and an OperationType, and you will be notified of the operation's progress via the Status that is returned when the ServerOperation is passed to SaveEntity.
func (ServerOperation) RequestForSave ¶
func (p ServerOperation) RequestForSave(a string) (request, error)
func (ServerOperation) StatusFromCreateResponse ¶
func (p ServerOperation) StatusFromCreateResponse(r []byte) (Status, error)
type Status ¶
A Status is returned by all SaveEntity calls and can be used to determine when long-running provisioning jobs have completed. Things like Server creation take time, and you can periodically call GetEntity on the returned Status to determine if the server is ready.
func (Status) HasSucceeded ¶
HasSucceeded will, unsurprisingly, tell you if the Status is successful.
Source Files
¶
Directories
¶
Path | Synopsis |
---|---|
Package fakeapi contains canned API responses from the CenturyLink Cloud API, and helpers to start an httptest.Server that will return those responses appropriately.
|
Package fakeapi contains canned API responses from the CenturyLink Cloud API, and helpers to start an httptest.Server that will return those responses appropriately. |