blob: 0968127b6f557aaaf2438788cfb4c4d77c0de6be [file] [log] [blame]
// 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
Networks *NetworksAPI
Services *ServicesAPI
Auth *AuthAPI
AvailabilityZones *AvailabilityZonesAPI
Info *InfoAPI
Routers *RoutersAPI
Subnets *SubnetsAPI
System *SystemAPI
Zones *ZonesAPI
Infra *InfraAPI
InfraHosts *InfraHostsAPI
}
// 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"`
}
type TokenCallback func(string)
// 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
// A function to be called if the access token was refreshed
// The client can save the new access token for future API
// calls so that it doesn't need to be refreshed again.
UpdateAccessTokenCallback TokenCallback
}
// 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
defaultOptions.UpdateAccessTokenCallback = options.UpdateAccessTokenCallback
}
if logger == nil {
logger = createPassThroughLogger()
}
tr := &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: defaultOptions.IgnoreCertificate,
RootCAs: defaultOptions.RootCAs},
}
endpoint = strings.TrimRight(endpoint, "/")
tokenCallback := func(newToken string) {
c.options.TokenOptions.AccessToken = newToken
if c.options.UpdateAccessTokenCallback != nil {
c.options.UpdateAccessTokenCallback(newToken)
}
}
restClient := &restClient{
httpClient: &http.Client{Transport: tr},
logger: logger,
UpdateAccessTokenCallback: tokenCallback,
}
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.Networks = &NetworksAPI{c}
c.Services = &ServicesAPI{c}
c.Auth = &AuthAPI{c}
c.AvailabilityZones = &AvailabilityZonesAPI{c}
c.Info = &InfoAPI{c}
c.Routers = &RoutersAPI{c}
c.Subnets = &SubnetsAPI{c}
c.System = &SystemAPI{c}
c.Zones = &ZonesAPI{c}
c.Infra = &InfraAPI{c}
c.InfraHosts = &InfraHostsAPI{c}
// Tell the restClient about the Auth API so it can request new
// acces tokens when they expire
restClient.Auth = c.Auth
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)
}