Photon Controller platform vendor code

This commit is contained in:
Miao Luo
2016-11-02 22:32:51 -07:00
parent b22ccc6780
commit a7fc0e4698
27 changed files with 4101 additions and 0 deletions

View File

@@ -0,0 +1,711 @@
// Copyright (c) 2016 VMware, Inc. All Rights Reserved.
//
// This product is licensed to you under the Apache License, Version 2.0 (the "License").
// You may not use this product except in compliance with the License.
//
// This product may include a number of subcomponents with separate copyright notices and
// license terms. Your use of these subcomponents is subject to the terms and conditions
// of the subcomponent's license, as noted in the LICENSE file.
package photon
import (
"fmt"
)
type Entity struct {
ID string `json:"id"`
Kind string `json:"kind"`
}
// Implement a generic sdk error
type SdkError struct {
Message string
}
func (e SdkError) Error() string {
return fmt.Sprintf("photon: %v", e.Message)
}
// Represents an error from the Photon API.
type ApiError struct {
Code string `json:"code"`
Data map[string]interface{} `json:"data"`
Message string `json:"message"`
HttpStatusCode int `json:"-"` // Not part of API contract
}
// Implement Go error interface for ApiError
func (e ApiError) Error() string {
return fmt.Sprintf(
"photon: { HTTP status: '%v', code: '%v', message: '%v', data: '%v' }",
e.HttpStatusCode,
e.Code,
e.Message,
e.Data)
}
// Used to represent a generic HTTP error, i.e. an unexpected HTTP 500.
type HttpError struct {
StatusCode int
Message string
}
// Implementation of error interface for HttpError
func (e HttpError) Error() string {
return fmt.Sprintf("photon: HTTP %d: %v", e.StatusCode, e.Message)
}
// Represents an Photon task that has entered into an error state.
// Photon task errors can be caught and type-checked against with
// the usual Go idiom.
type TaskError struct {
ID string `json:"id"`
Step Step `json:"step,omitempty"`
}
// Implement Go error interface for TaskError.
func (e TaskError) Error() string {
return fmt.Sprintf("photon: Task '%s' is in error state: {@step==%s}", e.ID, GetStep(e.Step))
}
// An error representing a timeout while waiting for a task to complete.
type TaskTimeoutError struct {
ID string
}
// Implement Go error interface for TaskTimeoutError.
func (e TaskTimeoutError) Error() string {
return fmt.Sprintf("photon: Timed out waiting for task '%s'. "+
"Task may not be in error state, examine task for full details.", e.ID)
}
// Represents an operation (Step) within a Task.
type Step struct {
ID string `json:"id"`
Operation string `json:"operation,omitempty"`
State string `json:"state"`
StartedTime int64 `json:"startedTime"`
EndTime int64 `json:"endTime,omitempty"`
QueuedTime int64 `json:"queuedTime"`
Sequence int `json:"sequence,omitempty"`
ResourceProperties map[string]interface{} `json:"resourceProperties,omitempty"`
Errors []ApiError `json:"errors,omitempty"`
Warnings []ApiError `json:"warnings,omitempty"`
Options map[string]interface{} `json:"options,omitempty"`
SelfLink string `json:"selfLink,omitempty"`
}
// Implement Go error interface for Step.
func GetStep(s Step) string {
return fmt.Sprintf("{\"sequence\"=>\"%d\",\"state\"=>\"%s\",\"errors\"=>%s,\"warnings\"=>%s,\"operation\"=>\"%s\","+
"\"startedTime\"=>\"%d\",\"queuedTime\"=>\"%d\",\"endTime\"=>\"%d\",\"options\"=>%s}",
s.Sequence, s.State, s.Errors, s.Warnings, s.Operation, s.StartedTime, s.QueuedTime,
s.EndTime, s.Options)
}
// Represents an asynchronous task.
type Task struct {
ID string `json:"id"`
Operation string `json:"operation,omitempty"`
State string `json:"state"`
StartedTime int64 `json:"startedTime"`
EndTime int64 `json:"endTime,omitempty"`
QueuedTime int64 `json:"queuedTime"`
Entity Entity `json:"entity,omitempty"`
SelfLink string `json:"selfLink,omitempty"`
Steps []Step `json:"steps,omitempty"`
ResourceProperties interface{} `json:"resourceProperties,omitempty"`
}
// Represents multiple tasks returned by the API.
type TaskList struct {
Items []Task `json:"items"`
}
// Options for GetTasks API.
type TaskGetOptions struct {
State string `urlParam:"state"`
Kind string `urlParam:"kind"`
EntityID string `urlParam:"entityId"`
EntityKind string `urlParam:"entityKind"`
}
type BaseCompact struct {
Name string `json:"name"`
ID string `json:"id"`
}
type QuotaLineItem struct {
Unit string `json:"unit"`
Value float64 `json:"value"`
Key string `json:"key"`
}
// Creation spec for locality.
type LocalitySpec struct {
Kind string `json:"kind"`
ID string `json:"id"`
}
// Creation spec for disks.
type DiskCreateSpec struct {
Flavor string `json:"flavor"`
Kind string `json:"kind"`
CapacityGB int `json:"capacityGb"`
Affinities []LocalitySpec `json:"affinities,omitempty"`
Name string `json:"name"`
Tags []string `json:"tags,omitempty"`
}
// Represents a persistent disk.
type PersistentDisk struct {
Flavor string `json:"flavor"`
Cost []QuotaLineItem `json:"cost"`
Kind string `json:"kind"`
Datastore string `json:"datastore,omitempty"`
CapacityGB int `json:"capacityGb,omitempty"`
Name string `json:"name"`
State string `json:"state"`
ID string `json:"id"`
VMs []string `json:"vms"`
Tags []string `json:"tags,omitempty"`
SelfLink string `json:"selfLink,omitempty"`
}
// Represents multiple persistent disks returned by the API.
type DiskList struct {
Items []PersistentDisk `json:"items"`
}
// Creation spec for projects.
type ProjectCreateSpec struct {
ResourceTicket ResourceTicketReservation `json:"resourceTicket"`
Name string `json:"name"`
SecurityGroups []string `json:"securityGroups,omitempty"`
}
// Represents multiple projects returned by the API.
type ProjectList struct {
Items []ProjectCompact `json:"items"`
}
// Compact representation of projects.
type ProjectCompact struct {
Kind string `json:"kind"`
ResourceTicket ProjectTicket `json:"resourceTicket"`
Name string `json:"name"`
ID string `json:"id"`
Tags []string `json:"tags"`
SelfLink string `json:"selfLink"`
SecurityGroups []SecurityGroup `json:"securityGroups"`
}
type ProjectTicket struct {
TenantTicketID string `json:"tenantTicketId"`
Usage []QuotaLineItem `json:"usage"`
TenantTicketName string `json:"tenantTicketName"`
Limits []QuotaLineItem `json:"limits"`
}
// Represents an image.
type Image struct {
Size int64 `json:"size"`
Kind string `json:"kind"`
Name string `json:"name"`
State string `json:"state"`
ID string `json:"id"`
Tags []string `json:"tags"`
SelfLink string `json:"selfLink"`
Settings []ImageSetting `json:"settings"`
ReplicationType string `json:"replicationType"`
ReplicationProgress string `json:"replicationProgress"`
SeedingProgress string `json:"seedingProgress"`
}
// Represents an image setting
type ImageSetting struct {
Name string `json:"name"`
DefaultValue string `json:"defaultValue"`
}
// Creation spec for images.
type ImageCreateOptions struct {
ReplicationType string
}
// Represents multiple images returned by the API.
type Images struct {
Items []Image `json:"items"`
}
// Represents a component with status.
type Component struct {
Component string
Message string
Status string
}
// Represents status of the photon system.
type Status struct {
Status string
Components []Component
}
// Represents a single tenant.
type Tenant struct {
Projects []BaseCompact `json:"projects"`
ResourceTickets []BaseCompact `json:"resourceTickets"`
Kind string `json:"kind"`
Name string `json:"name"`
ID string `json:"id"`
SelfLink string `json:"selfLink"`
Tags []string `json:"tags"`
SecurityGroups []SecurityGroup `json:"securityGroups"`
}
// Represents multiple tenants returned by the API.
type Tenants struct {
Items []Tenant `json:"items"`
}
// Creation spec for tenants.
type TenantCreateSpec struct {
Name string `json:"name"`
SecurityGroups []string `json:"securityGroups,omitempty"`
}
// Creation spec for resource tickets.
type ResourceTicketCreateSpec struct {
Name string `json:"name"`
Limits []QuotaLineItem `json:"limits"`
}
// Represents a single resource ticket.
type ResourceTicket struct {
Kind string `json:"kind"`
Usage []QuotaLineItem `json:"usage"`
TenantId string `json:"tenantId"`
Name string `json:"name"`
ID string `json:"id"`
Limits []QuotaLineItem `json:"limits"`
Tags []string `json:"tags"`
SelfLink string `json:"selfLink"`
}
// Represents multiple resource tickets returned by the API.
type ResourceList struct {
Items []ResourceTicket `json:"items"`
}
// Represents a resource reservation on a resource ticket.
type ResourceTicketReservation struct {
Name string `json:"name"`
Limits []QuotaLineItem `json:"limits"`
}
// Creation spec for VMs.
type VmCreateSpec struct {
Flavor string `json:"flavor"`
SourceImageID string `json:"sourceImageId"`
AttachedDisks []AttachedDisk `json:"attachedDisks"`
Affinities []LocalitySpec `json:"affinities,omitempty"`
Name string `json:"name"`
Tags []string `json:"tags,omitempty"`
Subnets []string `json:"subnets,omitempty"`
Environment map[string]string `json:"environment,omitempty"`
}
// Represents possible operations for VMs. Valid values include:
// START_VM, STOP_VM, RESTART_VM, SUSPEND_VM, RESUME_VM
type VmOperation struct {
Operation string `json:"operation"`
Arguments map[string]interface{} `json:"arguments,omitempty"`
}
// Represents metadata that can be set on a VM.
type VmMetadata struct {
Metadata map[string]string `json:"metadata"`
}
// Represents tags that can be set on a VM.
type VmTag struct {
Tag string `json:"value"`
}
// Represents a single attached disk.
type AttachedDisk struct {
Flavor string `json:"flavor"`
Kind string `json:"kind"`
CapacityGB int `json:"capacityGb,omitempty"`
Name string `json:"name"`
State string `json:"state"`
ID string `json:"id,omitempty"`
BootDisk bool `json:"bootDisk"`
}
// Represents a single VM.
type VM struct {
SourceImageID string `json:"sourceImageId,omitempty"`
Cost []QuotaLineItem `json:"cost"`
Kind string `json:"kind"`
AttachedDisks []AttachedDisk `json:"attachedDisks"`
Datastore string `json:"datastore,omitempty"`
AttachedISOs []ISO `json:"attachedIsos,omitempty"`
Tags []string `json:"tags,omitempty"`
Metadata map[string]string `json:"metadata,omitempty"`
SelfLink string `json:"selfLink,omitempty"`
Flavor string `json:"flavor"`
Host string `json:"host,omitempty"`
Name string `json:"name"`
State string `json:"state"`
ID string `json:"id"`
FloatingIp string `json:"floatingIp"`
}
// Represents multiple VMs returned by the API.
type VMs struct {
Items []VM `json:"items"`
}
// Represents an ISO.
type ISO struct {
Size int64 `json:"size,omitempty"`
Kind string `json:"kind,omitempty"`
Name string `json:"name"`
ID string `json:"id"`
}
// Represents operations for disks.
type VmDiskOperation struct {
DiskID string `json:"diskId"`
Arguments map[string]interface{} `json:"arguments,omitempty"`
}
// Represents a floating IP operation related to a VM.
type VmFloatingIpSpec struct {
NetworkId string `json:"networkId"`
}
// Creation spec for flavors.
type FlavorCreateSpec struct {
Cost []QuotaLineItem `json:"cost"`
Kind string `json:"kind"`
Name string `json:"name"`
}
// Represents a single flavor.
type Flavor struct {
Cost []QuotaLineItem `json:"cost"`
Kind string `json:"kind"`
Name string `json:"name"`
ID string `json:"id"`
Tags []string `json:"tags"`
SelfLink string `json:"selfLink"`
State string `json:"state"`
}
// Represents multiple flavors returned by the API.
type FlavorList struct {
Items []Flavor `json:"items"`
}
// Creation spec for hosts.
type HostCreateSpec struct {
Username string `json:"username"`
Password string `json:"password"`
AvailabilityZone string `json:"availabilityZone,omitempty"`
Metadata map[string]string `json:"metadata,omitempty"`
Address string `json:"address"`
Tags []string `json:"usageTags"`
}
// Represents a host
type Host struct {
Username string `json:"username"`
Password string `json:"password"`
Address string `json:"address"`
Kind string `json:"kind"`
ID string `json:"id"`
AvailabilityZone string `json:"availabilityZone,omitempty"`
Tags []string `json:"usageTags"`
Metadata map[string]string `json:"metadata,omitempty"`
SelfLink string `json:"selfLink"`
State string `json:"state"`
EsxVersion string `json:"esxVersion"`
}
// Represents multiple hosts returned by the API.
type Hosts struct {
Items []Host `json:"items"`
}
// Creation spec for deployments.
type DeploymentCreateSpec struct {
NTPEndpoint interface{} `json:"ntpEndpoint"`
UseImageDatastoreForVms bool `json:"useImageDatastoreForVms"`
SyslogEndpoint interface{} `json:"syslogEndpoint"`
Stats *StatsInfo `json:"stats"`
ImageDatastores []string `json:"imageDatastores"`
Auth *AuthInfo `json:"auth"`
NetworkConfiguration *NetworkConfigurationCreateSpec `json:"networkConfiguration"`
LoadBalancerEnabled bool `json:"loadBalancerEnabled"`
}
// Deployment deploy config.
type DeploymentDeployOperation struct {
DesiredState string `json:"desiredState"`
}
type MigrationStatus struct {
CompletedDataMigrationCycles int `json:"completedDataMigrationCycles"`
DataMigrationCycleProgress int `json:"dataMigrationCycleProgress"`
DataMigrationCycleSize int `json:"dataMigrationCycleSize"`
VibsUploaded int `json:"vibsUploaded"`
VibsUploading int `json:"vibsUploading"`
}
// Represents a deployment
type Deployment struct {
NTPEndpoint string `json:"ntpEndpoint,omitempty"`
UseImageDatastoreForVms bool `json:"useImageDatastoreForVms,omitempty"`
Auth *AuthInfo `json:"auth"`
NetworkConfiguration *NetworkConfiguration `json:"networkConfiguration"`
Kind string `json:"kind"`
SyslogEndpoint string `json:"syslogEndpoint,omitempty"`
Stats *StatsInfo `json:"stats,omitempty"`
State string `json:"state"`
ID string `json:"id"`
ImageDatastores []string `json:"imageDatastores"`
SelfLink string `json:"selfLink"`
Migration *MigrationStatus `json:"migrationStatus,omitempty"`
ClusterConfigurations []ClusterConfiguration `json:"clusterConfigurations,omitempty"`
LoadBalancerEnabled bool `json:"loadBalancerEnabled"`
LoadBalancerAddress string `json:"loadBalancerAddress"`
}
// Represents multiple deployments returned by the API.
type Deployments struct {
Items []Deployment `json:"items"`
}
// Represents source load balacer address to migrate deployment
type InitializeMigrationOperation struct {
SourceNodeGroupReference string `json:"sourceNodeGroupReference"`
}
// Represents source load balacer address to finish migration of deployment
type FinalizeMigrationOperation struct {
SourceNodeGroupReference string `json:"sourceNodeGroupReference"`
}
// Represents stats information
type StatsInfo struct {
Enabled bool `json:"enabled,omitempty"`
StoreEndpoint string `json:"storeEndpoint,omitempty"`
StorePort int `json:"storePort,omitempty"`
}
// Represents authentication information
type AuthInfo struct {
Password string `json:"password,omitempty"`
Endpoint string `json:"endpoint,omitempty"`
Tenant string `json:"tenant,omitempty"`
Port int `json:"port,omitempty"`
SecurityGroups []string `json:"securityGroups,omitempty"`
Enabled bool `json:"enabled,omitempty"`
Username string `json:"username,omitempty"`
}
// Represents ip range
type IpRange struct {
Start string `json:"start,omitempty"`
End string `json:"end,omitempty"`
}
// Represents creation spec for network configuration.
type NetworkConfigurationCreateSpec struct {
Enabled bool `json:"sdnEnabled,omitempty"`
Address string `json:"networkManagerAddress,omitempty"`
Username string `json:"networkManagerUsername,omitempty"`
Password string `json:"networkManagerPassword,omitempty"`
NetworkZoneId string `json:"networkZoneId,omitempty"`
TopRouterId string `json:"networkTopRouterId,omitempty"`
IpRange string `json:"ipRange,omitempty"`
ExternalIpRange *IpRange `json:"externalIpRange,omitempty"`
DhcpServers []string `json:"dhcpServers,omitempty"`
}
// Represents network configuration.
type NetworkConfiguration struct {
Enabled bool `json:"sdnEnabled,omitempty"`
Address string `json:"networkManagerAddress,omitempty"`
Username string `json:"networkManagerUsername,omitempty"`
Password string `json:"networkManagerPassword,omitempty"`
NetworkZoneId string `json:"networkZoneId,omitempty"`
TopRouterId string `json:"networkTopRouterId,omitempty"`
IpRange string `json:"ipRange,omitempty"`
FloatingIpRange *IpRange `json:"floatingIpRange,omitempty"`
SnatIp string `json:"snatIp,omitempty"`
DhcpServers []string `json:"dhcpServers,omitempty"`
}
// Creation spec for subnets.
type SubnetCreateSpec struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
PortGroups []string `json:"portGroups"`
}
// Represents a subnet
type Subnet struct {
Kind string `json:"kind"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
State string `json:"state"`
ID string `json:"id"`
PortGroups []string `json:"portGroups"`
Tags []string `json:"tags,omitempty"`
SelfLink string `json:"selfLink"`
IsDefault bool `json:"isDefault"`
}
// Represents multiple subnets returned by the API
type Subnets struct {
Items []Subnet `json:"items"`
}
// Create spec for virtual subnet
type VirtualSubnetCreateSpec struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
RoutingType string `json:"routingType"`
Size int `json:"size"`
ReservedStaticIpSize int `json:"reservedStaticIpSize,omitempty"`
}
// Represents a virtual network
type VirtualSubnet struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
State string `json:"state"`
RoutingType string `json:"routingType"`
IsDefault string `json:"isDefault"`
Cidr string `json:"cidr,omitempty"`
LowIpDynamic string `json:"lowIpDynamic,omitempty"`
HighIpDynamic string `json:"highIpDynamic,omitempty"`
LowIpStatic string `json:"lowIpStatic,omitempty"`
HighIpStatic string `json:"highIpStatic,omitempty"`
ReservedIpList []string `json:"reservedIpList"`
SelfLink string `json:"selfLink"`
}
// Represents multiple virtual subnets returned
type VirtualSubnets struct {
Items []VirtualSubnet `json:"items"`
}
// Creation spec for Cluster Configuration.
type ClusterConfigurationSpec struct {
Type string `json:"type"`
ImageID string `json:"imageId"`
}
// Represnts a Cluster configuration.
type ClusterConfiguration struct {
Kind string `json:"kind"`
Type string `json:"type"`
ImageID string `json:"imageId"`
}
// Creation spec for clusters.
type ClusterCreateSpec struct {
Name string `json:"name"`
Type string `json:"type"`
VMFlavor string `json:"vmFlavor,omitempty"`
DiskFlavor string `json:"diskFlavor,omitempty"`
NetworkID string `json:"vmNetworkId,omitempty"`
WorkerCount int `json:"workerCount"`
BatchSizeWorker int `json:"workerBatchExpansionSize,omitempty"`
ExtendedProperties map[string]string `json:"extendedProperties"`
}
// Represents a cluster
type Cluster struct {
Kind string `json:"kind"`
Name string `json:"name"`
State string `json:"state"`
ID string `json:"id"`
Type string `json:"type"`
ProjectID string `json:"projectID,omitempty"`
WorkerCount int `json:"workerCount"`
SelfLink string `json:"selfLink,omitempty"`
ErrorReason string `json:"errorReason,omitempty"`
ExtendedProperties map[string]string `json:"extendedProperties"`
}
// Represents multiple clusters returned by the API
type Clusters struct {
Items []Cluster `json:"items"`
}
// Represents cluster size that can be resized for cluster
type ClusterResizeOperation struct {
NewWorkerCount int `json:"newWorkerCount"`
}
// Represents a security group
type SecurityGroup struct {
Name string `json:"name"`
Inherited bool `json:"inherited"`
}
// Represents set_security_groups spec
type SecurityGroupsSpec struct {
Items []string `json:"items"`
}
// Represents single availability zone.
type AvailabilityZone struct {
Kind string `json:"kind"`
Name string `json:"name"`
State string `json:"state"`
ID string `json:"id"`
SelfLink string `json:"selfLink"`
}
// Represents multiple availability zones returned by the API.
type AvailabilityZones struct {
Items []AvailabilityZone `json:"items"`
}
// Creation spec for availability zones.
type AvailabilityZoneCreateSpec struct {
Name string `json:"name"`
}
// Represents availability zone that can be set for host
type HostSetAvailabilityZoneOperation struct {
AvailabilityZoneId string `json:"availabilityZoneId"`
}
// Represents the list of image datastores.
type ImageDatastores struct {
Items []string `json:"items"`
}
// Image creation spec.
type ImageCreateSpec struct {
Name string `json:"name"`
ReplicationType string `json:"replicationType"`
}
// Represents deployment info
type Info struct {
BaseVersion string `json:"baseVersion"`
FullVersion string `json:"fullVersion"`
GitCommitHash string `json:"gitCommitHash"`
NetworkType string `json:"networkType"`
}

View File

@@ -0,0 +1,130 @@
// Copyright (c) 2016 VMware, Inc. All Rights Reserved.
//
// This product is licensed to you under the Apache License, Version 2.0 (the "License").
// You may not use this product except in compliance with the License.
//
// This product may include a number of subcomponents with separate copyright notices and
// license terms. Your use of these subcomponents is subject to the terms and conditions
// of the subcomponent's license, as noted in the LICENSE file.
package photon
import (
"encoding/json"
"fmt"
"github.com/vmware/photon-controller-go-sdk/photon/lightwave"
)
// Contains functionality for auth API.
type AuthAPI struct {
client *Client
}
const authUrl string = "/auth"
// Gets authentication info.
func (api *AuthAPI) Get() (info *AuthInfo, err error) {
res, err := api.client.restClient.Get(api.client.Endpoint+authUrl, "")
if err != nil {
return
}
defer res.Body.Close()
res, err = getError(res)
if err != nil {
return
}
info = &AuthInfo{}
err = json.NewDecoder(res.Body).Decode(info)
return
}
// Gets Tokens from username/password.
func (api *AuthAPI) GetTokensByPassword(username string, password string) (tokenOptions *TokenOptions, err error) {
oidcClient, err := api.buildOIDCClient()
if err != nil {
return
}
tokenResponse, err := oidcClient.GetTokenByPasswordGrant(username, password)
if err != nil {
return
}
return api.toTokenOptions(tokenResponse), nil
}
// Gets tokens from refresh token.
func (api *AuthAPI) GetTokensByRefreshToken(refreshtoken string) (tokenOptions *TokenOptions, err error) {
oidcClient, err := api.buildOIDCClient()
if err != nil {
return
}
tokenResponse, err := oidcClient.GetTokenByRefreshTokenGrant(refreshtoken)
if err != nil {
return
}
return api.toTokenOptions(tokenResponse), nil
}
func (api *AuthAPI) getAuthEndpoint() (endpoint string, err error) {
authInfo, err := api.client.Auth.Get()
if err != nil {
return
}
if !authInfo.Enabled {
return "", SdkError{Message: "Authentication not enabled on this endpoint"}
}
if authInfo.Port == 0 {
authInfo.Port = 443
}
return fmt.Sprintf("https://%s:%d", authInfo.Endpoint, authInfo.Port), nil
}
func (api *AuthAPI) buildOIDCClient() (client *lightwave.OIDCClient, err error) {
authEndPoint, err := api.getAuthEndpoint()
if err != nil {
return
}
return lightwave.NewOIDCClient(
authEndPoint,
api.buildOIDCClientOptions(&api.client.options),
api.client.restClient.logger), nil
}
const tokenScope string = "openid offline_access rs_esxcloud at_groups"
func (api *AuthAPI) buildOIDCClientOptions(options *ClientOptions) *lightwave.OIDCClientOptions {
return &lightwave.OIDCClientOptions{
IgnoreCertificate: api.client.options.IgnoreCertificate,
RootCAs: api.client.options.RootCAs,
TokenScope: tokenScope,
}
}
func (api *AuthAPI) toTokenOptions(response *lightwave.OIDCTokenResponse) *TokenOptions {
return &TokenOptions{
AccessToken: response.AccessToken,
ExpiresIn: response.ExpiresIn,
RefreshToken: response.RefreshToken,
IdToken: response.IdToken,
TokenType: response.TokenType,
}
}
// Parse the given token details.
func (api *AuthAPI) parseTokenDetails(token string) (jwtToken *lightwave.JWTToken, err error) {
jwtToken = lightwave.ParseTokenDetails(token)
return jwtToken, nil
}
// Parse the given token raw details.
func (api *AuthAPI) parseRawTokenDetails(token string) (jwtToken []string, err error) {
jwtToken, err = lightwave.ParseRawTokenDetails(token)
return jwtToken, err
}

View File

@@ -0,0 +1,102 @@
// Copyright (c) 2016 VMware, Inc. All Rights Reserved.
//
// This product is licensed to you under the Apache License, Version 2.0 (the "License").
// You may not use this product except in compliance with the License.
//
// This product may include a number of subcomponents with separate copyright notices and
// license terms. Your use of these subcomponents is subject to the terms and conditions
// of the subcomponent's license, as noted in the LICENSE file.
package photon
import (
"bytes"
"encoding/json"
)
// Contains functionality for availability zones API.
type AvailabilityZonesAPI struct {
client *Client
}
var availabilityzoneUrl string = "/availabilityzones"
// Creates availability zone.
func (api *AvailabilityZonesAPI) Create(availabilityzoneSpec *AvailabilityZoneCreateSpec) (task *Task, err error) {
body, err := json.Marshal(availabilityzoneSpec)
if err != nil {
return
}
res, err := api.client.restClient.Post(
api.client.Endpoint+availabilityzoneUrl,
"application/json",
bytes.NewReader(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Gets availability zone with the specified ID.
func (api *AvailabilityZonesAPI) Get(id string) (availabilityzone *AvailabilityZone, err error) {
res, err := api.client.restClient.Get(api.getEntityUrl(id), api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
res, err = getError(res)
if err != nil {
return
}
availabilityzone = &AvailabilityZone{}
err = json.NewDecoder(res.Body).Decode(availabilityzone)
return
}
// Returns all availability zones on an photon instance.
func (api *AvailabilityZonesAPI) GetAll() (result *AvailabilityZones, err error) {
uri := api.client.Endpoint + availabilityzoneUrl
res, err := api.client.restClient.GetList(api.client.Endpoint, uri, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
result = &AvailabilityZones{}
err = json.Unmarshal(res, result)
return
}
// Deletes the availability zone with specified ID.
func (api *AvailabilityZonesAPI) Delete(id string) (task *Task, err error) {
res, err := api.client.restClient.Delete(api.client.Endpoint+availabilityzoneUrl+"/"+id, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Gets all tasks with the specified availability zone ID, using options to filter the results.
// If options is nil, no filtering will occur.
func (api *AvailabilityZonesAPI) GetTasks(id string, options *TaskGetOptions) (result *TaskList, err error) {
uri := api.client.Endpoint + availabilityzoneUrl + "/" + id + "/tasks"
if options != nil {
uri += getQueryString(options)
}
res, err := api.client.restClient.GetList(api.client.Endpoint, uri, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
result = &TaskList{}
err = json.Unmarshal(res, result)
return
}
func (api *AvailabilityZonesAPI) getEntityUrl(id string) (url string) {
return api.client.Endpoint + availabilityzoneUrl + "/" + id
}

View File

@@ -0,0 +1,169 @@
// Copyright (c) 2016 VMware, Inc. All Rights Reserved.
//
// This product is licensed to you under the Apache License, Version 2.0 (the "License").
// You may not use this product except in compliance with the License.
//
// This product may include a number of subcomponents with separate copyright notices and
// license terms. Your use of these subcomponents is subject to the terms and conditions
// of the subcomponent's license, as noted in the LICENSE file.
package photon
import (
"crypto/tls"
"crypto/x509"
"io/ioutil"
"log"
"net/http"
"strings"
"time"
)
// Represents stateless context needed to call photon APIs.
type Client struct {
options ClientOptions
restClient *restClient
logger *log.Logger
Endpoint string
Status *StatusAPI
Tenants *TenantsAPI
Tasks *TasksAPI
Projects *ProjectsAPI
Flavors *FlavorsAPI
Images *ImagesAPI
Disks *DisksAPI
VMs *VmAPI
Hosts *HostsAPI
Deployments *DeploymentsAPI
ResourceTickets *ResourceTicketsAPI
Subnets *SubnetsAPI
VirtualSubnets *VirtualSubnetsAPI
Clusters *ClustersAPI
Auth *AuthAPI
AvailabilityZones *AvailabilityZonesAPI
Info *InfoAPI
}
// Represents Tokens
type TokenOptions struct {
AccessToken string `json:"access_token"`
ExpiresIn int `json:"expires_in"`
RefreshToken string `json:"refresh_token,omitempty"`
IdToken string `json:"id_token"`
TokenType string `json:"token_type"`
}
// Options for Client
type ClientOptions struct {
// When using the Tasks.Wait APIs, defines the duration of how long
// the SDK should continue to poll the server. Default is 30 minutes.
// TasksAPI.WaitTimeout() can be used to specify timeout on
// individual calls.
TaskPollTimeout time.Duration
// Whether or not to ignore any TLS errors when talking to photon,
// false by default.
IgnoreCertificate bool
// List of root CA's to use for server validation
// nil by default.
RootCAs *x509.CertPool
// For tasks APIs, defines the delay between each polling attempt.
// Default is 100 milliseconds.
TaskPollDelay time.Duration
// For tasks APIs, defines the number of retries to make in the event
// of an error. Default is 3.
TaskRetryCount int
// Tokens for user authentication. Default is empty.
TokenOptions *TokenOptions
}
// Creates a new photon client with specified options. If options
// is nil, default options will be used.
func NewClient(endpoint string, options *ClientOptions, logger *log.Logger) (c *Client) {
defaultOptions := &ClientOptions{
TaskPollTimeout: 30 * time.Minute,
TaskPollDelay: 100 * time.Millisecond,
TaskRetryCount: 3,
TokenOptions: &TokenOptions{},
IgnoreCertificate: false,
RootCAs: nil,
}
if options != nil {
if options.TaskPollTimeout != 0 {
defaultOptions.TaskPollTimeout = options.TaskPollTimeout
}
if options.TaskPollDelay != 0 {
defaultOptions.TaskPollDelay = options.TaskPollDelay
}
if options.TaskRetryCount != 0 {
defaultOptions.TaskRetryCount = options.TaskRetryCount
}
if options.TokenOptions != nil {
defaultOptions.TokenOptions = options.TokenOptions
}
if options.RootCAs != nil {
defaultOptions.RootCAs = options.RootCAs
}
defaultOptions.IgnoreCertificate = options.IgnoreCertificate
}
if logger == nil {
logger = createPassThroughLogger()
}
tr := &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: defaultOptions.IgnoreCertificate,
RootCAs: defaultOptions.RootCAs},
}
endpoint = strings.TrimRight(endpoint, "/")
restClient := &restClient{
httpClient: &http.Client{Transport: tr},
logger: logger,
}
c = &Client{Endpoint: endpoint, restClient: restClient, logger: logger}
// Ensure a copy of options is made, rather than using a pointer
// which may change out from underneath if misused by the caller.
c.options = *defaultOptions
c.Status = &StatusAPI{c}
c.Tenants = &TenantsAPI{c}
c.Tasks = &TasksAPI{c}
c.Projects = &ProjectsAPI{c}
c.Flavors = &FlavorsAPI{c}
c.Images = &ImagesAPI{c}
c.Disks = &DisksAPI{c}
c.VMs = &VmAPI{c}
c.Hosts = &HostsAPI{c}
c.Deployments = &DeploymentsAPI{c}
c.ResourceTickets = &ResourceTicketsAPI{c}
c.Subnets = &SubnetsAPI{c}
c.VirtualSubnets = &VirtualSubnetsAPI{c}
c.Clusters = &ClustersAPI{c}
c.Auth = &AuthAPI{c}
c.AvailabilityZones = &AvailabilityZonesAPI{c}
c.Info = &InfoAPI{c}
return
}
// Creates a new photon client with specified options and http.Client.
// Useful for functional testing where http calls must be mocked out.
// If options is nil, default options will be used.
func NewTestClient(endpoint string, options *ClientOptions, httpClient *http.Client) (c *Client) {
c = NewClient(endpoint, options, nil)
c.restClient.httpClient = httpClient
return
}
func createPassThroughLogger() (l *log.Logger) {
// ioutil.Discard makes all logging operation be a no-op.
return log.New(ioutil.Discard, "", log.LstdFlags)
}

View File

@@ -0,0 +1,112 @@
// Copyright (c) 2016 VMware, Inc. All Rights Reserved.
//
// This product is licensed to you under the Apache License, Version 2.0 (the "License").
// You may not use this product except in compliance with the License.
//
// This product may include a number of subcomponents with separate copyright notices and
// license terms. Your use of these subcomponents is subject to the terms and conditions
// of the subcomponent's license, as noted in the LICENSE file.
package photon
import (
"bytes"
"encoding/json"
)
// Contains functionality for clusters API.
type ClustersAPI struct {
client *Client
}
var clusterUrl string = "/clusters/"
const ExtendedPropertyDNS string = "dns"
const ExtendedPropertyGateway string = "gateway"
const ExtendedPropertyNetMask string = "netmask"
const ExtendedPropertyMasterIP string = "master_ip"
const ExtendedPropertyContainerNetwork string = "container_network"
const ExtendedPropertyZookeeperIP1 string = "zookeeper_ip1"
const ExtendedPropertyZookeeperIP2 string = "zookeeper_ip2"
const ExtendedPropertyZookeeperIP3 string = "zookeeper_ip3"
const ExtendedPropertyETCDIP1 string = "etcd_ip1"
const ExtendedPropertyETCDIP2 string = "etcd_ip2"
const ExtendedPropertyETCDIP3 string = "etcd_ip3"
const ExtendedPropertySSHKey string = "ssh_key"
const ExtendedPropertyRegistryCACert string = "registry_ca_cert"
const ExtendedPropertyAdminPassword string = "admin_password"
// Deletes a cluster with specified ID.
func (api *ClustersAPI) Delete(id string) (task *Task, err error) {
res, err := api.client.restClient.Delete(api.client.Endpoint+clusterUrl+id, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Gets a cluster with the specified ID.
func (api *ClustersAPI) Get(id string) (cluster *Cluster, err error) {
res, err := api.client.restClient.Get(api.client.Endpoint+clusterUrl+id, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
res, err = getError(res)
if err != nil {
return
}
var result Cluster
err = json.NewDecoder(res.Body).Decode(&result)
return &result, nil
}
// Gets vms for cluster with the specified ID.
func (api *ClustersAPI) GetVMs(id string) (result *VMs, err error) {
uri := api.client.Endpoint + clusterUrl + id + "/vms"
res, err := api.client.restClient.GetList(api.client.Endpoint, uri, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
result = &VMs{}
err = json.Unmarshal(res, result)
return
}
// Resize a cluster to specified count.
func (api *ClustersAPI) Resize(id string, resize *ClusterResizeOperation) (task *Task, err error) {
body, err := json.Marshal(resize)
if err != nil {
return
}
res, err := api.client.restClient.Post(
api.client.Endpoint+clusterUrl+id+"/resize",
"application/json",
bytes.NewReader(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Start a background process to recreate failed VMs in a cluster with the specified ID.
func (api *ClustersAPI) TriggerMaintenance(id string) (task *Task, err error) {
body := []byte{}
res, err := api.client.restClient.Post(
api.client.Endpoint+clusterUrl+id+"/trigger_maintenance",
"application/json",
bytes.NewReader(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}

View File

@@ -0,0 +1,300 @@
// Copyright (c) 2016 VMware, Inc. All Rights Reserved.
//
// This product is licensed to you under the Apache License, Version 2.0 (the "License").
// You may not use this product except in compliance with the License.
//
// This product may include a number of subcomponents with separate copyright notices and
// license terms. Your use of these subcomponents is subject to the terms and conditions
// of the subcomponent's license, as noted in the LICENSE file.
package photon
import (
"bytes"
"encoding/json"
)
// Contains functionality for deployments API.
type DeploymentsAPI struct {
client *Client
}
var deploymentUrl string = "/deployments"
// Creates a deployment
func (api *DeploymentsAPI) Create(deploymentSpec *DeploymentCreateSpec) (task *Task, err error) {
body, err := json.Marshal(deploymentSpec)
if err != nil {
return
}
res, err := api.client.restClient.Post(
api.client.Endpoint+deploymentUrl,
"application/json",
bytes.NewBuffer(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Deletes a deployment with specified ID.
func (api *DeploymentsAPI) Delete(id string) (task *Task, err error) {
res, err := api.client.restClient.Delete(api.getEntityUrl(id), api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Deploys a deployment with specified ID.
func (api *DeploymentsAPI) Deploy(id string, config *DeploymentDeployOperation) (task *Task, err error) {
body, err := json.Marshal(config)
if err != nil {
return
}
res, err := api.client.restClient.Post(
api.getEntityUrl(id)+"/deploy",
"application/json",
bytes.NewBuffer(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Destroys a deployment with specified ID.
func (api *DeploymentsAPI) Destroy(id string) (task *Task, err error) {
res, err := api.client.restClient.Post(
api.getEntityUrl(id)+"/destroy",
"application/json",
bytes.NewBuffer([]byte("")),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Returns all deployments.
func (api *DeploymentsAPI) GetAll() (result *Deployments, err error) {
res, err := api.client.restClient.Get(api.client.Endpoint+deploymentUrl, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
res, err = getError(res)
if err != nil {
return
}
result = &Deployments{}
err = json.NewDecoder(res.Body).Decode(result)
return
}
// Gets a deployment with the specified ID.
func (api *DeploymentsAPI) Get(id string) (deployment *Deployment, err error) {
res, err := api.client.restClient.Get(api.getEntityUrl(id), api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
res, err = getError(res)
if err != nil {
return
}
var result Deployment
err = json.NewDecoder(res.Body).Decode(&result)
return &result, nil
}
// Gets all hosts with the specified deployment ID.
func (api *DeploymentsAPI) GetHosts(id string) (result *Hosts, err error) {
uri := api.getEntityUrl(id) + "/hosts"
res, err := api.client.restClient.GetList(api.client.Endpoint, uri, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
result = &Hosts{}
err = json.Unmarshal(res, result)
return
}
// Gets all the vms with the specified deployment ID.
func (api *DeploymentsAPI) GetVms(id string) (result *VMs, err error) {
uri := api.getEntityUrl(id) + "/vms"
res, err := api.client.restClient.GetList(api.client.Endpoint, uri, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
result = &VMs{}
err = json.Unmarshal(res, result)
return
}
// Initialize deployment migration from source to destination
func (api *DeploymentsAPI) InitializeDeploymentMigration(sourceAddress *InitializeMigrationOperation, id string) (task *Task, err error) {
body, err := json.Marshal(sourceAddress)
if err != nil {
return
}
res, err := api.client.restClient.Post(
api.getEntityUrl(id)+"/initialize_migration",
"application/json",
bytes.NewBuffer(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Finalize deployment migration from source to destination
func (api *DeploymentsAPI) FinalizeDeploymentMigration(sourceAddress *FinalizeMigrationOperation, id string) (task *Task, err error) {
body, err := json.Marshal(sourceAddress)
if err != nil {
return
}
res, err := api.client.restClient.Post(
api.getEntityUrl(id)+"/finalize_migration",
"application/json",
bytes.NewBuffer(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
func (api *DeploymentsAPI) SetSecurityGroups(id string, securityGroups *SecurityGroupsSpec) (*Task, error) {
return setSecurityGroups(api.client, api.getEntityUrl(id), securityGroups)
}
// Update image datastores of a deployment.
func (api *DeploymentsAPI) SetImageDatastores(id string, imageDatastores *ImageDatastores) (task *Task, err error) {
body, err := json.Marshal(imageDatastores)
if err != nil {
return
}
res, err := api.client.restClient.Post(
api.getEntityUrl(id)+"/set_image_datastores",
"application/json",
bytes.NewBuffer(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Pause system with specified deployment ID.
func (api *DeploymentsAPI) PauseSystem(id string) (task *Task, err error) {
res, err := api.client.restClient.Post(
api.getEntityUrl(id)+"/pause_system",
"application/json",
bytes.NewBuffer([]byte("")),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Pause background tasks of system with specified deployment ID.
func (api *DeploymentsAPI) PauseBackgroundTasks(id string) (task *Task, err error) {
res, err := api.client.restClient.Post(
api.getEntityUrl(id)+"/pause_background_tasks",
"application/json",
bytes.NewBuffer([]byte("")),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Pause background tasks of system with specified deployment ID.
func (api *DeploymentsAPI) ResumeSystem(id string) (task *Task, err error) {
res, err := api.client.restClient.Post(
api.getEntityUrl(id)+"/resume_system",
"application/json",
bytes.NewBuffer([]byte("")),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Enable cluster type with specified deployment ID.
func (api *DeploymentsAPI) EnableClusterType(id string, clusterConfigSpec *ClusterConfigurationSpec) (task *Task, err error) {
body, err := json.Marshal(clusterConfigSpec)
if err != nil {
return
}
res, err := api.client.restClient.Post(
api.getEntityUrl(id)+"/enable_cluster_type",
"application/json",
bytes.NewBuffer(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Disable cluster type with specified deployment ID.
func (api *DeploymentsAPI) DisableClusterType(id string, clusterConfigSpec *ClusterConfigurationSpec) (task *Task, err error) {
body, err := json.Marshal(clusterConfigSpec)
if err != nil {
return
}
res, err := api.client.restClient.Post(
api.getEntityUrl(id)+"/disable_cluster_type",
"application/json",
bytes.NewBuffer(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
func (api *DeploymentsAPI) getEntityUrl(id string) (url string) {
return api.client.Endpoint + deploymentUrl + "/" + id
}

View File

@@ -0,0 +1,65 @@
// Copyright (c) 2016 VMware, Inc. All Rights Reserved.
//
// This product is licensed to you under the Apache License, Version 2.0 (the "License").
// You may not use this product except in compliance with the License.
//
// This product may include a number of subcomponents with separate copyright notices and
// license terms. Your use of these subcomponents is subject to the terms and conditions
// of the subcomponent's license, as noted in the LICENSE file.
package photon
import (
"encoding/json"
)
// Contains functionality for disks API.
type DisksAPI struct {
client *Client
}
var diskUrl string = "/disks/"
// Gets a PersistentDisk for the disk with specified ID.
func (api *DisksAPI) Get(diskID string) (disk *PersistentDisk, err error) {
res, err := api.client.restClient.Get(api.client.Endpoint+diskUrl+diskID, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
res, err = getError(res)
if err != nil {
return
}
disk = &PersistentDisk{}
err = json.NewDecoder(res.Body).Decode(disk)
return
}
// Deletes a disk with the specified ID.
func (api *DisksAPI) Delete(diskID string) (task *Task, err error) {
res, err := api.client.restClient.Delete(api.client.Endpoint+diskUrl+diskID, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Gets all tasks with the specified disk ID, using options to filter the results.
// If options is nil, no filtering will occur.
func (api *DisksAPI) GetTasks(id string, options *TaskGetOptions) (result *TaskList, err error) {
uri := api.client.Endpoint + diskUrl + id + "/tasks"
if options != nil {
uri += getQueryString(options)
}
res, err := api.client.restClient.GetList(api.client.Endpoint, uri, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
result = &TaskList{}
err = json.Unmarshal(res, result)
return
}

View File

@@ -0,0 +1,108 @@
// Copyright (c) 2016 VMware, Inc. All Rights Reserved.
//
// This product is licensed to you under the Apache License, Version 2.0 (the "License").
// You may not use this product except in compliance with the License.
//
// This product may include a number of subcomponents with separate copyright notices and
// license terms. Your use of these subcomponents is subject to the terms and conditions
// of the subcomponent's license, as noted in the LICENSE file.
package photon
import (
"bytes"
"encoding/json"
)
// Contains functionality for flavors API.
type FlavorsAPI struct {
client *Client
}
// Options used for find/get APIs
type FlavorGetOptions struct {
Name string `urlParam:"name"`
Kind string `urlParam:"kind"`
}
var flavorUrl string = "/flavors"
// Creates a flavor.
func (api *FlavorsAPI) Create(spec *FlavorCreateSpec) (task *Task, err error) {
body, err := json.Marshal(spec)
if err != nil {
return
}
res, err := api.client.restClient.Post(
api.client.Endpoint+flavorUrl,
"application/json",
bytes.NewReader(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Gets details of flavor with specified ID.
func (api *FlavorsAPI) Get(flavorID string) (flavor *Flavor, err error) {
res, err := api.client.restClient.Get(api.client.Endpoint+flavorUrl+"/"+flavorID, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
res, err = getError(res)
if err != nil {
return
}
flavor = &Flavor{}
err = json.NewDecoder(res.Body).Decode(flavor)
return
}
// Gets flavors using options to filter results. Returns all flavors if options is nil.
func (api *FlavorsAPI) GetAll(options *FlavorGetOptions) (flavors *FlavorList, err error) {
uri := api.client.Endpoint + flavorUrl
if options != nil {
uri += getQueryString(options)
}
res, err := api.client.restClient.GetList(api.client.Endpoint, uri, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
flavors = &FlavorList{}
err = json.Unmarshal(res, flavors)
return
}
// Deletes flavor with specified ID.
func (api *FlavorsAPI) Delete(flavorID string) (task *Task, err error) {
res, err := api.client.restClient.Delete(api.client.Endpoint+flavorUrl+"/"+flavorID, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Gets all tasks with the specified flavor ID, using options to filter the results.
// If options is nil, no filtering will occur.
func (api *FlavorsAPI) GetTasks(id string, options *TaskGetOptions) (result *TaskList, err error) {
uri := api.client.Endpoint + flavorUrl + "/" + id + "/tasks"
if options != nil {
uri += getQueryString(options)
}
res, err := api.client.restClient.GetList(api.client.Endpoint, uri, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
result = &TaskList{}
err = json.Unmarshal(res, result)
return
}

View File

@@ -0,0 +1,203 @@
// Copyright (c) 2016 VMware, Inc. All Rights Reserved.
//
// This product is licensed to you under the Apache License, Version 2.0 (the "License").
// You may not use this product except in compliance with the License.
//
// This product may include a number of subcomponents with separate copyright notices and
// license terms. Your use of these subcomponents is subject to the terms and conditions
// of the subcomponent's license, as noted in the LICENSE file.
package photon
import (
"bytes"
"encoding/json"
)
// Contains functionality for hosts API.
type HostsAPI struct {
client *Client
}
var hostUrl string = "/hosts"
// Creates a host.
func (api *HostsAPI) Create(hostSpec *HostCreateSpec, deploymentId string) (task *Task, err error) {
body, err := json.Marshal(hostSpec)
if err != nil {
return
}
res, err := api.client.restClient.Post(
api.client.Endpoint+deploymentUrl+"/"+deploymentId+hostUrl,
"application/json",
bytes.NewBuffer(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Deletes a host with specified ID.
func (api *HostsAPI) Delete(id string) (task *Task, err error) {
res, err := api.client.restClient.Delete(api.client.Endpoint+hostUrl+"/"+id, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Returns all hosts
func (api *HostsAPI) GetAll() (result *Hosts, err error) {
res, err := api.client.restClient.Get(api.client.Endpoint+hostUrl, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
res, err = getError(res)
if err != nil {
return
}
result = &Hosts{}
err = json.NewDecoder(res.Body).Decode(result)
return
}
// Gets a host with the specified ID.
func (api *HostsAPI) Get(id string) (host *Host, err error) {
res, err := api.client.restClient.Get(api.client.Endpoint+hostUrl+"/"+id, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
res, err = getError(res)
if err != nil {
return
}
var result Host
err = json.NewDecoder(res.Body).Decode(&result)
return &result, nil
}
// Sets host's availability zone.
func (api *HostsAPI) SetAvailabilityZone(id string, availabilityZone *HostSetAvailabilityZoneOperation) (task *Task, err error) {
body, err := json.Marshal(availabilityZone)
if err != nil {
return
}
res, err := api.client.restClient.Post(
api.client.Endpoint+hostUrl+"/"+id+"/set_availability_zone",
"application/json",
bytes.NewBuffer(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Gets all tasks with the specified host ID, using options to filter the results.
// If options is nil, no filtering will occur.
func (api *HostsAPI) GetTasks(id string, options *TaskGetOptions) (result *TaskList, err error) {
uri := api.client.Endpoint + hostUrl + "/" + id + "/tasks"
if options != nil {
uri += getQueryString(options)
}
res, err := api.client.restClient.GetList(api.client.Endpoint, uri, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
result = &TaskList{}
err = json.Unmarshal(res, result)
return
}
// Gets all the vms with the specified deployment ID.
func (api *HostsAPI) GetVMs(id string) (result *VMs, err error) {
res, err := api.client.restClient.Get(api.client.Endpoint+hostUrl+"/"+id+"/vms", api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
res, err = getError(res)
if err != nil {
return
}
result = &VMs{}
err = json.NewDecoder(res.Body).Decode(result)
return
}
// Suspend the host with the specified id
func (api *HostsAPI) Suspend(id string) (task *Task, err error) {
body := []byte{}
res, err := api.client.restClient.Post(
api.client.Endpoint+hostUrl+"/"+id+"/suspend",
"application/json",
bytes.NewReader(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Resume the host with the specified id
func (api *HostsAPI) Resume(id string) (task *Task, err error) {
body := []byte{}
res, err := api.client.restClient.Post(
api.client.Endpoint+hostUrl+"/"+id+"/resume",
"application/json",
bytes.NewReader(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Host with the specified id enter maintenance mode
func (api *HostsAPI) EnterMaintenanceMode(id string) (task *Task, err error) {
body := []byte{}
res, err := api.client.restClient.Post(
api.client.Endpoint+hostUrl+"/"+id+"/enter_maintenance",
"application/json",
bytes.NewReader(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Host with the specified id exit maintenance mode
func (api *HostsAPI) ExitMaintenanceMode(id string) (task *Task, err error) {
body := []byte{}
res, err := api.client.restClient.Post(
api.client.Endpoint+hostUrl+"/"+id+"/exit_maintenance",
"application/json",
bytes.NewReader(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}

View File

@@ -0,0 +1,125 @@
// Copyright (c) 2016 VMware, Inc. All Rights Reserved.
//
// This product is licensed to you under the Apache License, Version 2.0 (the "License").
// You may not use this product except in compliance with the License.
//
// This product may include a number of subcomponents with separate copyright notices and
// license terms. Your use of these subcomponents is subject to the terms and conditions
// of the subcomponent's license, as noted in the LICENSE file.
package photon
import (
"encoding/json"
"io"
)
// Contains functionality for images API.
type ImagesAPI struct {
client *Client
}
// Options for GetImage API.
type ImageGetOptions struct {
Name string `urlParam:"name"`
}
var imageUrl string = "/images"
// Uploads a new image, reading from the specified image path.
// If options is nil, default options are used.
func (api *ImagesAPI) CreateFromFile(imagePath string, options *ImageCreateOptions) (task *Task, err error) {
params := imageCreateOptionsToMap(options)
res, err := api.client.restClient.MultipartUploadFile(api.client.Endpoint+imageUrl, imagePath, params, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
result, err := getTask(getError(res))
return result, err
}
// Uploads a new image, reading from the specified io.Reader.
// Name is a descriptive name of the image, it is used in the filename field of the Content-Disposition header,
// and does not need to be unique.
// If options is nil, default options are used.
func (api *ImagesAPI) Create(reader io.Reader, name string, options *ImageCreateOptions) (task *Task, err error) {
params := imageCreateOptionsToMap(options)
res, err := api.client.restClient.MultipartUpload(api.client.Endpoint+imageUrl, reader, name, params, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
result, err := getTask(getError(res))
return result, err
}
// Gets all images on this photon instance.
func (api *ImagesAPI) GetAll(options *ImageGetOptions) (images *Images, err error) {
uri := api.client.Endpoint + imageUrl
if options != nil {
uri += getQueryString(options)
}
res, err := api.client.restClient.GetList(api.client.Endpoint, uri, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
images = &Images{}
err = json.Unmarshal(res, images)
return
}
// Gets details of image with the specified ID.
func (api *ImagesAPI) Get(imageID string) (image *Image, err error) {
res, err := api.client.restClient.Get(api.client.Endpoint+imageUrl+"/"+imageID, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
res, err = getError(res)
if err != nil {
return
}
var result Image
err = json.NewDecoder(res.Body).Decode(&result)
return &result, nil
}
// Deletes image with the specified ID.
func (api *ImagesAPI) Delete(imageID string) (task *Task, err error) {
res, err := api.client.restClient.Delete(api.client.Endpoint+imageUrl+"/"+imageID, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
result, err := getTask(getError(res))
return result, err
}
// Gets all tasks with the specified image ID, using options to filter the results.
// If options is nil, no filtering will occur.
func (api *ImagesAPI) GetTasks(id string, options *TaskGetOptions) (result *TaskList, err error) {
uri := api.client.Endpoint + imageUrl + "/" + id + "/tasks"
if options != nil {
uri += getQueryString(options)
}
res, err := api.client.restClient.GetList(api.client.Endpoint, uri, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
result = &TaskList{}
err = json.Unmarshal(res, result)
return
}
func imageCreateOptionsToMap(opts *ImageCreateOptions) map[string]string {
if opts == nil {
return nil
}
return map[string]string{
"ImageReplication": opts.ReplicationType,
}
}

View File

@@ -0,0 +1,39 @@
// Copyright (c) 2016 VMware, Inc. All Rights Reserved.
//
// This product is licensed to you under the Apache License, Version 2.0 (the "License").
// You may not use this product except in compliance with the License.
//
// This product may include a number of subcomponents with separate copyright notices and
// license terms. Your use of these subcomponents is subject to the terms and conditions
// of the subcomponent's license, as noted in the LICENSE file.
package photon
import (
"encoding/json"
)
type InfoAPI struct {
client *Client
}
var infoUrl = "/info"
// Get info
func (api *InfoAPI) Get() (info *Info, err error) {
res, err := api.client.restClient.Get(api.client.Endpoint+infoUrl, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
res, err = getError(res)
if err != nil {
return
}
info = new(Info)
err = json.NewDecoder(res.Body).Decode(info)
return
}

View File

@@ -0,0 +1,59 @@
package lightwave
import (
"encoding/base64"
"encoding/json"
"strings"
)
type JWTToken struct {
TokenId string `json:"jti"`
Algorithm string `json:"alg"`
Subject string `json:"sub"`
Audience []string `json:"aud"`
Groups []string `json:"groups"`
Issuer string `json:"iss"`
IssuedAt int64 `json:"iat"`
Expires int64 `json:"exp"`
Scope string `json:"scope"`
TokenType string `json:"token_type"`
TokenClass string `json:"token_class"`
Tenant string `json:"tenant"`
// It's possible to have more fields depending on how Lightwave defines the token.
// This covers all the fields we currently have.
}
// A JSON web token is a set of Base64 encoded strings separated by a period (.)
// When decoded, it will either be JSON text or a signature
// Here we decode the strings into a single token structure. We do not parse the signature.
func ParseTokenDetails(token string) (jwtToken *JWTToken) {
jwtToken = &JWTToken{}
chunks := strings.Split(token, ".")
for _, chunk := range chunks {
json_string, err := base64.RawURLEncoding.DecodeString(chunk)
if err == nil {
// Ignore errors. We expect that the signature is not JSON,
// so unmarshalling it will fail. That's fine. We'll extract
// all the data we can.
_ = json.Unmarshal(json_string, &jwtToken)
}
}
return jwtToken
}
// A JSON web token is a set of Base64 encoded strings separated by a period (.)
// When decoded, it will either be JSON text or a signature
// Here we parse the full JSON text. We do not parse the signature.
func ParseRawTokenDetails(token string) (jwtToken []string, err error) {
chunks := strings.Split(token, ".")
for _, chunk := range chunks {
jsonString, err := base64.RawURLEncoding.DecodeString(chunk)
if err == nil {
jwtToken = append(jwtToken, string(jsonString))
}
}
return jwtToken, err
}

View File

@@ -0,0 +1,235 @@
// Copyright (c) 2016 VMware, Inc. All Rights Reserved.
//
// This product is licensed to you under the Apache License, Version 2.0 (the "License").
// You may not use this product except in compliance with the License.
//
// This product may include a number of subcomponents with separate copyright notices and
// license terms. Your use of these subcomponents is subject to the terms and conditions
// of the subcomponent's license, as noted in the LICENSE file.
package lightwave
import (
"crypto/tls"
"crypto/x509"
"encoding/json"
"encoding/pem"
"fmt"
"io/ioutil"
"log"
"net/http"
"strings"
)
const tokenScope string = "openid offline_access"
type OIDCClient struct {
httpClient *http.Client
logger *log.Logger
Endpoint string
Options *OIDCClientOptions
}
type OIDCClientOptions struct {
// Whether or not to ignore any TLS errors when talking to photon,
// false by default.
IgnoreCertificate bool
// List of root CA's to use for server validation
// nil by default.
RootCAs *x509.CertPool
// The scope values to use when requesting tokens
TokenScope string
}
func NewOIDCClient(endpoint string, options *OIDCClientOptions, logger *log.Logger) (c *OIDCClient) {
if logger == nil {
logger = log.New(ioutil.Discard, "", log.LstdFlags)
}
options = buildOptions(options)
tr := &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: options.IgnoreCertificate,
RootCAs: options.RootCAs},
}
c = &OIDCClient{
httpClient: &http.Client{Transport: tr},
logger: logger,
Endpoint: strings.TrimRight(endpoint, "/"),
Options: options,
}
return
}
func buildOptions(options *OIDCClientOptions) (result *OIDCClientOptions) {
result = &OIDCClientOptions{
TokenScope: tokenScope,
}
if options == nil {
return
}
result.IgnoreCertificate = options.IgnoreCertificate
if options.RootCAs != nil {
result.RootCAs = options.RootCAs
}
if options.TokenScope != "" {
result.TokenScope = options.TokenScope
}
return
}
func (client *OIDCClient) buildUrl(path string) (url string) {
return fmt.Sprintf("%s%s", client.Endpoint, path)
}
// Cert download helper
const certDownloadPath string = "/afd/vecs/ssl"
type lightWaveCert struct {
Value string `json:"encoded"`
}
func (client *OIDCClient) GetRootCerts() (certList []*x509.Certificate, err error) {
// turn TLS verification off for
originalTr := client.httpClient.Transport
defer client.setTransport(originalTr)
tr := &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
}
client.setTransport(tr)
// get the certs
resp, err := client.httpClient.Get(client.buildUrl(certDownloadPath))
if err != nil {
return
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
err = fmt.Errorf("Unexpected error retrieving auth server certs: %v %s", resp.StatusCode, resp.Status)
return
}
// parse the certs
certsData := &[]lightWaveCert{}
err = json.NewDecoder(resp.Body).Decode(certsData)
if err != nil {
return
}
certList = make([]*x509.Certificate, len(*certsData))
for idx, cert := range *certsData {
block, _ := pem.Decode([]byte(cert.Value))
if block == nil {
err = fmt.Errorf("Unexpected response format: %v", certsData)
return nil, err
}
decodedCert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, err
}
certList[idx] = decodedCert
}
return
}
func (client *OIDCClient) setTransport(tr http.RoundTripper) {
client.httpClient.Transport = tr
}
// Toke request helpers
const tokenPath string = "/openidconnect/token"
const passwordGrantFormatString = "grant_type=password&username=%s&password=%s&scope=%s"
const refreshTokenGrantFormatString = "grant_type=refresh_token&refresh_token=%s"
type OIDCTokenResponse struct {
AccessToken string `json:"access_token"`
ExpiresIn int `json:"expires_in"`
RefreshToken string `json:"refresh_token,omitempty"`
IdToken string `json:"id_token"`
TokenType string `json:"token_type"`
}
func (client *OIDCClient) GetTokenByPasswordGrant(username string, password string) (tokens *OIDCTokenResponse, err error) {
body := fmt.Sprintf(passwordGrantFormatString, username, password, client.Options.TokenScope)
return client.getToken(body)
}
func (client *OIDCClient) GetTokenByRefreshTokenGrant(refreshToken string) (tokens *OIDCTokenResponse, err error) {
body := fmt.Sprintf(refreshTokenGrantFormatString, refreshToken)
return client.getToken(body)
}
func (client *OIDCClient) getToken(body string) (tokens *OIDCTokenResponse, err error) {
request, err := http.NewRequest("POST", client.buildUrl(tokenPath), strings.NewReader(body))
if err != nil {
return nil, err
}
request.Header.Add("Content-Type", "application/x-www-form-urlencoded")
resp, err := client.httpClient.Do(request)
if err != nil {
return nil, err
}
defer resp.Body.Close()
err = client.checkResponse(resp)
if err != nil {
return nil, err
}
tokens = &OIDCTokenResponse{}
err = json.NewDecoder(resp.Body).Decode(tokens)
if err != nil {
return nil, err
}
return
}
type OIDCError struct {
Code string `json:"error"`
Message string `json:"error_description"`
}
func (e OIDCError) Error() string {
return fmt.Sprintf("%v: %v", e.Code, e.Message)
}
func (client *OIDCClient) checkResponse(response *http.Response) (err error) {
if response.StatusCode/100 == 2 {
return
}
respBody, readErr := ioutil.ReadAll(response.Body)
if err != nil {
return fmt.Errorf(
"Status: %v, Body: %v [%v]", response.Status, string(respBody[:]), readErr)
}
var oidcErr OIDCError
err = json.Unmarshal(respBody, &oidcErr)
if err != nil {
return fmt.Errorf(
"Status: %v, Body: %v [%v]", response.Status, string(respBody[:]), readErr)
}
return oidcErr
}

View File

@@ -0,0 +1,101 @@
// Copyright (c) 2016 VMware, Inc. All Rights Reserved.
//
// This product is licensed to you under the Apache License, Version 2.0 (the "License").
// You may not use this product except in compliance with the License.
//
// This product may include a number of subcomponents with separate copyright notices and
// license terms. Your use of these subcomponents is subject to the terms and conditions
// of the subcomponent's license, as noted in the LICENSE file.
package photon
import (
"bytes"
"encoding/json"
)
// Contains functionality for networks API.
type SubnetsAPI struct {
client *Client
}
// Options used for GetAll API
type SubnetGetOptions struct {
Name string `urlParam:"name"`
}
var subnetUrl string = "/subnets"
// Creates a network.
func (api *SubnetsAPI) Create(networkSpec *SubnetCreateSpec) (task *Task, err error) {
body, err := json.Marshal(networkSpec)
if err != nil {
return
}
res, err := api.client.restClient.Post(
api.client.Endpoint+subnetUrl,
"application/json",
bytes.NewBuffer(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Deletes a network with specified ID.
func (api *SubnetsAPI) Delete(id string) (task *Task, err error) {
res, err := api.client.restClient.Delete(api.client.Endpoint+subnetUrl+"/"+id, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Gets a network with the specified ID.
func (api *SubnetsAPI) Get(id string) (network *Subnet, err error) {
res, err := api.client.restClient.Get(api.client.Endpoint+subnetUrl+"/"+id, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
res, err = getError(res)
if err != nil {
return
}
var result Subnet
err = json.NewDecoder(res.Body).Decode(&result)
return &result, nil
}
// Returns all networks
func (api *SubnetsAPI) GetAll(options *SubnetGetOptions) (result *Subnets, err error) {
uri := api.client.Endpoint + subnetUrl
if options != nil {
uri += getQueryString(options)
}
res, err := api.client.restClient.GetList(api.client.Endpoint, uri, api.client.options.TokenOptions.AccessToken)
result = &Subnets{}
err = json.Unmarshal(res, result)
return
}
// Sets default network.
func (api *SubnetsAPI) SetDefault(id string) (task *Task, err error) {
res, err := api.client.restClient.Post(
api.client.Endpoint+subnetUrl+"/"+id+"/set_default",
"application/json",
bytes.NewBuffer([]byte("")),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}

View File

@@ -0,0 +1,189 @@
// Copyright (c) 2016 VMware, Inc. All Rights Reserved.
//
// This product is licensed to you under the Apache License, Version 2.0 (the "License").
// You may not use this product except in compliance with the License.
//
// This product may include a number of subcomponents with separate copyright notices and
// license terms. Your use of these subcomponents is subject to the terms and conditions
// of the subcomponent's license, as noted in the LICENSE file.
package photon
import (
"bytes"
"encoding/json"
)
// Contains functionality for projects API.
type ProjectsAPI struct {
client *Client
}
// Options for GetDisks API.
type DiskGetOptions struct {
Name string `urlParam:"name"`
}
// Options for GetDisks API.
type VmGetOptions struct {
Name string `urlParam:"name"`
}
var projectUrl string = "/projects/"
// Deletes the project with specified ID. Any VMs, disks, etc., owned by the project must be deleted first.
func (api *ProjectsAPI) Delete(projectID string) (task *Task, err error) {
res, err := api.client.restClient.Delete(api.client.Endpoint+projectUrl+projectID, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Creates a disk on the specified project.
func (api *ProjectsAPI) CreateDisk(projectID string, spec *DiskCreateSpec) (task *Task, err error) {
body, err := json.Marshal(spec)
if err != nil {
return
}
res, err := api.client.restClient.Post(
api.client.Endpoint+projectUrl+projectID+"/disks",
"application/json",
bytes.NewReader(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Gets disks for project with the specified ID, using options to filter the results.
// If options is nil, no filtering will occur.
func (api *ProjectsAPI) GetDisks(projectID string, options *DiskGetOptions) (result *DiskList, err error) {
uri := api.client.Endpoint + projectUrl + projectID + "/disks"
if options != nil {
uri += getQueryString(options)
}
res, err := api.client.restClient.GetList(api.client.Endpoint, uri, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
result = &DiskList{}
err = json.Unmarshal(res, result)
return
}
// Creates a VM on the specified project.
func (api *ProjectsAPI) CreateVM(projectID string, spec *VmCreateSpec) (task *Task, err error) {
body, err := json.Marshal(spec)
if err != nil {
return
}
res, err := api.client.restClient.Post(
api.client.Endpoint+projectUrl+projectID+"/vms",
"application/json",
bytes.NewReader(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Gets all tasks with the specified project ID, using options to filter the results.
// If options is nil, no filtering will occur.
func (api *ProjectsAPI) GetTasks(id string, options *TaskGetOptions) (result *TaskList, err error) {
uri := api.client.Endpoint + projectUrl + id + "/tasks"
if options != nil {
uri += getQueryString(options)
}
res, err := api.client.restClient.GetList(api.client.Endpoint, uri, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
result = &TaskList{}
err = json.Unmarshal(res, result)
return
}
// Gets vms for project with the specified ID, using options to filter the results.
// If options is nil, no filtering will occur.
func (api *ProjectsAPI) GetVMs(projectID string, options *VmGetOptions) (result *VMs, err error) {
uri := api.client.Endpoint + projectUrl + projectID + "/vms"
if options != nil {
uri += getQueryString(options)
}
res, err := api.client.restClient.GetList(api.client.Endpoint, uri, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
result = &VMs{}
err = json.Unmarshal(res, result)
return
}
// Creates a cluster on the specified project.
func (api *ProjectsAPI) CreateCluster(projectID string, spec *ClusterCreateSpec) (task *Task, err error) {
body, err := json.Marshal(spec)
if err != nil {
return
}
res, err := api.client.restClient.Post(
api.client.Endpoint+projectUrl+projectID+"/clusters",
"application/json",
bytes.NewReader(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Gets clusters for project with the specified ID
func (api *ProjectsAPI) GetClusters(projectID string) (result *Clusters, err error) {
uri := api.client.Endpoint + projectUrl + projectID + "/clusters"
res, err := api.client.restClient.GetList(api.client.Endpoint, uri, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
result = &Clusters{}
err = json.Unmarshal(res, result)
return
}
// Gets the project with a specified ID.
func (api *ProjectsAPI) Get(id string) (project *ProjectCompact, err error) {
res, err := api.client.restClient.Get(api.getEntityUrl(id), api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
res, err = getError(res)
if err != nil {
return
}
project = &ProjectCompact{}
err = json.NewDecoder(res.Body).Decode(project)
return
}
// Set security groups for this project, overwriting any existing ones.
func (api *ProjectsAPI) SetSecurityGroups(projectID string, securityGroups *SecurityGroupsSpec) (*Task, error) {
return setSecurityGroups(api.client, api.getEntityUrl(projectID), securityGroups)
}
func (api *ProjectsAPI) getEntityUrl(id string) string {
return api.client.Endpoint + projectUrl + id
}

View File

@@ -0,0 +1,55 @@
// Copyright (c) 2016 VMware, Inc. All Rights Reserved.
//
// This product is licensed to you under the Apache License, Version 2.0 (the "License").
// You may not use this product except in compliance with the License.
//
// This product may include a number of subcomponents with separate copyright notices and
// license terms. Your use of these subcomponents is subject to the terms and conditions
// of the subcomponent's license, as noted in the LICENSE file.
package photon
import (
"encoding/json"
)
// Contains functionality for hosts API.
type ResourceTicketsAPI struct {
client *Client
}
var resourceTicketUrl string = "/resource-tickets/"
// Gets all tasks with the specified resource ticket ID, using options to filter the results.
// If options is nil, no filtering will occur.
func (api *ResourceTicketsAPI) GetTasks(id string, options *TaskGetOptions) (result *TaskList, err error) {
uri := api.client.Endpoint + resourceTicketUrl + id + "/tasks"
if options != nil {
uri += getQueryString(options)
}
res, err := api.client.restClient.GetList(api.client.Endpoint, uri, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
result = &TaskList{}
err = json.Unmarshal(res, result)
return
}
// Gets the resource ticket with a specified ID.
func (api *ResourceTicketsAPI) Get(id string) (resourceTicket *ResourceTicket, err error) {
res, err := api.client.restClient.Get(api.client.Endpoint+resourceTicketUrl+"/"+id,
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
res, err = getError(res)
if err != nil {
return
}
resourceTicket = &ResourceTicket{}
err = json.NewDecoder(res.Body).Decode(resourceTicket)
return
}

View File

@@ -0,0 +1,219 @@
// Copyright (c) 2016 VMware, Inc. All Rights Reserved.
//
// This product is licensed to you under the Apache License, Version 2.0 (the "License").
// You may not use this product except in compliance with the License.
//
// This product may include a number of subcomponents with separate copyright notices and
// license terms. Your use of these subcomponents is subject to the terms and conditions
// of the subcomponent's license, as noted in the LICENSE file.
package photon
import (
"crypto/rand"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"path/filepath"
"strings"
)
type restClient struct {
httpClient *http.Client
logger *log.Logger
}
type request struct {
Method string
URL string
ContentType string
Body io.Reader
Token string
}
type page struct {
Items []interface{} `json:"items"`
NextPageLink string `json:"nextPageLink"`
PreviousPageLink string `json:"previousPageLink"`
}
type documentList struct {
Items []interface{}
}
const appJson string = "application/json"
// From https://golang.org/src/mime/multipart/writer.go
var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")
func (client *restClient) AppendSlice(origSlice []interface{}, dataToAppend []interface{}) []interface{} {
origLen := len(origSlice)
newLen := origLen + len(dataToAppend)
if newLen > cap(origSlice) {
newSlice := make([]interface{}, (newLen+1)*2)
copy(newSlice, origSlice)
origSlice = newSlice
}
origSlice = origSlice[0:newLen]
copy(origSlice[origLen:newLen], dataToAppend)
return origSlice
}
func (client *restClient) Get(url string, token string) (res *http.Response, err error) {
req := request{"GET", url, "", nil, token}
res, err = client.Do(&req)
return
}
func (client *restClient) GetList(endpoint string, url string, token string) (result []byte, err error) {
req := request{"GET", url, "", nil, token}
res, err := client.Do(&req)
if err != nil {
return
}
res, err = getError(res)
if err != nil {
return
}
decoder := json.NewDecoder(res.Body)
decoder.UseNumber()
page := &page{}
err = decoder.Decode(page)
if err != nil {
return
}
documentList := &documentList{}
documentList.Items = client.AppendSlice(documentList.Items, page.Items)
for page.NextPageLink != "" {
req = request{"GET", endpoint + page.NextPageLink, "", nil, token}
res, err = client.Do(&req)
if err != nil {
return
}
res, err = getError(res)
if err != nil {
return
}
decoder = json.NewDecoder(res.Body)
decoder.UseNumber()
page.NextPageLink = ""
page.PreviousPageLink = ""
err = decoder.Decode(page)
if err != nil {
return
}
documentList.Items = client.AppendSlice(documentList.Items, page.Items)
}
result, err = json.Marshal(documentList)
return
}
func (client *restClient) Post(url string, contentType string, body io.Reader, token string) (res *http.Response, err error) {
if contentType == "" {
contentType = appJson
}
req := request{"POST", url, contentType, body, token}
res, err = client.Do(&req)
return
}
func (client *restClient) Delete(url string, token string) (res *http.Response, err error) {
req := request{"DELETE", url, "", nil, token}
res, err = client.Do(&req)
return
}
func (client *restClient) Do(req *request) (res *http.Response, err error) {
r, err := http.NewRequest(req.Method, req.URL, req.Body)
if err != nil {
client.logger.Printf("An error occured creating request %s on %s. Error: %s", req.Method, req.URL, err)
return
}
if req.ContentType != "" {
r.Header.Add("Content-Type", req.ContentType)
}
if req.Token != "" {
r.Header.Add("Authorization", "Bearer "+req.Token)
}
res, err = client.httpClient.Do(r)
if err != nil {
client.logger.Printf("An error occured when calling %s on %s. Error: %s", req.Method, req.URL, err)
return
}
client.logger.Printf("[%s] %s - %s %s", res.Header.Get("request-id"), res.Status, req.Method, req.URL)
return
}
func (client *restClient) MultipartUploadFile(url, filePath string, params map[string]string, token string) (res *http.Response, err error) {
file, err := os.Open(filePath)
if err != nil {
return
}
defer file.Close()
return client.MultipartUpload(url, file, filepath.Base(filePath), params, token)
}
func (client *restClient) MultipartUpload(url string, reader io.Reader, filename string, params map[string]string, token string) (res *http.Response, err error) {
// The mime/multipart package does not support streaming multipart data from disk,
// at least not without complicated, problematic goroutines that simultaneously read/write into a buffer.
// A much easier approach is to just construct the multipart request by hand, using io.MultiPart to
// concatenate the parts of the request together into a single io.Reader.
boundary := client.randomBoundary()
parts := []io.Reader{}
// Create a part for each key, val pair in params
for k, v := range params {
parts = append(parts, client.createFieldPart(k, v, boundary))
}
start := fmt.Sprintf("\r\n--%s\r\n", boundary)
start += fmt.Sprintf("Content-Disposition: form-data; name=\"file\"; filename=\"%s\"\r\n", quoteEscaper.Replace(filename))
start += fmt.Sprintf("Content-Type: application/octet-stream\r\n\r\n")
end := fmt.Sprintf("\r\n--%s--", boundary)
// The request will consist of a reader to begin the request, a reader which points
// to the file data on disk, and a reader containing the closing boundary of the request.
parts = append(parts, strings.NewReader(start), reader, strings.NewReader(end))
contentType := fmt.Sprintf("multipart/form-data; boundary=%s", boundary)
res, err = client.Do(&request{"POST", url, contentType, io.MultiReader(parts...), token})
return
}
// From https://golang.org/src/mime/multipart/writer.go
func (client *restClient) randomBoundary() string {
var buf [30]byte
_, err := io.ReadFull(rand.Reader, buf[:])
if err != nil {
panic(err)
}
return fmt.Sprintf("%x", buf[:])
}
// Creates a reader that encapsulates a single multipart form part
func (client *restClient) createFieldPart(fieldname, value, boundary string) io.Reader {
str := fmt.Sprintf("\r\n--%s\r\n", boundary)
str += fmt.Sprintf("Content-Disposition: form-data; name=\"%s\"\r\n\r\n", quoteEscaper.Replace(fieldname))
str += value
return strings.NewReader(str)
}

View File

@@ -0,0 +1,37 @@
// Copyright (c) 2016 VMware, Inc. All Rights Reserved.
//
// This product is licensed to you under the Apache License, Version 2.0 (the "License").
// You may not use this product except in compliance with the License.
//
// This product may include a number of subcomponents with separate copyright notices and
// license terms. Your use of these subcomponents is subject to the terms and conditions
// of the subcomponent's license, as noted in the LICENSE file.
package photon
import (
"encoding/json"
)
// Contains functionality for status API.
type StatusAPI struct {
client *Client
}
var statusUrl string = "/status"
// Returns the status of an photon endpoint.
func (api *StatusAPI) Get() (status *Status, err error) {
res, err := api.client.restClient.Get(api.client.Endpoint+statusUrl, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
res, err = getError(res)
if err != nil {
return
}
status = &Status{}
err = json.NewDecoder(res.Body).Decode(status)
return
}

View File

@@ -0,0 +1,108 @@
// Copyright (c) 2016 VMware, Inc. All Rights Reserved.
//
// This product is licensed to you under the Apache License, Version 2.0 (the "License").
// You may not use this product except in compliance with the License.
//
// This product may include a number of subcomponents with separate copyright notices and
// license terms. Your use of these subcomponents is subject to the terms and conditions
// of the subcomponent's license, as noted in the LICENSE file.
package photon
import (
"encoding/json"
"time"
)
// Contains functionality for tasks API.
type TasksAPI struct {
client *Client
}
var taskUrl string = "/tasks"
// Gets a task by ID.
func (api *TasksAPI) Get(id string) (task *Task, err error) {
res, err := api.client.restClient.Get(api.client.Endpoint+taskUrl+"/"+id, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
result, err := getTask(getError(res))
return result, err
}
// Gets all tasks, using options to filter the results.
// If options is nil, no filtering will occur.
func (api *TasksAPI) GetAll(options *TaskGetOptions) (result *TaskList, err error) {
uri := api.client.Endpoint + taskUrl
if options != nil {
uri += getQueryString(options)
}
res, err := api.client.restClient.GetList(api.client.Endpoint, uri, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
result = &TaskList{}
err = json.Unmarshal(res, result)
return
}
// Waits for a task to complete by polling the tasks API until a task returns with
// the state COMPLETED or ERROR. Will wait no longer than the duration specified by timeout.
func (api *TasksAPI) WaitTimeout(id string, timeout time.Duration) (task *Task, err error) {
start := time.Now()
numErrors := 0
maxErrors := api.client.options.TaskRetryCount
for time.Since(start) < timeout {
task, err = api.Get(id)
if err != nil {
switch err.(type) {
// If an ApiError comes back, something is wrong, return the error to the caller
case ApiError:
return
// For other errors, retry before giving up
default:
numErrors++
if numErrors > maxErrors {
return
}
}
} else {
// Reset the error count any time a successful call is made
numErrors = 0
if task.State == "COMPLETED" {
return
}
if task.State == "ERROR" {
err = TaskError{task.ID, getFailedStep(task)}
return
}
}
time.Sleep(api.client.options.TaskPollDelay)
}
err = TaskTimeoutError{id}
return
}
// Waits for a task to complete by polling the tasks API until a task returns with
// the state COMPLETED or ERROR.
func (api *TasksAPI) Wait(id string) (task *Task, err error) {
return api.WaitTimeout(id, api.client.options.TaskPollTimeout)
}
// Gets the failed step in the task to get error details for failed task.
func getFailedStep(task *Task) (step Step) {
var errorStep Step
for _, s := range task.Steps {
if s.State == "ERROR" {
errorStep = s
break
}
}
return errorStep
}

View File

@@ -0,0 +1,189 @@
// Copyright (c) 2016 VMware, Inc. All Rights Reserved.
//
// This product is licensed to you under the Apache License, Version 2.0 (the "License").
// You may not use this product except in compliance with the License.
//
// This product may include a number of subcomponents with separate copyright notices and
// license terms. Your use of these subcomponents is subject to the terms and conditions
// of the subcomponent's license, as noted in the LICENSE file.
package photon
import (
"bytes"
"encoding/json"
)
// Contains functionality for tenants API.
type TenantsAPI struct {
client *Client
}
// Options for GetResourceTickets API.
type ResourceTicketGetOptions struct {
Name string `urlParam:"name"`
}
// Options for GetProjects API.
type ProjectGetOptions struct {
Name string `urlParam:"name"`
}
var tenantUrl string = "/tenants"
// Returns all tenants on an photon instance.
func (api *TenantsAPI) GetAll() (result *Tenants, err error) {
uri := api.client.Endpoint + tenantUrl
res, err := api.client.restClient.GetList(api.client.Endpoint, uri, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
result = &Tenants{}
err = json.Unmarshal(res, result)
return
}
// Creates a tenant.
func (api *TenantsAPI) Create(tenantSpec *TenantCreateSpec) (task *Task, err error) {
body, err := json.Marshal(tenantSpec)
if err != nil {
return
}
res, err := api.client.restClient.Post(
api.client.Endpoint+tenantUrl,
"application/json",
bytes.NewReader(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Deletes the tenant with specified ID. Any projects, VMs, disks, etc., owned by the tenant must be deleted first.
func (api *TenantsAPI) Delete(id string) (task *Task, err error) {
res, err := api.client.restClient.Delete(api.client.Endpoint+tenantUrl+"/"+id, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Creates a resource ticket on the specified tenant.
func (api *TenantsAPI) CreateResourceTicket(tenantId string, spec *ResourceTicketCreateSpec) (task *Task, err error) {
body, err := json.Marshal(spec)
if err != nil {
return
}
res, err := api.client.restClient.Post(
api.client.Endpoint+tenantUrl+"/"+tenantId+"/resource-tickets",
"application/json",
bytes.NewReader(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Gets resource tickets for tenant with the specified ID, using options to filter the results.
// If options is nil, no filtering will occur.
func (api *TenantsAPI) GetResourceTickets(tenantId string, options *ResourceTicketGetOptions) (tickets *ResourceList, err error) {
uri := api.client.Endpoint + tenantUrl + "/" + tenantId + "/resource-tickets"
if options != nil {
uri += getQueryString(options)
}
res, err := api.client.restClient.GetList(api.client.Endpoint, uri, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
tickets = &ResourceList{}
err = json.Unmarshal(res, tickets)
return
}
// Creates a project on the specified tenant.
func (api *TenantsAPI) CreateProject(tenantId string, spec *ProjectCreateSpec) (task *Task, err error) {
body, err := json.Marshal(spec)
if err != nil {
return
}
res, err := api.client.restClient.Post(
api.client.Endpoint+tenantUrl+"/"+tenantId+"/projects",
"application/json",
bytes.NewReader(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Gets the projects for tenant with the specified ID, using options to filter the results.
// If options is nil, no filtering will occur.
func (api *TenantsAPI) GetProjects(tenantId string, options *ProjectGetOptions) (result *ProjectList, err error) {
uri := api.client.Endpoint + tenantUrl + "/" + tenantId + "/projects"
if options != nil {
uri += getQueryString(options)
}
res, err := api.client.restClient.GetList(api.client.Endpoint, uri, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
result = &(ProjectList{})
err = json.Unmarshal(res, result)
return
}
// Gets all tasks with the specified tenant ID, using options to filter the results.
// If options is nil, no filtering will occur.
func (api *TenantsAPI) GetTasks(id string, options *TaskGetOptions) (result *TaskList, err error) {
uri := api.client.Endpoint + tenantUrl + "/" + id + "/tasks"
if options != nil {
uri += getQueryString(options)
}
res, err := api.client.restClient.GetList(api.client.Endpoint, uri, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
result = &TaskList{}
err = json.Unmarshal(res, result)
return
}
// Gets a tenant with the specified ID.
func (api *TenantsAPI) Get(id string) (tenant *Tenant, err error) {
res, err := api.client.restClient.Get(api.getEntityUrl(id), api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
res, err = getError(res)
if err != nil {
return
}
tenant = &Tenant{}
err = json.NewDecoder(res.Body).Decode(tenant)
return
}
// Set security groups for this tenant, overwriting any existing ones.
func (api *TenantsAPI) SetSecurityGroups(id string, securityGroups *SecurityGroupsSpec) (*Task, error) {
return setSecurityGroups(api.client, api.getEntityUrl(id), securityGroups)
}
func (api *TenantsAPI) getEntityUrl(id string) (url string) {
return api.client.Endpoint + tenantUrl + "/" + id
}

View File

@@ -0,0 +1,110 @@
// Copyright (c) 2016 VMware, Inc. All Rights Reserved.
//
// This product is licensed to you under the Apache License, Version 2.0 (the "License").
// You may not use this product except in compliance with the License.
//
// This product may include a number of subcomponents with separate copyright notices and
// license terms. Your use of these subcomponents is subject to the terms and conditions
// of the subcomponent's license, as noted in the LICENSE file.
package photon
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"reflect"
)
// Reads an error out of the HTTP response, or does nothing if
// no error occured.
func getError(res *http.Response) (*http.Response, error) {
// Do nothing if the response is a successful 2xx
if res.StatusCode/100 == 2 {
return res, nil
}
var apiError ApiError
// ReadAll is usually a bad practice, but here we need to read the response all
// at once because we may attempt to use the data twice. It's preferable to use
// methods that take io.Reader, e.g. json.NewDecoder
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, err
}
err = json.Unmarshal(body, &apiError)
if err != nil {
// If deserializing into ApiError fails, return a generic HttpError instead
return nil, HttpError{res.StatusCode, string(body[:])}
}
apiError.HttpStatusCode = res.StatusCode
return nil, apiError
}
// Reads a task object out of the HTTP response. Takes an error argument
// so that GetTask can easily wrap GetError. This function will do nothing
// if e is not nil.
// e.g. res, err := getTask(getError(someApi.Get()))
func getTask(res *http.Response, e error) (*Task, error) {
if e != nil {
return nil, e
}
var task Task
err := json.NewDecoder(res.Body).Decode(&task)
if err != nil {
return nil, err
}
if task.State == "ERROR" {
// Critical: return task as well, so that it can be examined
// for error details.
return &task, TaskError{task.ID, getFailedStep(&task)}
}
return &task, nil
}
// Converts an options struct into a query string.
// E.g. type Foo struct {A int; B int} might return "?a=5&b=10".
// Will return an empty string if no options are set.
func getQueryString(options interface{}) string {
buffer := bytes.Buffer{}
buffer.WriteString("?")
strct := reflect.ValueOf(options).Elem()
typ := strct.Type()
for i := 0; i < strct.NumField(); i++ {
field := strct.Field(i)
value := fmt.Sprint(field.Interface())
if value != "" {
buffer.WriteString(typ.Field(i).Tag.Get("urlParam") + "=" + url.QueryEscape(value))
if i < strct.NumField()-1 {
buffer.WriteString("&")
}
}
}
uri := buffer.String()
if uri == "?" {
return ""
}
return uri
}
// Sets security groups for a given entity (deployment/tenant/project)
func setSecurityGroups(client *Client, entityUrl string, securityGroups *SecurityGroupsSpec) (task *Task, err error) {
body, err := json.Marshal(securityGroups)
if err != nil {
return
}
url := entityUrl + "/set_security_groups"
res, err := client.restClient.Post(
url,
"application/json",
bytes.NewReader(body),
client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}

View File

@@ -0,0 +1,122 @@
// Copyright (c) 2016 VMware, Inc. All Rights Reserved.
//
// This product is licensed to you under the Apache License, Version 2.0 (the "License").
// You may not use this product except in compliance with the License.
//
// This product may include a number of subcomponents with separate copyright notices and
// license terms. Your use of these subcomponents is subject to the terms and conditions
// of the subcomponent's license, as noted in the LICENSE file.
package photon
import (
"bytes"
"encoding/json"
)
// Contains pointer to api client.
type VirtualSubnetsAPI struct {
client *Client
}
// Options used for GetAll API
type VirtualSubnetGetOptions struct {
Name string `urlParam:"name"`
}
var subnetsUrl = "/subnets"
var projectsUrl = "/projects"
// Create a virtual network
func (api *VirtualSubnetsAPI) Create(projectId string,
virtualNetworkSpec *VirtualSubnetCreateSpec) (task *Task, err error) {
body, err := json.Marshal(virtualNetworkSpec)
if err != nil {
return
}
res, err := api.client.restClient.Post(
api.client.Endpoint+projectsUrl+"/"+projectId+"/subnets",
"application/json",
bytes.NewBuffer(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Delete a virtual network with the specified ID.
func (api *VirtualSubnetsAPI) Delete(id string) (task *Task, err error) {
res, err := api.client.restClient.Delete(api.client.Endpoint+subnetsUrl+"/"+id,
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Get the virtual subnet with the specified id
func (api *VirtualSubnetsAPI) Get(id string) (subnet *VirtualSubnet, err error) {
res, err := api.client.restClient.Get(api.client.Endpoint+subnetsUrl+"/"+id,
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
res, err = getError(res)
if err != nil {
return
}
subnet = new(VirtualSubnet)
err = json.NewDecoder(res.Body).Decode(subnet)
return
}
// Return all virtual networks
func (api *VirtualSubnetsAPI) GetAll(projectId string,
options *VirtualSubnetGetOptions) (subnets *VirtualSubnets, err error) {
uri := api.client.Endpoint + projectsUrl + "/" + projectId + "/subnets"
if options != nil {
uri += getQueryString(options)
}
res, err := api.client.restClient.GetList(api.client.Endpoint, uri, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
subnets = &VirtualSubnets{}
err = json.Unmarshal(res, subnets)
return
}
func (api *VirtualSubnetsAPI) SetDefault(id string) (task *Task, err error) {
res, err := api.client.restClient.Post(
api.client.Endpoint+subnetsUrl+"/"+id+"/set_default",
"application/json",
bytes.NewBuffer([]byte("")),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}

View File

@@ -0,0 +1,327 @@
// Copyright (c) 2016 VMware, Inc. All Rights Reserved.
//
// This product is licensed to you under the Apache License, Version 2.0 (the "License").
// You may not use this product except in compliance with the License.
//
// This product may include a number of subcomponents with separate copyright notices and
// license terms. Your use of these subcomponents is subject to the terms and conditions
// of the subcomponent's license, as noted in the LICENSE file.
package photon
import (
"bytes"
"encoding/json"
"io"
)
// Contains functionality for VMs API.
type VmAPI struct {
client *Client
}
var vmUrl string = "/vms/"
func (api *VmAPI) Get(id string) (vm *VM, err error) {
res, err := api.client.restClient.Get(api.client.Endpoint+vmUrl+id, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
res, err = getError(res)
if err != nil {
return
}
vm = &VM{}
err = json.NewDecoder(res.Body).Decode(vm)
return
}
func (api *VmAPI) Delete(id string) (task *Task, err error) {
res, err := api.client.restClient.Delete(api.client.Endpoint+vmUrl+id, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
func (api *VmAPI) AttachDisk(id string, op *VmDiskOperation) (task *Task, err error) {
body, err := json.Marshal(op)
if err != nil {
return
}
res, err := api.client.restClient.Post(
api.client.Endpoint+vmUrl+id+"/attach_disk",
"application/json",
bytes.NewReader(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
func (api *VmAPI) DetachDisk(id string, op *VmDiskOperation) (task *Task, err error) {
body, err := json.Marshal(op)
if err != nil {
return
}
res, err := api.client.restClient.Post(
api.client.Endpoint+vmUrl+id+"/detach_disk",
"application/json",
bytes.NewReader(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
func (api *VmAPI) AttachISO(id string, reader io.Reader, name string) (task *Task, err error) {
res, err := api.client.restClient.MultipartUpload(
api.client.Endpoint+vmUrl+id+"/attach_iso", reader, name, nil, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
result, err := getTask(getError(res))
return result, err
}
func (api *VmAPI) DetachISO(id string) (task *Task, err error) {
body := []byte{}
if err != nil {
return
}
res, err := api.client.restClient.Post(
api.client.Endpoint+vmUrl+id+"/detach_iso",
"application/json",
bytes.NewReader(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
func (api *VmAPI) Start(id string) (task *Task, err error) {
body := []byte{}
if err != nil {
return
}
res, err := api.client.restClient.Post(
api.client.Endpoint+vmUrl+id+"/start",
"application/json",
bytes.NewReader(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
func (api *VmAPI) Stop(id string) (task *Task, err error) {
body := []byte{}
if err != nil {
return
}
res, err := api.client.restClient.Post(
api.client.Endpoint+vmUrl+id+"/stop",
"application/json",
bytes.NewReader(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
func (api *VmAPI) Restart(id string) (task *Task, err error) {
body := []byte{}
if err != nil {
return
}
res, err := api.client.restClient.Post(
api.client.Endpoint+vmUrl+id+"/restart",
"application/json",
bytes.NewReader(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
func (api *VmAPI) Resume(id string) (task *Task, err error) {
body := []byte{}
if err != nil {
return
}
res, err := api.client.restClient.Post(
api.client.Endpoint+vmUrl+id+"/resume",
"application/json",
bytes.NewReader(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
func (api *VmAPI) Suspend(id string) (task *Task, err error) {
body := []byte{}
if err != nil {
return
}
res, err := api.client.restClient.Post(
api.client.Endpoint+vmUrl+id+"/suspend",
"application/json",
bytes.NewReader(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
func (api *VmAPI) SetMetadata(id string, metadata *VmMetadata) (task *Task, err error) {
body, err := json.Marshal(metadata)
if err != nil {
return
}
res, err := api.client.restClient.Post(
api.client.Endpoint+vmUrl+id+"/set_metadata",
"application/json",
bytes.NewReader(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
// Gets all tasks with the specified vm ID, using options to filter the results.
// If options is nil, no filtering will occur.
func (api *VmAPI) GetTasks(id string, options *TaskGetOptions) (result *TaskList, err error) {
uri := api.client.Endpoint + vmUrl + id + "/tasks"
if options != nil {
uri += getQueryString(options)
}
res, err := api.client.restClient.GetList(api.client.Endpoint, uri, api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
result = &TaskList{}
err = json.Unmarshal(res, result)
return
}
func (api *VmAPI) GetNetworks(id string) (task *Task, err error) {
res, err := api.client.restClient.Get(api.client.Endpoint+vmUrl+id+"/subnets", api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
func (api *VmAPI) AcquireFloatingIp(id string, spec *VmFloatingIpSpec) (task *Task, err error) {
body, err := json.Marshal(spec)
if err != nil {
return
}
res, err := api.client.restClient.Post(
api.client.Endpoint+vmUrl+id+"/acquire_floating_ip",
"application/json",
bytes.NewReader(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
func (api *VmAPI) ReleaseFloatingIp(id string) (task *Task, err error) {
res, err := api.client.restClient.Delete(
api.client.Endpoint+vmUrl+id+"/release_floating_ip",
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
func (api *VmAPI) GetMKSTicket(id string) (task *Task, err error) {
res, err := api.client.restClient.Get(api.client.Endpoint+vmUrl+id+"/mks_ticket", api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
func (api *VmAPI) SetTag(id string, tag *VmTag) (task *Task, err error) {
body, err := json.Marshal(tag)
if err != nil {
return
}
res, err := api.client.restClient.Post(
api.client.Endpoint+vmUrl+id+"/tags",
"application/json",
bytes.NewReader(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}
func (api *VmAPI) CreateImage(id string, options *ImageCreateSpec) (task *Task, err error) {
body, err := json.Marshal(options)
if err != nil {
return
}
res, err := api.client.restClient.Post(
api.client.Endpoint+vmUrl+id+"/create_image",
"application/json",
bytes.NewReader(body),
api.client.options.TokenOptions.AccessToken)
if err != nil {
return
}
defer res.Body.Close()
task, err = getTask(getError(res))
return
}