Update Triton API libraries to fix regression with CNS and machine tags. (#13612)

* Update Triton API libraries (`joyent/triton-go`) to fix regression with CNS and machine tags.

* Update checksum to match the latest upstream sha
This commit is contained in:
Sean Chittenden 2017-04-13 04:30:46 -07:00 committed by Paul Stack
parent 5e7531120c
commit 5d64819ffa
3 changed files with 180 additions and 24 deletions

View File

@ -10,7 +10,6 @@ import (
"net/http"
"net/url"
"os"
"strings"
"time"
"github.com/hashicorp/errwrap"
@ -22,7 +21,7 @@ import (
type Client struct {
client *retryablehttp.Client
authorizer []authentication.Signer
endpoint string
apiURL url.URL
accountName string
}
@ -36,6 +35,15 @@ func NewClient(endpoint string, accountName string, signers ...authentication.Si
defaultRetryWaitMax := 5 * time.Minute
defaultRetryMax := 32
apiURL, err := url.Parse(endpoint)
if err != nil {
return nil, errwrap.Wrapf("invalid endpoint: {{err}}", err)
}
if accountName == "" {
return nil, fmt.Errorf("account name can not be empty")
}
httpClient := &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
@ -62,7 +70,7 @@ func NewClient(endpoint string, accountName string, signers ...authentication.Si
return &Client{
client: retryableClient,
authorizer: signers,
endpoint: strings.TrimSuffix(endpoint, "/"),
apiURL: *apiURL,
accountName: accountName,
}, nil
}
@ -71,10 +79,6 @@ func doNotFollowRedirects(*http.Request, []*http.Request) error {
return http.ErrUseLastResponse
}
func (c *Client) formatURL(path string) string {
return fmt.Sprintf("%s%s", c.endpoint, path)
}
func (c *Client) executeRequestURIParams(method, path string, body interface{}, query *url.Values) (io.ReadCloser, error) {
var requestBody io.ReadSeeker
if body != nil {
@ -85,7 +89,13 @@ func (c *Client) executeRequestURIParams(method, path string, body interface{},
requestBody = bytes.NewReader(marshaled)
}
req, err := retryablehttp.NewRequest(method, c.formatURL(path), requestBody)
endpoint := c.apiURL
endpoint.Path = path
if query != nil {
endpoint.RawQuery = query.Encode()
}
req, err := retryablehttp.NewRequest(method, endpoint.String(), requestBody)
if err != nil {
return nil, errwrap.Wrapf("Error constructing HTTP request: {{err}}", err)
}
@ -106,10 +116,6 @@ func (c *Client) executeRequestURIParams(method, path string, body interface{},
req.Header.Set("Content-Type", "application/json")
}
if query != nil {
req.URL.RawQuery = query.Encode()
}
resp, err := c.client.Do(req)
if err != nil {
return nil, errwrap.Wrapf("Error executing HTTP request: {{err}}", err)
@ -149,7 +155,10 @@ func (c *Client) executeRequestRaw(method, path string, body interface{}) (*http
requestBody = bytes.NewReader(marshaled)
}
req, err := retryablehttp.NewRequest(method, c.formatURL(path), requestBody)
endpoint := c.apiURL
endpoint.Path = path
req, err := retryablehttp.NewRequest(method, endpoint.String(), requestBody)
if err != nil {
return nil, errwrap.Wrapf("Error constructing HTTP request: {{err}}", err)
}

View File

@ -4,10 +4,12 @@ import (
"encoding/json"
"fmt"
"net/http"
"strings"
"time"
"github.com/hashicorp/errwrap"
"net/url"
"github.com/hashicorp/errwrap"
)
type MachinesClient struct {
@ -20,6 +22,21 @@ func (c *Client) Machines() *MachinesClient {
return &MachinesClient{c}
}
const (
machineCNSTagDisable = "triton.cns.disable"
machineCNSTagReversePTR = "triton.cns.reverse_ptr"
machineCNSTagServices = "triton.cns.services"
)
// MachineCNS is a container for the CNS-specific attributes. In the API these
// values are embedded within a Machine's Tags attribute, however they are
// exposed to the caller as their native types.
type MachineCNS struct {
Disable *bool
ReversePTR *string
Services []string
}
type Machine struct {
ID string `json:"id"`
Name string `json:"name"`
@ -41,6 +58,14 @@ type Machine struct {
ComputeNode string `json:"compute_node"`
Package string `json:"package"`
DomainNames []string `json:"dns_names"`
CNS MachineCNS
}
// _Machine is a private facade over Machine that handles the necessary API
// overrides from vmapi's machine endpoint(s).
type _Machine struct {
Machine
Tags map[string]interface{} `json:"tags"`
}
type NIC struct {
@ -57,7 +82,19 @@ type GetMachineInput struct {
ID string
}
func (gmi *GetMachineInput) Validate() error {
if gmi.ID == "" {
return fmt.Errorf("machine ID can not be empty")
}
return nil
}
func (client *MachinesClient) GetMachine(input *GetMachineInput) (*Machine, error) {
if err := input.Validate(); err != nil {
return nil, errwrap.Wrapf("unable to get machine: {{err}}", err)
}
path := fmt.Sprintf("/%s/machines/%s", client.accountName, input.ID)
response, err := client.executeRequestRaw(http.MethodGet, path, nil)
if response != nil {
@ -73,13 +110,51 @@ func (client *MachinesClient) GetMachine(input *GetMachineInput) (*Machine, erro
client.decodeError(response.StatusCode, response.Body))
}
var result *Machine
var result *_Machine
decoder := json.NewDecoder(response.Body)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding GetMachine response: {{err}}", err)
}
return result, nil
native, err := result.toNative()
if err != nil {
return nil, errwrap.Wrapf("unable to convert API response for machines to native type: {{err}}", err)
}
return native, nil
}
func (client *MachinesClient) GetMachines() ([]*Machine, error) {
path := fmt.Sprintf("/%s/machines", client.accountName)
response, err := client.executeRequestRaw(http.MethodGet, path, nil)
if response != nil {
defer response.Body.Close()
}
if response.StatusCode == http.StatusNotFound {
return nil, &TritonError{
Code: "ResourceNotFound",
}
}
if err != nil {
return nil, errwrap.Wrapf("Error executing GetMachines request: {{err}}",
client.decodeError(response.StatusCode, response.Body))
}
var results []*_Machine
decoder := json.NewDecoder(response.Body)
if err = decoder.Decode(&results); err != nil {
return nil, errwrap.Wrapf("Error decoding GetMachines response: {{err}}", err)
}
machines := make([]*Machine, 0, len(results))
for _, machineAPI := range results {
native, err := machineAPI.toNative()
if err != nil {
return nil, errwrap.Wrapf("unable to convert API response for machines to native type: {{err}}", err)
}
machines = append(machines, native)
}
return machines, nil
}
type CreateMachineInput struct {
@ -93,23 +168,31 @@ type CreateMachineInput struct {
Metadata map[string]string
Tags map[string]string
FirewallEnabled bool
CNS MachineCNS
}
func transformCreateMachineInput(input *CreateMachineInput) map[string]interface{} {
result := make(map[string]interface{}, 8+len(input.Metadata)+len(input.Tags))
func (input *CreateMachineInput) toAPI() map[string]interface{} {
const numExtraParams = 8
result := make(map[string]interface{}, numExtraParams+len(input.Metadata)+len(input.Tags))
result["firewall_enabled"] = input.FirewallEnabled
if input.Name != "" {
result["name"] = input.Name
}
if input.Package != "" {
result["package"] = input.Package
}
if input.Image != "" {
result["image"] = input.Image
}
if len(input.Networks) > 0 {
result["networks"] = input.Networks
}
locality := struct {
Strict bool `json:"strict"`
Near []string `json:"near,omitempty"`
@ -123,6 +206,11 @@ func transformCreateMachineInput(input *CreateMachineInput) map[string]interface
for key, value := range input.Tags {
result[fmt.Sprintf("tag.%s", key)] = value
}
// Deliberately clobber any user-specified Tags with the attributes from the
// CNS struct.
input.CNS.toTags(result)
for key, value := range input.Metadata {
result[fmt.Sprintf("metadata.%s", key)] = value
}
@ -131,7 +219,7 @@ func transformCreateMachineInput(input *CreateMachineInput) map[string]interface
}
func (client *MachinesClient) CreateMachine(input *CreateMachineInput) (*Machine, error) {
respReader, err := client.executeRequest(http.MethodPost, "/my/machines", transformCreateMachineInput(input))
respReader, err := client.executeRequest(http.MethodPost, "/my/machines", input.toAPI())
if respReader != nil {
defer respReader.Close()
}
@ -309,13 +397,14 @@ func (client *MachinesClient) ListMachineTags(input *ListMachineTagsInput) (map[
return nil, errwrap.Wrapf("Error executing ListMachineTags request: {{err}}", err)
}
var result map[string]string
var result map[string]interface{}
decoder := json.NewDecoder(respReader)
if err = decoder.Decode(&result); err != nil {
return nil, errwrap.Wrapf("Error decoding ListMachineTags response: {{err}}", err)
}
return result, nil
_, tags := machineTagsExtractMeta(result)
return tags, nil
}
type UpdateMachineMetadataInput struct {
@ -470,3 +559,61 @@ func (client *MachinesClient) RemoveNIC(input *RemoveNICInput) error {
return nil
}
var reservedMachineCNSTags = map[string]struct{}{
machineCNSTagDisable: {},
machineCNSTagReversePTR: {},
machineCNSTagServices: {},
}
// machineTagsExtractMeta() extracts all of the misc parameters from Tags and
// returns a clean CNS and Tags struct.
func machineTagsExtractMeta(tags map[string]interface{}) (MachineCNS, map[string]string) {
nativeCNS := MachineCNS{}
nativeTags := make(map[string]string, len(tags))
for k, raw := range tags {
if _, found := reservedMachineCNSTags[k]; found {
switch k {
case machineCNSTagDisable:
b := raw.(bool)
nativeCNS.Disable = &b
case machineCNSTagReversePTR:
s := raw.(string)
nativeCNS.ReversePTR = &s
case machineCNSTagServices:
nativeCNS.Services = strings.Split(raw.(string), ",")
default:
// TODO(seanc@): should assert, logic fail
}
} else {
nativeTags[k] = raw.(string)
}
}
return nativeCNS, nativeTags
}
// toNative() exports a given _Machine (API representation) to its native object
// format.
func (api *_Machine) toNative() (*Machine, error) {
m := Machine(api.Machine)
m.CNS, m.Tags = machineTagsExtractMeta(api.Tags)
return &m, nil
}
// toTags() injects its state information into a Tags map suitable for use to
// submit an API call to the vmapi machine endpoint
func (mcns *MachineCNS) toTags(m map[string]interface{}) {
if mcns.Disable != nil {
s := fmt.Sprintf("%t", mcns.Disable)
m[machineCNSTagDisable] = &s
}
if mcns.ReversePTR != nil {
m[machineCNSTagReversePTR] = &mcns.ReversePTR
}
if len(mcns.Services) > 0 {
m[machineCNSTagServices] = strings.Join(mcns.Services, ",")
}
}

6
vendor/vendor.json vendored
View File

@ -2294,10 +2294,10 @@
"revisionTime": "2016-06-16T18:50:15Z"
},
{
"checksumSHA1": "fue8Al8kqw/Q6VFPsNzoky7NIgo=",
"checksumSHA1": "2HimxaJVVp2QDVQ0570L71Zd5s4=",
"path": "github.com/joyent/triton-go",
"revision": "66b31a94af28a65e902423879a2820ea34b773fb",
"revisionTime": "2017-03-31T18:12:29Z"
"revision": "5db9e2b6a4c1f7ffd2a7e7aa625f42dba956608c",
"revisionTime": "2017-04-12T23:23:58Z"
},
{
"checksumSHA1": "QzUqkCSn/ZHyIK346xb9V6EBw9U=",