Update oracle SDK

This commit is contained in:
Matthew Hooker 2018-10-24 14:04:51 -07:00
parent bbb0d450f1
commit ab723eaf78
No known key found for this signature in database
GPG Key ID: 7B5F933D9CE8C6A1
40 changed files with 1423 additions and 601 deletions

View File

@ -5,16 +5,20 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"math/rand"
"mime/multipart"
"net/http" "net/http"
"net/url" "net/url"
"runtime" "runtime"
"strings"
"time" "time"
"github.com/hashicorp/go-oracle-terraform/opc" "github.com/hashicorp/go-oracle-terraform/opc"
) )
const DEFAULT_MAX_RETRIES = 1 const defaultMaxRetries = 1
const USER_AGENT_HEADER = "User-Agent" const userAgentHeader = "User-Agent"
var ( var (
// defaultUserAgent builds a string containing the Go version, system archityecture and OS, // defaultUserAgent builds a string containing the Go version, system archityecture and OS,
@ -40,6 +44,7 @@ type Client struct {
loglevel opc.LogLevelType loglevel opc.LogLevelType
} }
// NewClient returns a new client
func NewClient(c *opc.Config) (*Client, error) { func NewClient(c *opc.Config) (*Client, error) {
// First create a client // First create a client
client := &Client{ client := &Client{
@ -71,7 +76,7 @@ func NewClient(c *opc.Config) (*Client, error) {
// Default max retries if unset // Default max retries if unset
if c.MaxRetries == nil { if c.MaxRetries == nil {
client.MaxRetries = opc.Int(DEFAULT_MAX_RETRIES) client.MaxRetries = opc.Int(defaultMaxRetries)
} }
// Protect against any nil http client // Protect against any nil http client
@ -82,7 +87,7 @@ func NewClient(c *opc.Config) (*Client, error) {
return client, nil return client, nil
} }
// Marshalls the request body and returns the resulting byte slice // MarshallRequestBody marshalls the request body and returns the resulting byte slice
// This is split out of the BuildRequestBody method so as to allow // This is split out of the BuildRequestBody method so as to allow
// the developer to print a debug string of the request body if they // the developer to print a debug string of the request body if they
// should so choose. // should so choose.
@ -95,7 +100,7 @@ func (c *Client) MarshallRequestBody(body interface{}) ([]byte, error) {
return json.Marshal(body) return json.Marshal(body)
} }
// Builds an HTTP Request that accepts a pre-marshaled body parameter as a raw byte array // BuildRequestBody builds an HTTP Request that accepts a pre-marshaled body parameter as a raw byte array
// Returns the raw HTTP Request and any error occured // Returns the raw HTTP Request and any error occured
func (c *Client) BuildRequestBody(method, path string, body []byte) (*http.Request, error) { func (c *Client) BuildRequestBody(method, path string, body []byte) (*http.Request, error) {
// Parse URL Path // Parse URL Path
@ -115,13 +120,13 @@ func (c *Client) BuildRequestBody(method, path string, body []byte) (*http.Reque
return nil, err return nil, err
} }
// Adding UserAgent Header // Adding UserAgent Header
req.Header.Add(USER_AGENT_HEADER, *c.UserAgent) req.Header.Add(userAgentHeader, *c.UserAgent)
return req, nil return req, nil
} }
// Build a new HTTP request that doesn't marshall the request body // BuildNonJSONRequest builds a new HTTP request that doesn't marshall the request body
func (c *Client) BuildNonJSONRequest(method, path string, body io.ReadSeeker) (*http.Request, error) { func (c *Client) BuildNonJSONRequest(method, path string, body io.Reader) (*http.Request, error) {
// Parse URL Path // Parse URL Path
urlPath, err := url.Parse(path) urlPath, err := url.Parse(path)
if err != nil { if err != nil {
@ -134,12 +139,56 @@ func (c *Client) BuildNonJSONRequest(method, path string, body io.ReadSeeker) (*
return nil, err return nil, err
} }
// Adding UserAgentHeader // Adding UserAgentHeader
req.Header.Add(USER_AGENT_HEADER, *c.UserAgent) req.Header.Add(userAgentHeader, *c.UserAgent)
return req, nil return req, nil
} }
// This method executes the http.Request from the BuildRequest method. // BuildMultipartFormRequest builds a new HTTP Request for a multipart form request from specifies attributes
func (c *Client) BuildMultipartFormRequest(method, path string, files map[string][]byte, parameters map[string]interface{}) (*http.Request, error) {
urlPath, err := url.Parse(path)
if err != nil {
return nil, err
}
body := new(bytes.Buffer)
writer := multipart.NewWriter(body)
var (
part io.Writer
)
for fileName, fileContents := range files {
part, err = writer.CreateFormFile(fileName, fmt.Sprintf("%s.json", fileName))
if err != nil {
return nil, err
}
_, err = part.Write(fileContents)
if err != nil {
return nil, err
}
}
// Add additional parameters to the writer
for key, val := range parameters {
if val.(string) != "" {
_ = writer.WriteField(key, val.(string))
}
}
err = writer.Close()
if err != nil {
return nil, err
}
req, err := http.NewRequest(method, c.formatURL(urlPath), body)
req.Header.Set("Content-Type", writer.FormDataContentType())
return req, err
}
// ExecuteRequest executes the http.Request from the BuildRequest method.
// It is split up to add additional authentication that is Oracle API dependent. // It is split up to add additional authentication that is Oracle API dependent.
func (c *Client) ExecuteRequest(req *http.Request) (*http.Response, error) { func (c *Client) ExecuteRequest(req *http.Request) (*http.Response, error) {
// Execute request with supplied client // Execute request with supplied client
@ -161,7 +210,10 @@ func (c *Client) ExecuteRequest(req *http.Request) (*http.Response, error) {
// error fields that are possible to be returned we can have stricter error types. // error fields that are possible to be returned we can have stricter error types.
if resp.Body != nil { if resp.Body != nil {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
buf.ReadFrom(resp.Body) _, err = buf.ReadFrom(resp.Body)
if err != nil {
return resp, nil
}
oracleErr.Message = buf.String() oracleErr.Message = buf.String()
} }
@ -177,7 +229,7 @@ func (c *Client) retryRequest(req *http.Request) (*http.Response, error) {
// Double check maxRetries is not nil // Double check maxRetries is not nil
var retries int var retries int
if c.MaxRetries == nil { if c.MaxRetries == nil {
retries = DEFAULT_MAX_RETRIES retries = defaultMaxRetries
} else { } else {
retries = *c.MaxRetries retries = *c.MaxRetries
} }
@ -185,7 +237,28 @@ func (c *Client) retryRequest(req *http.Request) (*http.Response, error) {
var statusCode int var statusCode int
var errMessage string var errMessage string
for i := 0; i < retries; i++ { // Cache the body content for retries.
// This is to allow reuse of the original request for the retries attempts
// as the act of reading the body (when doing the httpClient.Do()) closes the
// Reader.
var body []byte
if req.Body != nil {
var err error
body, err = ioutil.ReadAll(req.Body)
if err != nil {
return nil, err
}
}
// Initial sleep time between retries
sleep := 1 * time.Second
for i := retries; i > 0; i-- {
// replace body with new unread Reader before each request
if len(body) > 0 {
req.Body = ioutil.NopCloser(bytes.NewBuffer(body))
}
resp, err := c.httpClient.Do(req) resp, err := c.httpClient.Do(req)
if err != nil { if err != nil {
return resp, err return resp, err
@ -196,11 +269,23 @@ func (c *Client) retryRequest(req *http.Request) (*http.Response, error) {
} }
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
buf.ReadFrom(resp.Body) _, err = buf.ReadFrom(resp.Body)
if err != nil {
return resp, err
}
errMessage = buf.String() errMessage = buf.String()
statusCode = resp.StatusCode statusCode = resp.StatusCode
c.DebugLogString(fmt.Sprintf("Encountered HTTP (%d) Error: %s", statusCode, errMessage)) c.DebugLogString(fmt.Sprintf("%s %s Encountered HTTP (%d) Error: %s", req.Method, req.URL, statusCode, errMessage))
c.DebugLogString(fmt.Sprintf("%d/%d retries left", i+1, retries)) if i != 1 {
c.DebugLogString(fmt.Sprintf("%d of %d retries remaining. Next retry in %ds", i-1, retries, sleep/time.Second))
time.Sleep(sleep)
// increase sleep time for next retry (exponential backoff with jitter)
// up to a maximum of ~60 seconds
if sleep <= 30*time.Second {
jitter := time.Duration(rand.Int63n(int64(sleep))) / 2
sleep = (sleep * 2) + jitter
}
}
} }
oracleErr := &opc.OracleError{ oracleErr := &opc.OracleError{
@ -216,29 +301,32 @@ func (c *Client) formatURL(path *url.URL) string {
return c.APIEndpoint.ResolveReference(path).String() return c.APIEndpoint.ResolveReference(path).String()
} }
// Retry function // WaitFor - Retry function
func (c *Client) WaitFor(description string, timeout time.Duration, test func() (bool, error)) error { func (c *Client) WaitFor(description string, pollInterval, timeout time.Duration, test func() (bool, error)) error {
tick := time.Tick(1 * time.Second)
timeoutSeconds := int(timeout.Seconds()) timeoutSeconds := int(timeout.Seconds())
pollIntervalSeconds := int(pollInterval.Seconds())
for i := 0; i < timeoutSeconds; i++ { c.DebugLogString(fmt.Sprintf("Starting Wait For %s, polling every %d for %d seconds ", description, pollIntervalSeconds, timeoutSeconds))
select {
case <-tick: for i := 0; i < timeoutSeconds; i += pollIntervalSeconds {
completed, err := test() c.DebugLogString(fmt.Sprintf("Waiting %d seconds for %s (%d/%ds)", pollIntervalSeconds, description, i, timeoutSeconds))
c.DebugLogString(fmt.Sprintf("Waiting for %s (%d/%ds)", description, i, timeoutSeconds)) time.Sleep(pollInterval)
if err != nil || completed { completed, err := test()
return err if err != nil || completed {
} return err
} }
} }
return fmt.Errorf("Timeout waiting for %s", description) return fmt.Errorf("Timeout after %d seconds waiting for %s", timeoutSeconds, description)
} }
// Used to determine if the checked resource was found or not. // WasNotFoundError Used to determine if the checked resource was found or not.
func WasNotFoundError(e error) bool { func WasNotFoundError(e error) bool {
err, ok := e.(*opc.OracleError) err, ok := e.(*opc.OracleError)
if ok { if ok {
if strings.Contains(err.Error(), "No such service exits") {
return true
}
return err.StatusCode == 404 return err.StatusCode == 404
} }
return false return false

View File

@ -1,28 +1,13 @@
package client package client
import ( import (
"bytes"
"fmt"
"net/http"
"github.com/hashicorp/go-oracle-terraform/opc" "github.com/hashicorp/go-oracle-terraform/opc"
) )
// Log a string if debug logs are on // DebugLogString logs a string if debug logs are on
func (c *Client) DebugLogString(str string) { func (c *Client) DebugLogString(str string) {
if c.loglevel != opc.LogDebug { if c.loglevel != opc.LogDebug {
return return
} }
c.logger.Log(str) c.logger.Log(str)
} }
func (c *Client) DebugLogReq(req *http.Request) {
// Don't need to log this if not debugging
if c.loglevel != opc.LogDebug {
return
}
buf := new(bytes.Buffer)
buf.ReadFrom(req.Body)
c.logger.Log(fmt.Sprintf("DEBUG: HTTP %s Req %s: %s",
req.Method, req.URL.String(), buf.String()))
}

View File

@ -6,20 +6,20 @@ type ACLsClient struct {
} }
const ( const (
ACLDescription = "acl" aclDescription = "acl"
ACLContainerPath = "/network/v1/acl/" aclContainerPath = "/network/v1/acl/"
ACLResourcePath = "/network/v1/acl" aclResourcePath = "/network/v1/acl"
) )
// ACLs obtains a ACLsClient which can be used to access to the // ACLs obtains a ACLsClient which can be used to access to the
// ACLs functions of the Compute API // ACLs functions of the Compute API
func (c *ComputeClient) ACLs() *ACLsClient { func (c *Client) ACLs() *ACLsClient {
return &ACLsClient{ return &ACLsClient{
ResourceClient: ResourceClient{ ResourceClient: ResourceClient{
ComputeClient: c, Client: c,
ResourceDescription: ACLDescription, ResourceDescription: aclDescription,
ContainerPath: ACLContainerPath, ContainerPath: aclContainerPath,
ResourceRootPath: ACLResourcePath, ResourceRootPath: aclResourcePath,
}} }}
} }
@ -29,8 +29,10 @@ type ACLInfo struct {
Description string `json:"description"` Description string `json:"description"`
// Indicates whether the ACL is enabled // Indicates whether the ACL is enabled
Enabled bool `json:"enabledFlag"` Enabled bool `json:"enabledFlag"`
// Fully Qualified Domain Name
FQDN string `json:"name"`
// The name of the ACL // The name of the ACL
Name string `json:"name"` Name string
// Tags associated with the ACL // Tags associated with the ACL
Tags []string `json:"tags"` Tags []string `json:"tags"`
// Uniform Resource Identifier for the ACL // Uniform Resource Identifier for the ACL
@ -133,6 +135,6 @@ func (c *ACLsClient) DeleteACL(deleteInput *DeleteACLInput) error {
} }
func (c *ACLsClient) success(aclInfo *ACLInfo) (*ACLInfo, error) { func (c *ACLsClient) success(aclInfo *ACLInfo) (*ACLInfo, error) {
aclInfo.Name = c.getUnqualifiedName(aclInfo.Name) aclInfo.Name = c.getUnqualifiedName(aclInfo.FQDN)
return aclInfo, nil return aclInfo, nil
} }

View File

@ -12,7 +12,7 @@ type AuthenticationReq struct {
} }
// Get a new auth cookie for the compute client // Get a new auth cookie for the compute client
func (c *ComputeClient) getAuthenticationCookie() error { func (c *Client) getAuthenticationCookie() error {
req := AuthenticationReq{ req := AuthenticationReq{
User: c.getUserName(), User: c.getUserName(),
Password: *c.client.Password, Password: *c.client.Password,

View File

@ -11,19 +11,20 @@ import (
"github.com/hashicorp/go-oracle-terraform/opc" "github.com/hashicorp/go-oracle-terraform/opc"
) )
const CMP_ACME = "/Compute-%s" const cmpACME = "/Compute-%s"
const CMP_USERNAME = "/Compute-%s/%s" const cmpUsername = "/Compute-%s/%s"
const CMP_QUALIFIED_NAME = "%s/%s" const cmpQualifiedName = "%s/%s"
// Client represents an authenticated compute client, with compute credentials and an api client. // Client represents an authenticated compute client, with compute credentials and an api client.
type ComputeClient struct { type Client struct {
client *client.Client client *client.Client
authCookie *http.Cookie authCookie *http.Cookie
cookieIssued time.Time cookieIssued time.Time
} }
func NewComputeClient(c *opc.Config) (*ComputeClient, error) { // NewComputeClient returns a compute client to interact with the Oracle Compute Infrastructure - Classic APIs
computeClient := &ComputeClient{} func NewComputeClient(c *opc.Config) (*Client, error) {
computeClient := &Client{}
client, err := client.NewClient(c) client, err := client.NewClient(c)
if err != nil { if err != nil {
return nil, err return nil, err
@ -37,7 +38,7 @@ func NewComputeClient(c *opc.Config) (*ComputeClient, error) {
return computeClient, nil return computeClient, nil
} }
func (c *ComputeClient) executeRequest(method, path string, body interface{}) (*http.Response, error) { func (c *Client) executeRequest(method, path string, body interface{}) (*http.Response, error) {
reqBody, err := c.client.MarshallRequestBody(body) reqBody, err := c.client.MarshallRequestBody(body)
if err != nil { if err != nil {
return nil, err return nil, err
@ -62,7 +63,7 @@ func (c *ComputeClient) executeRequest(method, path string, body interface{}) (*
if c.authCookie != nil { if c.authCookie != nil {
if time.Since(c.cookieIssued).Minutes() > 25 { if time.Since(c.cookieIssued).Minutes() > 25 {
c.authCookie = nil c.authCookie = nil
if err := c.getAuthenticationCookie(); err != nil { if err = c.getAuthenticationCookie(); err != nil {
return nil, err return nil, err
} }
} }
@ -76,42 +77,42 @@ func (c *ComputeClient) executeRequest(method, path string, body interface{}) (*
return resp, nil return resp, nil
} }
func (c *ComputeClient) getACME() string { func (c *Client) getACME() string {
return fmt.Sprintf(CMP_ACME, *c.client.IdentityDomain) return fmt.Sprintf(cmpACME, *c.client.IdentityDomain)
} }
func (c *ComputeClient) getUserName() string { func (c *Client) getUserName() string {
return fmt.Sprintf(CMP_USERNAME, *c.client.IdentityDomain, *c.client.UserName) return fmt.Sprintf(cmpUsername, *c.client.IdentityDomain, *c.client.UserName)
} }
func (c *ComputeClient) getQualifiedACMEName(name string) string { func (c *Client) getQualifiedACMEName(name string) string {
if name == "" { if name == "" {
return "" return ""
} }
if strings.HasPrefix(name, "/Compute-") && len(strings.Split(name, "/")) == 1 { if strings.HasPrefix(name, "/Compute-") && len(strings.Split(name, "/")) == 1 {
return name return name
} }
return fmt.Sprintf(CMP_QUALIFIED_NAME, c.getACME(), name) return fmt.Sprintf(cmpQualifiedName, c.getACME(), name)
} }
// From compute_client // From compute_client
// GetObjectName returns the fully-qualified name of an OPC object, e.g. /identity-domain/user@email/{name} // GetObjectName returns the fully-qualified name of an OPC object, e.g. /identity-domain/user@email/{name}
func (c *ComputeClient) getQualifiedName(name string) string { func (c *Client) getQualifiedName(name string) string {
if name == "" { if name == "" {
return "" return ""
} }
if strings.HasPrefix(name, "/oracle") || strings.HasPrefix(name, "/Compute-") { if strings.HasPrefix(name, "/oracle") || strings.HasPrefix(name, "/Compute-") {
return name return name
} }
return fmt.Sprintf(CMP_QUALIFIED_NAME, c.getUserName(), name) return fmt.Sprintf(cmpQualifiedName, c.getUserName(), name)
} }
func (c *ComputeClient) getObjectPath(root, name string) string { func (c *Client) getObjectPath(root, name string) string {
return fmt.Sprintf("%s%s", root, c.getQualifiedName(name)) return fmt.Sprintf("%s%s", root, c.getQualifiedName(name))
} }
// GetUnqualifiedName returns the unqualified name of an OPC object, e.g. the {name} part of /identity-domain/user@email/{name} // GetUnqualifiedName returns the unqualified name of an OPC object, e.g. the {name} part of /identity-domain/user@email/{name}
func (c *ComputeClient) getUnqualifiedName(name string) string { func (c *Client) getUnqualifiedName(name string) string {
if name == "" { if name == "" {
return name return name
} }
@ -126,40 +127,40 @@ func (c *ComputeClient) getUnqualifiedName(name string) string {
return strings.Join(nameParts[3:], "/") return strings.Join(nameParts[3:], "/")
} }
func (c *ComputeClient) unqualify(names ...*string) { func (c *Client) unqualify(names ...*string) {
for _, name := range names { for _, name := range names {
*name = c.getUnqualifiedName(*name) *name = c.getUnqualifiedName(*name)
} }
} }
func (c *ComputeClient) unqualifyUrl(url *string) { func (c *Client) unqualifyURL(url *string) {
var validID = regexp.MustCompile(`(\/(Compute[^\/\s]+))(\/[^\/\s]+)(\/[^\/\s]+)`) var validID = regexp.MustCompile(`(\/(Compute[^\/\s]+))(\/[^\/\s]+)(\/[^\/\s]+)`)
name := validID.FindString(*url) name := validID.FindString(*url)
*url = c.getUnqualifiedName(name) *url = c.getUnqualifiedName(name)
} }
func (c *ComputeClient) getQualifiedList(list []string) []string { func (c *Client) getQualifiedList(list []string) []string {
for i, name := range list { for i, name := range list {
list[i] = c.getQualifiedName(name) list[i] = c.getQualifiedName(name)
} }
return list return list
} }
func (c *ComputeClient) getUnqualifiedList(list []string) []string { func (c *Client) getUnqualifiedList(list []string) []string {
for i, name := range list { for i, name := range list {
list[i] = c.getUnqualifiedName(name) list[i] = c.getUnqualifiedName(name)
} }
return list return list
} }
func (c *ComputeClient) getQualifiedListName(name string) string { func (c *Client) getQualifiedListName(name string) string {
nameParts := strings.Split(name, ":") nameParts := strings.Split(name, ":")
listType := nameParts[0] listType := nameParts[0]
listName := nameParts[1] listName := nameParts[1]
return fmt.Sprintf("%s:%s", listType, c.getQualifiedName(listName)) return fmt.Sprintf("%s:%s", listType, c.getQualifiedName(listName))
} }
func (c *ComputeClient) unqualifyListName(qualifiedName string) string { func (c *Client) unqualifyListName(qualifiedName string) string {
nameParts := strings.Split(qualifiedName, ":") nameParts := strings.Split(qualifiedName, ":")
listType := nameParts[0] listType := nameParts[0]
listName := nameParts[1] listName := nameParts[1]

View File

@ -11,7 +11,7 @@ import (
// ResourceClient is an AuthenticatedClient with some additional information about the resources to be addressed. // ResourceClient is an AuthenticatedClient with some additional information about the resources to be addressed.
type ResourceClient struct { type ResourceClient struct {
*ComputeClient *Client
ResourceDescription string ResourceDescription string
ContainerPath string ContainerPath string
ResourceRootPath string ResourceRootPath string
@ -58,12 +58,7 @@ func (c *ResourceClient) deleteResource(name string) error {
objectPath = c.ResourceRootPath objectPath = c.ResourceRootPath
} }
_, err := c.executeRequest("DELETE", objectPath, nil) _, err := c.executeRequest("DELETE", objectPath, nil)
if err != nil { return err
return err
}
// No errors and no response body to write
return nil
} }
func (c *ResourceClient) deleteOrchestration(name string) error { func (c *ResourceClient) deleteOrchestration(name string) error {
@ -77,22 +72,20 @@ func (c *ResourceClient) deleteOrchestration(name string) error {
objectPath = fmt.Sprintf("%s?terminate=True", objectPath) objectPath = fmt.Sprintf("%s?terminate=True", objectPath)
_, err := c.executeRequest("DELETE", objectPath, nil) _, err := c.executeRequest("DELETE", objectPath, nil)
if err != nil { return err
return err
}
// No errors and no response body to write
return nil
} }
func (c *ResourceClient) unmarshalResponseBody(resp *http.Response, iface interface{}) error { func (c *ResourceClient) unmarshalResponseBody(resp *http.Response, iface interface{}) error {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
buf.ReadFrom(resp.Body) _, err := buf.ReadFrom(resp.Body)
if err != nil {
return err
}
c.client.DebugLogString(fmt.Sprintf("HTTP Resp (%d): %s", resp.StatusCode, buf.String())) c.client.DebugLogString(fmt.Sprintf("HTTP Resp (%d): %s", resp.StatusCode, buf.String()))
// JSON decode response into interface // JSON decode response into interface
var tmp interface{} var tmp interface{}
dcd := json.NewDecoder(buf) dcd := json.NewDecoder(buf)
if err := dcd.Decode(&tmp); err != nil { if err = dcd.Decode(&tmp); err != nil {
return err return err
} }

View File

@ -1,9 +1,9 @@
package compute package compute
const ( const (
ImageListDescription = "Image List" imageListDescription = "Image List"
ImageListContainerPath = "/imagelist/" imageListContainerPath = "/imagelist/"
ImageListResourcePath = "/imagelist" imageListResourcePath = "/imagelist"
) )
// ImageListClient is a client for the Image List functions of the Compute API. // ImageListClient is a client for the Image List functions of the Compute API.
@ -13,16 +13,17 @@ type ImageListClient struct {
// ImageList obtains an ImageListClient which can be used to access to the // ImageList obtains an ImageListClient which can be used to access to the
// Image List functions of the Compute API // Image List functions of the Compute API
func (c *ComputeClient) ImageList() *ImageListClient { func (c *Client) ImageList() *ImageListClient {
return &ImageListClient{ return &ImageListClient{
ResourceClient: ResourceClient{ ResourceClient: ResourceClient{
ComputeClient: c, Client: c,
ResourceDescription: ImageListDescription, ResourceDescription: imageListDescription,
ContainerPath: ImageListContainerPath, ContainerPath: imageListContainerPath,
ResourceRootPath: ImageListResourcePath, ResourceRootPath: imageListResourcePath,
}} }}
} }
// ImageListEntry details the attributes from an image list entry
type ImageListEntry struct { type ImageListEntry struct {
// User-defined parameters, in JSON format, that can be passed to an instance of this machine image when it is launched. // User-defined parameters, in JSON format, that can be passed to an instance of this machine image when it is launched.
Attributes map[string]interface{} `json:"attributes"` Attributes map[string]interface{} `json:"attributes"`
@ -51,8 +52,11 @@ type ImageList struct {
// Each machine image in an image list is identified by an image list entry. // Each machine image in an image list is identified by an image list entry.
Entries []ImageListEntry `json:"entries"` Entries []ImageListEntry `json:"entries"`
// Fully Qualified Domain Name
FQDN string `json:"name"`
// The name of the Image List // The name of the Image List
Name string `json:"name"` Name string
// Uniform Resource Identifier // Uniform Resource Identifier
URI string `json:"uri"` URI string `json:"uri"`
@ -86,7 +90,7 @@ func (c *ImageListClient) CreateImageList(createInput *CreateImageListInput) (*I
return c.success(&imageList) return c.success(&imageList)
} }
// DeleteKeyInput describes the image list to delete // DeleteImageListInput describes the image list to delete
type DeleteImageListInput struct { type DeleteImageListInput struct {
// The name of the Image List // The name of the Image List
Name string `json:"name"` Name string `json:"name"`
@ -144,7 +148,7 @@ func (c *ImageListClient) UpdateImageList(updateInput *UpdateImageListInput) (*I
} }
func (c *ImageListClient) success(imageList *ImageList) (*ImageList, error) { func (c *ImageListClient) success(imageList *ImageList) (*ImageList, error) {
c.unqualify(&imageList.Name) imageList.Name = c.getUnqualifiedName(imageList.FQDN)
for _, v := range imageList.Entries { for _, v := range imageList.Entries {
v.MachineImages = c.getUnqualifiedList(v.MachineImages) v.MachineImages = c.getUnqualifiedList(v.MachineImages)

View File

@ -3,24 +3,25 @@ package compute
import "fmt" import "fmt"
const ( const (
ImageListEntryDescription = "image list entry" imageListEntryDescription = "image list entry"
ImageListEntryContainerPath = "/imagelist" imageListEntryContainerPath = "/imagelist"
ImageListEntryResourcePath = "/imagelist" imageListEntryResourcePath = "/imagelist"
) )
// ImageListEntriesClient specifies the parameters for an image list entries client
type ImageListEntriesClient struct { type ImageListEntriesClient struct {
ResourceClient ResourceClient
} }
// ImageListEntries() returns an ImageListEntriesClient that can be used to access the // ImageListEntries returns an ImageListEntriesClient that can be used to access the
// necessary CRUD functions for Image List Entry's. // necessary CRUD functions for Image List Entry's.
func (c *ComputeClient) ImageListEntries() *ImageListEntriesClient { func (c *Client) ImageListEntries() *ImageListEntriesClient {
return &ImageListEntriesClient{ return &ImageListEntriesClient{
ResourceClient: ResourceClient{ ResourceClient: ResourceClient{
ComputeClient: c, Client: c,
ResourceDescription: ImageListEntryDescription, ResourceDescription: imageListEntryDescription,
ContainerPath: ImageListEntryContainerPath, ContainerPath: imageListEntryContainerPath,
ResourceRootPath: ImageListEntryResourcePath, ResourceRootPath: imageListEntryResourcePath,
}, },
} }
} }
@ -39,11 +40,12 @@ type ImageListEntryInfo struct {
// A list of machine images. // A list of machine images.
MachineImages []string `json:"machineimages"` MachineImages []string `json:"machineimages"`
// Uniform Resource Identifier for the Image List Entry // Uniform Resource Identifier for the Image List Entry
Uri string `json:"uri"` URI string `json:"uri"`
// Version number of these machineImages in the imagelist. // Version number of these machineImages in the imagelist.
Version int `json:"version"` Version int `json:"version"`
} }
// CreateImageListEntryInput specifies the parameters needed to creat an image list entry
type CreateImageListEntryInput struct { type CreateImageListEntryInput struct {
// The name of the Image List // The name of the Image List
Name string Name string
@ -62,7 +64,7 @@ type CreateImageListEntryInput struct {
Version int `json:"version"` Version int `json:"version"`
} }
// Create a new Image List Entry from an ImageListEntriesClient and an input struct. // CreateImageListEntry creates a new Image List Entry from an ImageListEntriesClient and an input struct.
// Returns a populated Info struct for the Image List Entry, and any errors // Returns a populated Info struct for the Image List Entry, and any errors
func (c *ImageListEntriesClient) CreateImageListEntry(input *CreateImageListEntryInput) (*ImageListEntryInfo, error) { func (c *ImageListEntriesClient) CreateImageListEntry(input *CreateImageListEntryInput) (*ImageListEntryInfo, error) {
c.updateClientPaths(input.Name, -1) c.updateClientPaths(input.Name, -1)
@ -73,6 +75,7 @@ func (c *ImageListEntriesClient) CreateImageListEntry(input *CreateImageListEntr
return c.success(&imageListEntryInfo) return c.success(&imageListEntryInfo)
} }
// GetImageListEntryInput details the parameters needed to retrive an image list entry
type GetImageListEntryInput struct { type GetImageListEntryInput struct {
// The name of the Image List // The name of the Image List
Name string Name string
@ -80,7 +83,7 @@ type GetImageListEntryInput struct {
Version int Version int
} }
// Returns a populated ImageListEntryInfo struct from an input struct // GetImageListEntry returns a populated ImageListEntryInfo struct from an input struct
func (c *ImageListEntriesClient) GetImageListEntry(input *GetImageListEntryInput) (*ImageListEntryInfo, error) { func (c *ImageListEntriesClient) GetImageListEntry(input *GetImageListEntryInput) (*ImageListEntryInfo, error) {
c.updateClientPaths(input.Name, input.Version) c.updateClientPaths(input.Name, input.Version)
var imageListEntryInfo ImageListEntryInfo var imageListEntryInfo ImageListEntryInfo
@ -90,6 +93,7 @@ func (c *ImageListEntriesClient) GetImageListEntry(input *GetImageListEntryInput
return c.success(&imageListEntryInfo) return c.success(&imageListEntryInfo)
} }
// DeleteImageListEntryInput details the parameters needed to delete an image list entry
type DeleteImageListEntryInput struct { type DeleteImageListEntryInput struct {
// The name of the Image List // The name of the Image List
Name string Name string
@ -97,6 +101,7 @@ type DeleteImageListEntryInput struct {
Version int Version int
} }
// DeleteImageListEntry deletes the specified image list entry
func (c *ImageListEntriesClient) DeleteImageListEntry(input *DeleteImageListEntryInput) error { func (c *ImageListEntriesClient) DeleteImageListEntry(input *DeleteImageListEntryInput) error {
c.updateClientPaths(input.Name, input.Version) c.updateClientPaths(input.Name, input.Version)
return c.deleteResource("") return c.deleteResource("")
@ -105,8 +110,8 @@ func (c *ImageListEntriesClient) DeleteImageListEntry(input *DeleteImageListEntr
func (c *ImageListEntriesClient) updateClientPaths(name string, version int) { func (c *ImageListEntriesClient) updateClientPaths(name string, version int) {
var containerPath, resourcePath string var containerPath, resourcePath string
name = c.getQualifiedName(name) name = c.getQualifiedName(name)
containerPath = ImageListEntryContainerPath + name + "/entry/" containerPath = imageListEntryContainerPath + name + "/entry/"
resourcePath = ImageListEntryContainerPath + name + "/entry" resourcePath = imageListEntryContainerPath + name + "/entry"
if version != -1 { if version != -1 {
containerPath = fmt.Sprintf("%s%d", containerPath, version) containerPath = fmt.Sprintf("%s%d", containerPath, version)
resourcePath = fmt.Sprintf("%s/%d", resourcePath, version) resourcePath = fmt.Sprintf("%s/%d", resourcePath, version)
@ -117,6 +122,6 @@ func (c *ImageListEntriesClient) updateClientPaths(name string, version int) {
// Unqualifies any qualified fields in the IPNetworkInfo struct // Unqualifies any qualified fields in the IPNetworkInfo struct
func (c *ImageListEntriesClient) success(info *ImageListEntryInfo) (*ImageListEntryInfo, error) { func (c *ImageListEntriesClient) success(info *ImageListEntryInfo) (*ImageListEntryInfo, error) {
c.unqualifyUrl(&info.Uri) c.unqualifyURL(&info.URI)
return info, nil return info, nil
} }

View File

@ -9,8 +9,10 @@ import (
"github.com/hashicorp/go-oracle-terraform/client" "github.com/hashicorp/go-oracle-terraform/client"
) )
const WaitForInstanceReadyTimeout = time.Duration(3600 * time.Second) const waitForInstanceReadyPollInterval = 10 * time.Second
const WaitForInstanceDeleteTimeout = time.Duration(3600 * time.Second) const waitForInstanceReadyTimeout = 3600 * time.Second
const waitForInstanceDeletePollInterval = 10 * time.Second
const waitForInstanceDeleteTimeout = 3600 * time.Second
// InstancesClient is a client for the Instance functions of the Compute API. // InstancesClient is a client for the Instance functions of the Compute API.
type InstancesClient struct { type InstancesClient struct {
@ -19,33 +21,45 @@ type InstancesClient struct {
// Instances obtains an InstancesClient which can be used to access to the // Instances obtains an InstancesClient which can be used to access to the
// Instance functions of the Compute API // Instance functions of the Compute API
func (c *ComputeClient) Instances() *InstancesClient { func (c *Client) Instances() *InstancesClient {
return &InstancesClient{ return &InstancesClient{
ResourceClient: ResourceClient{ ResourceClient: ResourceClient{
ComputeClient: c, Client: c,
ResourceDescription: "instance", ResourceDescription: "instance",
ContainerPath: "/launchplan/", ContainerPath: "/launchplan/",
ResourceRootPath: "/instance", ResourceRootPath: "/instance",
}} }}
} }
// InstanceState specifies the constants that an instance state can be in
type InstanceState string type InstanceState string
const ( const (
InstanceRunning InstanceState = "running" // InstanceRunning - running
InstanceRunning InstanceState = "running"
// InstanceInitializing - initializing
InstanceInitializing InstanceState = "initializing" InstanceInitializing InstanceState = "initializing"
InstancePreparing InstanceState = "preparing" // InstancePreparing - preparing
InstanceStarting InstanceState = "starting" InstancePreparing InstanceState = "preparing"
InstanceStopping InstanceState = "stopping" // InstanceStarting - starting
InstanceShutdown InstanceState = "shutdown" InstanceStarting InstanceState = "starting"
InstanceQueued InstanceState = "queued" // InstanceStopping - stopping
InstanceError InstanceState = "error" InstanceStopping InstanceState = "stopping"
// InstanceShutdown - shutdown
InstanceShutdown InstanceState = "shutdown"
// InstanceQueued - queued
InstanceQueued InstanceState = "queued"
// InstanceError - error
InstanceError InstanceState = "error"
) )
// InstanceDesiredState specifies the constants that for a desired instance state
type InstanceDesiredState string type InstanceDesiredState string
const ( const (
InstanceDesiredRunning InstanceDesiredState = "running" // InstanceDesiredRunning - running
InstanceDesiredRunning InstanceDesiredState = "running"
// InstanceDesiredShutdown - shutdown
InstanceDesiredShutdown InstanceDesiredState = "shutdown" InstanceDesiredShutdown InstanceDesiredState = "shutdown"
) )
@ -54,31 +68,24 @@ type InstanceInfo struct {
// The ID for the instance. Set by the SDK based on the request - not the API. // The ID for the instance. Set by the SDK based on the request - not the API.
ID string ID string
// A dictionary of attributes to be made available to the instance.
// A value with the key "userdata" will be made available in an EC2-compatible manner.
Attributes map[string]interface{} `json:"attributes"`
// The availability domain for the instance // The availability domain for the instance
AvailabilityDomain string `json:"availability_domain"` AvailabilityDomain string `json:"availability_domain"`
// Boot order list.
BootOrder []int `json:"boot_order"`
// The default domain to use for the hostname and DNS lookups // The default domain to use for the hostname and DNS lookups
Domain string `json:"domain"` Domain string `json:"domain"`
// The desired state of an instance // The desired state of an instance
DesiredState InstanceDesiredState `json:"desired_state"` DesiredState InstanceDesiredState `json:"desired_state"`
// Optional ImageListEntry number. Default will be used if not specified
Entry int `json:"entry"`
// The reason for the instance going to error state, if available. // The reason for the instance going to error state, if available.
ErrorReason string `json:"error_reason"` ErrorReason string `json:"error_reason"`
// SSH Server Fingerprint presented by the instance // SSH Server Fingerprint presented by the instance
Fingerprint string `json:"fingerprint"` Fingerprint string `json:"fingerprint"`
// Fully Qualified Domain Name
FQDN string `json:"name"`
// The hostname for the instance // The hostname for the instance
Hostname string `json:"hostname"` Hostname string `json:"hostname"`
@ -95,10 +102,7 @@ type InstanceInfo struct {
Label string `json:"label"` Label string `json:"label"`
// Name of this instance, generated by the server. // Name of this instance, generated by the server.
Name string `json:"name"` Name string
// Mapping of to network specifiers for virtual NICs to be attached to this instance.
Networking map[string]NetworkingInfo `json:"networking"`
// A list of strings specifying arbitrary tags on nodes to be matched on placement. // A list of strings specifying arbitrary tags on nodes to be matched on placement.
PlacementRequirements []string `json:"placement_requirements"` PlacementRequirements []string `json:"placement_requirements"`
@ -118,9 +122,6 @@ type InstanceInfo struct {
// Resolvers to use instead of the default resolvers // Resolvers to use instead of the default resolvers
Resolvers []string `json:"resolvers"` Resolvers []string `json:"resolvers"`
// Add PTR records for the hostname
ReverseDNS bool `json:"reverse_dns"`
// Type of instance, as defined on site configuration. // Type of instance, as defined on site configuration.
Shape string `json:"shape"` Shape string `json:"shape"`
@ -145,14 +146,31 @@ type InstanceInfo struct {
// vCable for this instance. // vCable for this instance.
VCableID string `json:"vcable_id"` VCableID string `json:"vcable_id"`
// IP Address and port of the VNC console for the instance
VNC string `json:"vnc"`
// Add PTR records for the hostname
ReverseDNS bool `json:"reverse_dns"`
// Specify if the devices created for the instance are virtio devices. If not specified, the default // Specify if the devices created for the instance are virtio devices. If not specified, the default
// will come from the cluster configuration file // will come from the cluster configuration file
Virtio bool `json:"virtio,omitempty"` Virtio bool `json:"virtio,omitempty"`
// IP Address and port of the VNC console for the instance // Optional ImageListEntry number. Default will be used if not specified
VNC string `json:"vnc"` Entry int `json:"entry"`
// Boot order list.
BootOrder []int `json:"boot_order"`
// A dictionary of attributes to be made available to the instance.
// A value with the key "userdata" will be made available in an EC2-compatible manner.
Attributes map[string]interface{} `json:"attributes"`
// Mapping of to network specifiers for virtual NICs to be attached to this instance.
Networking map[string]NetworkingInfo `json:"networking"`
} }
// StorageAttachment specifies the parameters for a storage attachment
type StorageAttachment struct { type StorageAttachment struct {
// The index number for the volume. // The index number for the volume.
Index int `json:"index"` Index int `json:"index"`
@ -165,12 +183,11 @@ type StorageAttachment struct {
} }
func (i *InstanceInfo) getInstanceName() string { func (i *InstanceInfo) getInstanceName() string {
return fmt.Sprintf(CMP_QUALIFIED_NAME, i.Name, i.ID) return fmt.Sprintf(cmpQualifiedName, i.Name, i.ID)
} }
// CreateInstanceInput specifies the parameters needed to create an instance
type CreateInstanceInput struct { type CreateInstanceInput struct {
// Optional ImageListEntry number. Default will be used if not specified
Entry int `json:"entry,omitempty"`
// A dictionary of user-defined attributes to be made available to the instance. // A dictionary of user-defined attributes to be made available to the instance.
// Optional // Optional
Attributes map[string]interface{} `json:"attributes"` Attributes map[string]interface{} `json:"attributes"`
@ -181,6 +198,9 @@ type CreateInstanceInput struct {
// Omits if empty. // Omits if empty.
// Optional // Optional
DesiredState InstanceDesiredState `json:"desired_state,omitempty"` DesiredState InstanceDesiredState `json:"desired_state,omitempty"`
// ImageListEntry number. Default will be used if not specified
// Optional
Entry int `json:"entry,omitempty"`
// The host name assigned to the instance. On an Oracle Linux instance, // The host name assigned to the instance. On an Oracle Linux instance,
// this host name is displayed in response to the hostname command. // this host name is displayed in response to the hostname command.
// Only relative DNS is supported. The domain name is suffixed to the host name // Only relative DNS is supported. The domain name is suffixed to the host name
@ -220,6 +240,7 @@ type CreateInstanceInput struct {
Timeout time.Duration `json:"-"` Timeout time.Duration `json:"-"`
} }
// StorageAttachmentInput specifies the attributes needed to attach a storage attachment
type StorageAttachmentInput struct { type StorageAttachmentInput struct {
// The index number for the volume. The allowed range is 1 to 10. // The index number for the volume. The allowed range is 1 to 10.
// If you want to use a storage volume as the boot disk for an instance, you must specify the index number for that volume as 1. // If you want to use a storage volume as the boot disk for an instance, you must specify the index number for that volume as 1.
@ -230,16 +251,21 @@ type StorageAttachmentInput struct {
Volume string `json:"volume"` Volume string `json:"volume"`
} }
// ReservationPrefix - ipreservation
const ReservationPrefix = "ipreservation" const ReservationPrefix = "ipreservation"
// ReservationIPPrefix - network/v1/ipreservation
const ReservationIPPrefix = "network/v1/ipreservation" const ReservationIPPrefix = "network/v1/ipreservation"
// NICModel specifies the constants that a nic model can be in
type NICModel string type NICModel string
const ( const (
// NICDefaultModel - e1000
NICDefaultModel NICModel = "e1000" NICDefaultModel NICModel = "e1000"
) )
// Struct of Networking info from a populated instance, or to be used as input to create an instance // NetworkingInfo struct of Networking info from a populated instance, or to be used as input to create an instance
type NetworkingInfo struct { type NetworkingInfo struct {
// The DNS name for the Shared network (Required) // The DNS name for the Shared network (Required)
// DNS A Record for an IP Network (Optional) // DNS A Record for an IP Network (Optional)
@ -293,20 +319,23 @@ type NetworkingInfo struct {
VnicSets []string `json:"vnicsets,omitempty"` VnicSets []string `json:"vnicsets,omitempty"`
} }
// LaunchPlan defines a launch plan, used to launch instances with the supplied InstanceSpec(s) // LaunchPlanInput defines a launch plan, used to launch instances with the supplied InstanceSpec(s)
type LaunchPlanInput struct { type LaunchPlanInput struct {
// Describes an array of instances which should be launched // Describes an array of instances which should be launched
Instances []CreateInstanceInput `json:"instances"` Instances []CreateInstanceInput `json:"instances"`
// Time to wait between polls to check status
PollInterval time.Duration `json:"-"`
// Time to wait for instance boot // Time to wait for instance boot
Timeout time.Duration `json:"-"` Timeout time.Duration `json:"-"`
} }
// LaunchPlanResponse details the response recieved when submitting a launchplan
type LaunchPlanResponse struct { type LaunchPlanResponse struct {
// An array of instances which have been launched // An array of instances which have been launched
Instances []InstanceInfo `json:"instances"` Instances []InstanceInfo `json:"instances"`
} }
// LaunchInstance creates and submits a LaunchPlan to launch a new instance. // CreateInstance creates and submits a LaunchPlan to launch a new instance.
func (c *InstancesClient) CreateInstance(input *CreateInstanceInput) (*InstanceInfo, error) { func (c *InstancesClient) CreateInstance(input *CreateInstanceInput) (*InstanceInfo, error) {
qualifiedSSHKeys := []string{} qualifiedSSHKeys := []string{}
for _, key := range input.SSHKeys { for _, key := range input.SSHKeys {
@ -326,7 +355,7 @@ func (c *InstancesClient) CreateInstance(input *CreateInstanceInput) (*InstanceI
input.Networking = c.qualifyNetworking(input.Networking) input.Networking = c.qualifyNetworking(input.Networking)
input.Name = fmt.Sprintf(CMP_QUALIFIED_NAME, c.getUserName(), input.Name) input.Name = c.getQualifiedName(input.Name)
plan := LaunchPlanInput{ plan := LaunchPlanInput{
Instances: []CreateInstanceInput{*input}, Instances: []CreateInstanceInput{*input},
@ -337,12 +366,12 @@ func (c *InstancesClient) CreateInstance(input *CreateInstanceInput) (*InstanceI
instanceInfo *InstanceInfo instanceInfo *InstanceInfo
instanceError error instanceError error
) )
for i := 0; i < *c.ComputeClient.client.MaxRetries; i++ { for i := 0; i < *c.Client.client.MaxRetries; i++ {
c.client.DebugLogString(fmt.Sprintf("(Iteration: %d of %d) Creating instance with name %s\n Plan: %+v", i, *c.ComputeClient.client.MaxRetries, input.Name, plan)) c.client.DebugLogString(fmt.Sprintf("(Iteration: %d of %d) Creating instance with name %s\n Plan: %+v", i, *c.Client.client.MaxRetries, input.Name, plan))
instanceInfo, instanceError = c.startInstance(input.Name, plan) instanceInfo, instanceError = c.startInstance(input.Name, plan)
if instanceError == nil { if instanceError == nil {
c.client.DebugLogString(fmt.Sprintf("(Iteration: %d of %d) Finished creating instance with name %s\n Info: %+v", i, *c.ComputeClient.client.MaxRetries, input.Name, instanceInfo)) c.client.DebugLogString(fmt.Sprintf("(Iteration: %d of %d) Finished creating instance with name %s\n Info: %+v", i, *c.Client.client.MaxRetries, input.Name, instanceInfo))
return instanceInfo, nil return instanceInfo, nil
} }
} }
@ -366,14 +395,16 @@ func (c *InstancesClient) startInstance(name string, plan LaunchPlanInput) (*Ins
ID: responseBody.Instances[0].ID, ID: responseBody.Instances[0].ID,
} }
//timeout := WaitForInstanceReadyTimeout if plan.PollInterval == 0 {
plan.PollInterval = waitForInstanceReadyPollInterval
}
if plan.Timeout == 0 { if plan.Timeout == 0 {
plan.Timeout = WaitForInstanceReadyTimeout plan.Timeout = waitForInstanceReadyTimeout
} }
// Wait for instance to be ready and return the result // Wait for instance to be ready and return the result
// Don't have to unqualify any objects, as the GetInstance method will handle that // Don't have to unqualify any objects, as the GetInstance method will handle that
instanceInfo, instanceError := c.WaitForInstanceRunning(getInput, plan.Timeout) instanceInfo, instanceError := c.WaitForInstanceRunning(getInput, plan.PollInterval, plan.Timeout)
// If the instance enters an error state we need to delete the instance and retry // If the instance enters an error state we need to delete the instance and retry
if instanceError != nil { if instanceError != nil {
deleteInput := &DeleteInstanceInput{ deleteInput := &DeleteInstanceInput{
@ -389,17 +420,18 @@ func (c *InstancesClient) startInstance(name string, plan LaunchPlanInput) (*Ins
return instanceInfo, nil return instanceInfo, nil
} }
// Both of these fields are required. If they're not provided, things go wrong in // GetInstanceInput specifies the parameters needed to retrieve an instance
// incredibly amazing ways.
type GetInstanceInput struct { type GetInstanceInput struct {
// The Unqualified Name of this Instance // The Unqualified Name of this Instance
// Required
Name string Name string
// The Unqualified ID of this Instance // The Unqualified ID of this Instance
// Required
ID string ID string
} }
func (g *GetInstanceInput) String() string { func (g *GetInstanceInput) String() string {
return fmt.Sprintf(CMP_QUALIFIED_NAME, g.Name, g.ID) return fmt.Sprintf(cmpQualifiedName, g.Name, g.ID)
} }
// GetInstance retrieves information about an instance. // GetInstance retrieves information about an instance.
@ -413,13 +445,13 @@ func (c *InstancesClient) GetInstance(input *GetInstanceInput) (*InstanceInfo, e
return nil, err return nil, err
} }
if responseBody.Name == "" { if responseBody.FQDN == "" {
return nil, fmt.Errorf("Empty response body when requesting instance %s", input.Name) return nil, fmt.Errorf("Empty response body when requesting instance %s", input.Name)
} }
// The returned 'Name' attribute is the fully qualified instance name + "/" + ID // The returned 'Name' attribute is the fully qualified instance name + "/" + ID
// Split these out to accurately populate the fields // Split these out to accurately populate the fields
nID := strings.Split(c.getUnqualifiedName(responseBody.Name), "/") nID := strings.Split(c.getUnqualifiedName(responseBody.FQDN), "/")
responseBody.Name = nID[0] responseBody.Name = nID[0]
responseBody.ID = nID[1] responseBody.ID = nID[1]
@ -442,18 +474,20 @@ func (c *InstancesClient) GetInstance(input *GetInstanceInput) (*InstanceInfo, e
return &responseBody, nil return &responseBody, nil
} }
// InstancesInfo specifies a list of instances
type InstancesInfo struct { type InstancesInfo struct {
Instances []InstanceInfo `json:"result"` Instances []InstanceInfo `json:"result"`
} }
type GetInstanceIdInput struct { // GetInstanceIDInput specifies the parameters needed to retrieve an instance
type GetInstanceIDInput struct {
// Name of the instance you want to get // Name of the instance you want to get
Name string Name string
} }
// GetInstanceFromName loops through all the instances and finds the instance for the given name // GetInstanceFromName loops through all the instances and finds the instance for the given name
// This is needed for orchestration since it doesn't return the id for the instance it creates. // This is needed for orchestration since it doesn't return the id for the instance it creates.
func (c *InstancesClient) GetInstanceFromName(input *GetInstanceIdInput) (*InstanceInfo, error) { func (c *InstancesClient) GetInstanceFromName(input *GetInstanceIDInput) (*InstanceInfo, error) {
input.Name = c.getQualifiedName(input.Name) input.Name = c.getQualifiedName(input.Name)
var instancesInfo InstancesInfo var instancesInfo InstancesInfo
@ -462,14 +496,14 @@ func (c *InstancesClient) GetInstanceFromName(input *GetInstanceIdInput) (*Insta
} }
for _, i := range instancesInfo.Instances { for _, i := range instancesInfo.Instances {
if strings.Contains(i.Name, input.Name) { if strings.Contains(i.FQDN, input.Name) {
if i.Name == "" { if i.Name == "" {
return nil, fmt.Errorf("Empty response body when requesting instance %s", input.Name) return nil, fmt.Errorf("Empty response body when requesting instance %s", input.Name)
} }
// The returned 'Name' attribute is the fully qualified instance name + "/" + ID // The returned 'Name' attribute is the fully qualified instance name + "/" + ID
// Split these out to accurately populate the fields // Split these out to accurately populate the fields
nID := strings.Split(c.getUnqualifiedName(i.Name), "/") nID := strings.Split(c.getUnqualifiedName(i.FQDN), "/")
i.Name = nID[0] i.Name = nID[0]
i.ID = nID[1] i.ID = nID[1]
@ -496,6 +530,7 @@ func (c *InstancesClient) GetInstanceFromName(input *GetInstanceIdInput) (*Insta
return nil, fmt.Errorf("Unable to find instance: %q", input.Name) return nil, fmt.Errorf("Unable to find instance: %q", input.Name)
} }
// UpdateInstanceInput specifies the parameters needed to update an instance
type UpdateInstanceInput struct { type UpdateInstanceInput struct {
// Name of this instance, generated by the server. // Name of this instance, generated by the server.
// Required // Required
@ -510,20 +545,23 @@ type UpdateInstanceInput struct {
// A list of tags to be supplied to the instance // A list of tags to be supplied to the instance
// Optional // Optional
Tags []string `json:"tags,omitempty"` Tags []string `json:"tags,omitempty"`
// Time to wait between polls for instance state
PollInterval time.Duration `json:"-"`
// Time to wait for instance to be ready, or shutdown depending on desired state // Time to wait for instance to be ready, or shutdown depending on desired state
Timeout time.Duration `json:"-"` Timeout time.Duration `json:"-"`
} }
func (g *UpdateInstanceInput) String() string { func (g *UpdateInstanceInput) String() string {
return fmt.Sprintf(CMP_QUALIFIED_NAME, g.Name, g.ID) return fmt.Sprintf(cmpQualifiedName, g.Name, g.ID)
} }
// UpdateInstance updates an instance with the specified attributes
func (c *InstancesClient) UpdateInstance(input *UpdateInstanceInput) (*InstanceInfo, error) { func (c *InstancesClient) UpdateInstance(input *UpdateInstanceInput) (*InstanceInfo, error) {
if input.Name == "" || input.ID == "" { if input.Name == "" || input.ID == "" {
return nil, errors.New("Both instance name and ID need to be specified") return nil, errors.New("Both instance name and ID need to be specified")
} }
input.Name = fmt.Sprintf(CMP_QUALIFIED_NAME, c.getUserName(), input.Name) input.Name = fmt.Sprintf(cmpQualifiedName, c.getUserName(), input.Name)
var responseBody InstanceInfo var responseBody InstanceInfo
if err := c.updateResource(input.String(), input, &responseBody); err != nil { if err := c.updateResource(input.String(), input, &responseBody); err != nil {
@ -535,8 +573,12 @@ func (c *InstancesClient) UpdateInstance(input *UpdateInstanceInput) (*InstanceI
ID: input.ID, ID: input.ID,
} }
if input.PollInterval == 0 {
input.PollInterval = waitForInstanceReadyPollInterval
}
if input.Timeout == 0 { if input.Timeout == 0 {
input.Timeout = WaitForInstanceReadyTimeout input.Timeout = waitForInstanceReadyTimeout
} }
// Wait for the correct instance action depending on the current desired state. // Wait for the correct instance action depending on the current desired state.
@ -545,23 +587,25 @@ func (c *InstancesClient) UpdateInstance(input *UpdateInstanceInput) (*InstanceI
// we wait until the correct action has finalized, either a shutdown or restart, catching // we wait until the correct action has finalized, either a shutdown or restart, catching
// any intermittent errors during the process. // any intermittent errors during the process.
if responseBody.DesiredState == InstanceDesiredRunning { if responseBody.DesiredState == InstanceDesiredRunning {
return c.WaitForInstanceRunning(getInput, input.Timeout) return c.WaitForInstanceRunning(getInput, input.PollInterval, input.Timeout)
} else {
return c.WaitForInstanceShutdown(getInput, input.Timeout)
} }
return c.WaitForInstanceShutdown(getInput, input.PollInterval, input.Timeout)
} }
// DeleteInstanceInput specifies the parameters needed to delete an instance
type DeleteInstanceInput struct { type DeleteInstanceInput struct {
// The Unqualified Name of this Instance // The Unqualified Name of this Instance
Name string Name string
// The Unqualified ID of this Instance // The Unqualified ID of this Instance
ID string ID string
// Time to wait between polls to check status
PollInterval time.Duration
// Time to wait for instance to be deleted // Time to wait for instance to be deleted
Timeout time.Duration Timeout time.Duration
} }
func (d *DeleteInstanceInput) String() string { func (d *DeleteInstanceInput) String() string {
return fmt.Sprintf(CMP_QUALIFIED_NAME, d.Name, d.ID) return fmt.Sprintf(cmpQualifiedName, d.Name, d.ID)
} }
// DeleteInstance deletes an instance. // DeleteInstance deletes an instance.
@ -571,19 +615,22 @@ func (c *InstancesClient) DeleteInstance(input *DeleteInstanceInput) error {
return err return err
} }
if input.PollInterval == 0 {
input.PollInterval = waitForInstanceDeletePollInterval
}
if input.Timeout == 0 { if input.Timeout == 0 {
input.Timeout = WaitForInstanceDeleteTimeout input.Timeout = waitForInstanceDeleteTimeout
} }
// Wait for instance to be deleted // Wait for instance to be deleted
return c.WaitForInstanceDeleted(input, input.Timeout) return c.WaitForInstanceDeleted(input, input.PollInterval, input.Timeout)
} }
// WaitForInstanceRunning waits for an instance to be completely initialized and available. // WaitForInstanceRunning waits for an instance to be completely initialized and available.
func (c *InstancesClient) WaitForInstanceRunning(input *GetInstanceInput, timeout time.Duration) (*InstanceInfo, error) { func (c *InstancesClient) WaitForInstanceRunning(input *GetInstanceInput, pollInterval, timeout time.Duration) (*InstanceInfo, error) {
var info *InstanceInfo var info *InstanceInfo
var getErr error var getErr error
err := c.client.WaitFor("instance to be ready", timeout, func() (bool, error) { err := c.client.WaitFor("instance to be ready", pollInterval, timeout, func() (bool, error) {
info, getErr = c.GetInstance(input) info, getErr = c.GetInstance(input)
if getErr != nil { if getErr != nil {
return false, getErr return false, getErr
@ -616,10 +663,10 @@ func (c *InstancesClient) WaitForInstanceRunning(input *GetInstanceInput, timeou
} }
// WaitForInstanceShutdown waits for an instance to be shutdown // WaitForInstanceShutdown waits for an instance to be shutdown
func (c *InstancesClient) WaitForInstanceShutdown(input *GetInstanceInput, timeout time.Duration) (*InstanceInfo, error) { func (c *InstancesClient) WaitForInstanceShutdown(input *GetInstanceInput, pollInterval, timeout time.Duration) (*InstanceInfo, error) {
var info *InstanceInfo var info *InstanceInfo
var getErr error var getErr error
err := c.client.WaitFor("instance to be shutdown", timeout, func() (bool, error) { err := c.client.WaitFor("instance to be shutdown", pollInterval, timeout, func() (bool, error) {
info, getErr = c.GetInstance(input) info, getErr = c.GetInstance(input)
if getErr != nil { if getErr != nil {
return false, getErr return false, getErr
@ -654,8 +701,8 @@ func (c *InstancesClient) WaitForInstanceShutdown(input *GetInstanceInput, timeo
} }
// WaitForInstanceDeleted waits for an instance to be fully deleted. // WaitForInstanceDeleted waits for an instance to be fully deleted.
func (c *InstancesClient) WaitForInstanceDeleted(input *DeleteInstanceInput, timeout time.Duration) error { func (c *InstancesClient) WaitForInstanceDeleted(input fmt.Stringer, pollInterval, timeout time.Duration) error {
return c.client.WaitFor("instance to be deleted", timeout, func() (bool, error) { return c.client.WaitFor("instance to be deleted", pollInterval, timeout, func() (bool, error) {
var info InstanceInfo var info InstanceInfo
if err := c.getResource(input.String(), &info); err != nil { if err := c.getResource(input.String(), &info); err != nil {
if client.WasNotFoundError(err) { if client.WasNotFoundError(err) {
@ -699,13 +746,8 @@ func (c *InstancesClient) qualifyNetworking(info map[string]NetworkingInfo) map[
} }
if v.SecLists != nil { if v.SecLists != nil {
// Network interface is for the shared network // Network interface is for the shared network
secLists := []string{} qfd.SecLists = c.getQualifiedList(v.SecLists)
for _, v := range v.SecLists {
secLists = append(secLists, c.getQualifiedName(v))
}
qfd.SecLists = secLists
} }
qualifiedNetworks[k] = qfd qualifiedNetworks[k] = qfd
} }
return qualifiedNetworks return qualifiedNetworks
@ -733,11 +775,7 @@ func (c *InstancesClient) unqualifyNetworking(info map[string]NetworkingInfo) (m
unq.VnicSets = c.getUnqualifiedList(v.VnicSets) unq.VnicSets = c.getUnqualifiedList(v.VnicSets)
} }
if v.SecLists != nil { if v.SecLists != nil {
secLists := []string{} unq.SecLists = c.getUnqualifiedList(v.SecLists)
for _, v := range v.SecLists {
secLists = append(secLists, c.getUnqualifiedName(v))
}
v.SecLists = secLists
} }
unqualifiedNetworks[k] = unq unqualifiedNetworks[k] = unq
} }

View File

@ -1,24 +1,25 @@
package compute package compute
const ( const (
IPAddressAssociationDescription = "ip address association" iPAddressAssociationDescription = "ip address association"
IPAddressAssociationContainerPath = "/network/v1/ipassociation/" iPAddressAssociationContainerPath = "/network/v1/ipassociation/"
IPAddressAssociationResourcePath = "/network/v1/ipassociation" iPAddressAssociationResourcePath = "/network/v1/ipassociation"
) )
// IPAddressAssociationsClient details the parameters for an ip address association client
type IPAddressAssociationsClient struct { type IPAddressAssociationsClient struct {
ResourceClient ResourceClient
} }
// IPAddressAssociations() returns an IPAddressAssociationsClient that can be used to access the // IPAddressAssociations returns an IPAddressAssociationsClient that can be used to access the
// necessary CRUD functions for IP Address Associations. // necessary CRUD functions for IP Address Associations.
func (c *ComputeClient) IPAddressAssociations() *IPAddressAssociationsClient { func (c *Client) IPAddressAssociations() *IPAddressAssociationsClient {
return &IPAddressAssociationsClient{ return &IPAddressAssociationsClient{
ResourceClient: ResourceClient{ ResourceClient: ResourceClient{
ComputeClient: c, Client: c,
ResourceDescription: IPAddressAssociationDescription, ResourceDescription: iPAddressAssociationDescription,
ContainerPath: IPAddressAssociationContainerPath, ContainerPath: iPAddressAssociationContainerPath,
ResourceRootPath: IPAddressAssociationResourcePath, ResourceRootPath: iPAddressAssociationResourcePath,
}, },
} }
} }
@ -26,20 +27,23 @@ func (c *ComputeClient) IPAddressAssociations() *IPAddressAssociationsClient {
// IPAddressAssociationInfo contains the exported fields necessary to hold all the information about an // IPAddressAssociationInfo contains the exported fields necessary to hold all the information about an
// IP Address Association // IP Address Association
type IPAddressAssociationInfo struct { type IPAddressAssociationInfo struct {
// Fully Qualified Domain Name
FQDN string `json:"name"`
// The name of the NAT IP address reservation. // The name of the NAT IP address reservation.
IPAddressReservation string `json:"ipAddressReservation"` IPAddressReservation string `json:"ipAddressReservation"`
// Name of the virtual NIC associated with this NAT IP reservation. // Name of the virtual NIC associated with this NAT IP reservation.
Vnic string `json:"vnic"` Vnic string `json:"vnic"`
// The name of the IP Address Association // The name of the IP Address Association
Name string `json:"name"` Name string
// Description of the IP Address Association // Description of the IP Address Association
Description string `json:"description"` Description string `json:"description"`
// Slice of tags associated with the IP Address Association // Slice of tags associated with the IP Address Association
Tags []string `json:"tags"` Tags []string `json:"tags"`
// Uniform Resource Identifier for the IP Address Association // Uniform Resource Identifier for the IP Address Association
Uri string `json:"uri"` URI string `json:"uri"`
} }
// CreateIPAddressAssociationInput details the attributes needed to create an ip address association
type CreateIPAddressAssociationInput struct { type CreateIPAddressAssociationInput struct {
// The name of the IP Address Association to create. Object names can only contain alphanumeric, // The name of the IP Address Association to create. Object names can only contain alphanumeric,
// underscore, dash, and period characters. Names are case-sensitive. // underscore, dash, and period characters. Names are case-sensitive.
@ -63,7 +67,7 @@ type CreateIPAddressAssociationInput struct {
Tags []string `json:"tags"` Tags []string `json:"tags"`
} }
// Create a new IP Address Association from an IPAddressAssociationsClient and an input struct. // CreateIPAddressAssociation creates a new IP Address Association from an IPAddressAssociationsClient and an input struct.
// Returns a populated Info struct for the IP Address Association, and any errors // Returns a populated Info struct for the IP Address Association, and any errors
func (c *IPAddressAssociationsClient) CreateIPAddressAssociation(input *CreateIPAddressAssociationInput) (*IPAddressAssociationInfo, error) { func (c *IPAddressAssociationsClient) CreateIPAddressAssociation(input *CreateIPAddressAssociationInput) (*IPAddressAssociationInfo, error) {
input.Name = c.getQualifiedName(input.Name) input.Name = c.getQualifiedName(input.Name)
@ -78,13 +82,14 @@ func (c *IPAddressAssociationsClient) CreateIPAddressAssociation(input *CreateIP
return c.success(&ipInfo) return c.success(&ipInfo)
} }
// GetIPAddressAssociationInput details the parameters needed to retrieve an ip address association
type GetIPAddressAssociationInput struct { type GetIPAddressAssociationInput struct {
// The name of the IP Address Association to query for. Case-sensitive // The name of the IP Address Association to query for. Case-sensitive
// Required // Required
Name string `json:"name"` Name string `json:"name"`
} }
// Returns a populated IPAddressAssociationInfo struct from an input struct // GetIPAddressAssociation returns a populated IPAddressAssociationInfo struct from an input struct
func (c *IPAddressAssociationsClient) GetIPAddressAssociation(input *GetIPAddressAssociationInput) (*IPAddressAssociationInfo, error) { func (c *IPAddressAssociationsClient) GetIPAddressAssociation(input *GetIPAddressAssociationInput) (*IPAddressAssociationInfo, error) {
input.Name = c.getQualifiedName(input.Name) input.Name = c.getQualifiedName(input.Name)
@ -96,56 +101,21 @@ func (c *IPAddressAssociationsClient) GetIPAddressAssociation(input *GetIPAddres
return c.success(&ipInfo) return c.success(&ipInfo)
} }
// UpdateIPAddressAssociationInput defines what to update in a ip address association // DeleteIPAddressAssociationInput details the parameters neccessary to delete an ip address association
type UpdateIPAddressAssociationInput struct {
// The name of the IP Address Association to create. Object names can only contain alphanumeric,
// underscore, dash, and period characters. Names are case-sensitive.
// Required
Name string `json:"name"`
// The name of the NAT IP address reservation.
// Optional
IPAddressReservation string `json:"ipAddressReservation,omitempty"`
// Name of the virtual NIC associated with this NAT IP reservation.
// Optional
Vnic string `json:"vnic,omitempty"`
// Description of the IPAddressAssociation
// Optional
Description string `json:"description"`
// String slice of tags to apply to the IP Address Association object
// Optional
Tags []string `json:"tags"`
}
// UpdateIPAddressAssociation update the ip address association
func (c *IPAddressAssociationsClient) UpdateIPAddressAssociation(updateInput *UpdateIPAddressAssociationInput) (*IPAddressAssociationInfo, error) {
updateInput.Name = c.getQualifiedName(updateInput.Name)
updateInput.IPAddressReservation = c.getQualifiedName(updateInput.IPAddressReservation)
updateInput.Vnic = c.getQualifiedName(updateInput.Vnic)
var ipInfo IPAddressAssociationInfo
if err := c.updateResource(updateInput.Name, updateInput, &ipInfo); err != nil {
return nil, err
}
return c.success(&ipInfo)
}
type DeleteIPAddressAssociationInput struct { type DeleteIPAddressAssociationInput struct {
// The name of the IP Address Association to query for. Case-sensitive // The name of the IP Address Association to query for. Case-sensitive
// Required // Required
Name string `json:"name"` Name string `json:"name"`
} }
// DeleteIPAddressAssociation deletes the specified ip address association
func (c *IPAddressAssociationsClient) DeleteIPAddressAssociation(input *DeleteIPAddressAssociationInput) error { func (c *IPAddressAssociationsClient) DeleteIPAddressAssociation(input *DeleteIPAddressAssociationInput) error {
return c.deleteResource(input.Name) return c.deleteResource(input.Name)
} }
// Unqualifies any qualified fields in the IPAddressAssociationInfo struct // Unqualifies any qualified fields in the IPAddressAssociationInfo struct
func (c *IPAddressAssociationsClient) success(info *IPAddressAssociationInfo) (*IPAddressAssociationInfo, error) { func (c *IPAddressAssociationsClient) success(info *IPAddressAssociationInfo) (*IPAddressAssociationInfo, error) {
c.unqualify(&info.Name) info.Name = c.getUnqualifiedName(info.FQDN)
c.unqualify(&info.Vnic) c.unqualify(&info.Vnic)
c.unqualify(&info.IPAddressReservation) c.unqualify(&info.IPAddressReservation)
return info, nil return info, nil

View File

@ -1,24 +1,25 @@
package compute package compute
const ( const (
IPAddressPrefixSetDescription = "ip address prefix set" iPAddressPrefixSetDescription = "ip address prefix set"
IPAddressPrefixSetContainerPath = "/network/v1/ipaddressprefixset/" iPAddressPrefixSetContainerPath = "/network/v1/ipaddressprefixset/"
IPAddressPrefixSetResourcePath = "/network/v1/ipaddressprefixset" iPAddressPrefixSetResourcePath = "/network/v1/ipaddressprefixset"
) )
// IPAddressPrefixSetsClient details the parameters for an ipaddress prefix set client
type IPAddressPrefixSetsClient struct { type IPAddressPrefixSetsClient struct {
ResourceClient ResourceClient
} }
// IPAddressPrefixSets() returns an IPAddressPrefixSetsClient that can be used to access the // IPAddressPrefixSets returns an IPAddressPrefixSetsClient that can be used to access the
// necessary CRUD functions for IP Address Prefix Sets. // necessary CRUD functions for IP Address Prefix Sets.
func (c *ComputeClient) IPAddressPrefixSets() *IPAddressPrefixSetsClient { func (c *Client) IPAddressPrefixSets() *IPAddressPrefixSetsClient {
return &IPAddressPrefixSetsClient{ return &IPAddressPrefixSetsClient{
ResourceClient: ResourceClient{ ResourceClient: ResourceClient{
ComputeClient: c, Client: c,
ResourceDescription: IPAddressPrefixSetDescription, ResourceDescription: iPAddressPrefixSetDescription,
ContainerPath: IPAddressPrefixSetContainerPath, ContainerPath: iPAddressPrefixSetContainerPath,
ResourceRootPath: IPAddressPrefixSetResourcePath, ResourceRootPath: iPAddressPrefixSetResourcePath,
}, },
} }
} }
@ -26,8 +27,10 @@ func (c *ComputeClient) IPAddressPrefixSets() *IPAddressPrefixSetsClient {
// IPAddressPrefixSetInfo contains the exported fields necessary to hold all the information about an // IPAddressPrefixSetInfo contains the exported fields necessary to hold all the information about an
// IP Address Prefix Set // IP Address Prefix Set
type IPAddressPrefixSetInfo struct { type IPAddressPrefixSetInfo struct {
// Fully Qualified Domain Name
FQDN string `json:"name"`
// The name of the IP Address Prefix Set // The name of the IP Address Prefix Set
Name string `json:"name"` Name string
// Description of the IP Address Prefix Set // Description of the IP Address Prefix Set
Description string `json:"description"` Description string `json:"description"`
// List of CIDR IPv4 prefixes assigned in the virtual network. // List of CIDR IPv4 prefixes assigned in the virtual network.
@ -35,9 +38,10 @@ type IPAddressPrefixSetInfo struct {
// Slice of tags associated with the IP Address Prefix Set // Slice of tags associated with the IP Address Prefix Set
Tags []string `json:"tags"` Tags []string `json:"tags"`
// Uniform Resource Identifier for the IP Address Prefix Set // Uniform Resource Identifier for the IP Address Prefix Set
Uri string `json:"uri"` URI string `json:"uri"`
} }
// CreateIPAddressPrefixSetInput details the parameters to create an ip address prefix set
type CreateIPAddressPrefixSetInput struct { type CreateIPAddressPrefixSetInput struct {
// The name of the IP Address Prefix Set to create. Object names can only contain alphanumeric, // The name of the IP Address Prefix Set to create. Object names can only contain alphanumeric,
// underscore, dash, and period characters. Names are case-sensitive. // underscore, dash, and period characters. Names are case-sensitive.
@ -57,7 +61,7 @@ type CreateIPAddressPrefixSetInput struct {
Tags []string `json:"tags"` Tags []string `json:"tags"`
} }
// Create a new IP Address Prefix Set from an IPAddressPrefixSetsClient and an input struct. // CreateIPAddressPrefixSet creates a new IP Address Prefix Set from an IPAddressPrefixSetsClient and an input struct.
// Returns a populated Info struct for the IP Address Prefix Set, and any errors // Returns a populated Info struct for the IP Address Prefix Set, and any errors
func (c *IPAddressPrefixSetsClient) CreateIPAddressPrefixSet(input *CreateIPAddressPrefixSetInput) (*IPAddressPrefixSetInfo, error) { func (c *IPAddressPrefixSetsClient) CreateIPAddressPrefixSet(input *CreateIPAddressPrefixSetInput) (*IPAddressPrefixSetInfo, error) {
input.Name = c.getQualifiedName(input.Name) input.Name = c.getQualifiedName(input.Name)
@ -70,13 +74,14 @@ func (c *IPAddressPrefixSetsClient) CreateIPAddressPrefixSet(input *CreateIPAddr
return c.success(&ipInfo) return c.success(&ipInfo)
} }
// GetIPAddressPrefixSetInput details the parameters to retrieve an ip address prefix set
type GetIPAddressPrefixSetInput struct { type GetIPAddressPrefixSetInput struct {
// The name of the IP Address Prefix Set to query for. Case-sensitive // The name of the IP Address Prefix Set to query for. Case-sensitive
// Required // Required
Name string `json:"name"` Name string `json:"name"`
} }
// Returns a populated IPAddressPrefixSetInfo struct from an input struct // GetIPAddressPrefixSet returns a populated IPAddressPrefixSetInfo struct from an input struct
func (c *IPAddressPrefixSetsClient) GetIPAddressPrefixSet(input *GetIPAddressPrefixSetInput) (*IPAddressPrefixSetInfo, error) { func (c *IPAddressPrefixSetsClient) GetIPAddressPrefixSet(input *GetIPAddressPrefixSetInput) (*IPAddressPrefixSetInfo, error) {
input.Name = c.getQualifiedName(input.Name) input.Name = c.getQualifiedName(input.Name)
@ -118,18 +123,20 @@ func (c *IPAddressPrefixSetsClient) UpdateIPAddressPrefixSet(updateInput *Update
return c.success(&ipInfo) return c.success(&ipInfo)
} }
// DeleteIPAddressPrefixSetInput details the parameters to delete an ip address prefix set
type DeleteIPAddressPrefixSetInput struct { type DeleteIPAddressPrefixSetInput struct {
// The name of the IP Address Prefix Set to query for. Case-sensitive // The name of the IP Address Prefix Set to query for. Case-sensitive
// Required // Required
Name string `json:"name"` Name string `json:"name"`
} }
// DeleteIPAddressPrefixSet deletes the specified ip address prefix set
func (c *IPAddressPrefixSetsClient) DeleteIPAddressPrefixSet(input *DeleteIPAddressPrefixSetInput) error { func (c *IPAddressPrefixSetsClient) DeleteIPAddressPrefixSet(input *DeleteIPAddressPrefixSetInput) error {
return c.deleteResource(input.Name) return c.deleteResource(input.Name)
} }
// Unqualifies any qualified fields in the IPAddressPrefixSetInfo struct // Unqualifies any qualified fields in the IPAddressPrefixSetInfo struct
func (c *IPAddressPrefixSetsClient) success(info *IPAddressPrefixSetInfo) (*IPAddressPrefixSetInfo, error) { func (c *IPAddressPrefixSetsClient) success(info *IPAddressPrefixSetInfo) (*IPAddressPrefixSetInfo, error) {
c.unqualify(&info.Name) info.Name = c.getUnqualifiedName(info.FQDN)
return info, nil return info, nil
} }

View File

@ -11,21 +11,21 @@ type IPAddressReservationsClient struct {
} }
const ( const (
IPAddressReservationDescription = "IP Address Reservation" iPAddressReservationDescription = "IP Address Reservation"
IPAddressReservationContainerPath = "/network/v1/ipreservation/" iPAddressReservationContainerPath = "/network/v1/ipreservation/"
IPAddressReservationResourcePath = "/network/v1/ipreservation" iPAddressReservationResourcePath = "/network/v1/ipreservation"
IPAddressReservationQualifier = "/oracle/public" iPAddressReservationQualifier = "/oracle/public"
) )
// IPAddressReservations returns an IPAddressReservationsClient to manage IP address reservation // IPAddressReservations returns an IPAddressReservationsClient to manage IP address reservation
// resources // resources
func (c *ComputeClient) IPAddressReservations() *IPAddressReservationsClient { func (c *Client) IPAddressReservations() *IPAddressReservationsClient {
return &IPAddressReservationsClient{ return &IPAddressReservationsClient{
ResourceClient: &ResourceClient{ ResourceClient: &ResourceClient{
ComputeClient: c, Client: c,
ResourceDescription: IPAddressReservationDescription, ResourceDescription: iPAddressReservationDescription,
ContainerPath: IPAddressReservationContainerPath, ContainerPath: iPAddressReservationContainerPath,
ResourceRootPath: IPAddressReservationResourcePath, ResourceRootPath: iPAddressReservationResourcePath,
}, },
} }
} }
@ -35,6 +35,9 @@ type IPAddressReservation struct {
// Description of the IP Address Reservation // Description of the IP Address Reservation
Description string `json:"description"` Description string `json:"description"`
// Fully Qualified Domain Name
FQDN string `json:"name"`
// Reserved NAT IPv4 address from the IP Address Pool // Reserved NAT IPv4 address from the IP Address Pool
IPAddress string `json:"ipAddress"` IPAddress string `json:"ipAddress"`
@ -42,17 +45,19 @@ type IPAddressReservation struct {
IPAddressPool string `json:"ipAddressPool"` IPAddressPool string `json:"ipAddressPool"`
// Name of the reservation // Name of the reservation
Name string `json:"name"` Name string
// Tags associated with the object // Tags associated with the object
Tags []string `json:"tags"` Tags []string `json:"tags"`
// Uniform Resource Identified for the reservation // Uniform Resource Identified for the reservation
Uri string `json:"uri"` URI string `json:"uri"`
} }
const ( const (
PublicIPAddressPool = "public-ippool" // PublicIPAddressPool - public-ippool
PublicIPAddressPool = "public-ippool"
// PrivateIPAddressPool - cloud-ippool
PrivateIPAddressPool = "cloud-ippool" PrivateIPAddressPool = "cloud-ippool"
) )
@ -81,7 +86,7 @@ type CreateIPAddressReservationInput struct {
Tags []string `json:"tags"` Tags []string `json:"tags"`
} }
// Takes an input struct, creates an IP Address reservation, and returns the info struct and any errors // CreateIPAddressReservation creates an IP Address reservation, and returns the info struct and any errors
func (c *IPAddressReservationsClient) CreateIPAddressReservation(input *CreateIPAddressReservationInput) (*IPAddressReservation, error) { func (c *IPAddressReservationsClient) CreateIPAddressReservation(input *CreateIPAddressReservationInput) (*IPAddressReservation, error) {
var ipAddrRes IPAddressReservation var ipAddrRes IPAddressReservation
// Qualify supplied name // Qualify supplied name
@ -98,14 +103,14 @@ func (c *IPAddressReservationsClient) CreateIPAddressReservation(input *CreateIP
return c.success(&ipAddrRes) return c.success(&ipAddrRes)
} }
// Parameters to retrieve information on an ip address reservation // GetIPAddressReservationInput details the parameters to retrieve information on an ip address reservation
type GetIPAddressReservationInput struct { type GetIPAddressReservationInput struct {
// Name of the IP Reservation // Name of the IP Reservation
// Required // Required
Name string `json:"name"` Name string `json:"name"`
} }
// Returns an IP Address Reservation and any errors // GetIPAddressReservation returns an IP Address Reservation and any errors
func (c *IPAddressReservationsClient) GetIPAddressReservation(input *GetIPAddressReservationInput) (*IPAddressReservation, error) { func (c *IPAddressReservationsClient) GetIPAddressReservation(input *GetIPAddressReservationInput) (*IPAddressReservation, error) {
var ipAddrRes IPAddressReservation var ipAddrRes IPAddressReservation
@ -117,7 +122,7 @@ func (c *IPAddressReservationsClient) GetIPAddressReservation(input *GetIPAddres
return c.success(&ipAddrRes) return c.success(&ipAddrRes)
} }
// Parameters to update an IP Address reservation // UpdateIPAddressReservationInput details the parameters to update an IP Address reservation
type UpdateIPAddressReservationInput struct { type UpdateIPAddressReservationInput struct {
// Description of the IP Address Reservation // Description of the IP Address Reservation
// Optional // Optional
@ -142,6 +147,7 @@ type UpdateIPAddressReservationInput struct {
Tags []string `json:"tags"` Tags []string `json:"tags"`
} }
// UpdateIPAddressReservation updates the specified ip address reservation
func (c *IPAddressReservationsClient) UpdateIPAddressReservation(input *UpdateIPAddressReservationInput) (*IPAddressReservation, error) { func (c *IPAddressReservationsClient) UpdateIPAddressReservation(input *UpdateIPAddressReservationInput) (*IPAddressReservation, error) {
var ipAddrRes IPAddressReservation var ipAddrRes IPAddressReservation
@ -159,19 +165,20 @@ func (c *IPAddressReservationsClient) UpdateIPAddressReservation(input *UpdateIP
return c.success(&ipAddrRes) return c.success(&ipAddrRes)
} }
// Parameters to delete an IP Address Reservation // DeleteIPAddressReservationInput details the parameters to delete an IP Address Reservation
type DeleteIPAddressReservationInput struct { type DeleteIPAddressReservationInput struct {
// The name of the reservation to delete // The name of the reservation to delete
Name string `json:"name"` Name string `json:"name"`
} }
// DeleteIPAddressReservation deletes the specified ip address reservation
func (c *IPAddressReservationsClient) DeleteIPAddressReservation(input *DeleteIPAddressReservationInput) error { func (c *IPAddressReservationsClient) DeleteIPAddressReservation(input *DeleteIPAddressReservationInput) error {
input.Name = c.getQualifiedName(input.Name) input.Name = c.getQualifiedName(input.Name)
return c.deleteResource(input.Name) return c.deleteResource(input.Name)
} }
func (c *IPAddressReservationsClient) success(result *IPAddressReservation) (*IPAddressReservation, error) { func (c *IPAddressReservationsClient) success(result *IPAddressReservation) (*IPAddressReservation, error) {
c.unqualify(&result.Name) result.Name = c.getUnqualifiedName(result.FQDN)
if result.IPAddressPool != "" { if result.IPAddressPool != "" {
result.IPAddressPool = c.unqualifyIPAddressPool(result.IPAddressPool) result.IPAddressPool = c.unqualifyIPAddressPool(result.IPAddressPool)
} }
@ -181,7 +188,7 @@ func (c *IPAddressReservationsClient) success(result *IPAddressReservation) (*IP
func (c *IPAddressReservationsClient) qualifyIPAddressPool(input string) string { func (c *IPAddressReservationsClient) qualifyIPAddressPool(input string) string {
// Add '/oracle/public/' // Add '/oracle/public/'
return fmt.Sprintf("%s/%s", IPAddressReservationQualifier, input) return fmt.Sprintf("%s/%s", iPAddressReservationQualifier, input)
} }
func (c *IPAddressReservationsClient) unqualifyIPAddressPool(input string) string { func (c *IPAddressReservationsClient) unqualifyIPAddressPool(input string) string {

View File

@ -12,10 +12,10 @@ type IPAssociationsClient struct {
// IPAssociations obtains a IPAssociationsClient which can be used to access to the // IPAssociations obtains a IPAssociationsClient which can be used to access to the
// IP Association functions of the Compute API // IP Association functions of the Compute API
func (c *ComputeClient) IPAssociations() *IPAssociationsClient { func (c *Client) IPAssociations() *IPAssociationsClient {
return &IPAssociationsClient{ return &IPAssociationsClient{
ResourceClient: &ResourceClient{ ResourceClient: &ResourceClient{
ComputeClient: c, Client: c,
ResourceDescription: "ip association", ResourceDescription: "ip association",
ContainerPath: "/ip/association/", ContainerPath: "/ip/association/",
ResourceRootPath: "/ip/association", ResourceRootPath: "/ip/association",
@ -26,8 +26,11 @@ func (c *ComputeClient) IPAssociations() *IPAssociationsClient {
type IPAssociationInfo struct { type IPAssociationInfo struct {
// TODO: it'd probably make sense to expose the `ip` field here too? // TODO: it'd probably make sense to expose the `ip` field here too?
// Fully Qualified Domain Name
FQDN string `json:"name"`
// The three-part name of the object (/Compute-identity_domain/user/object). // The three-part name of the object (/Compute-identity_domain/user/object).
Name string `json:"name"` Name string
// The three-part name of the IP reservation object in the format (/Compute-identity_domain/user/object). // The three-part name of the IP reservation object in the format (/Compute-identity_domain/user/object).
// An IP reservation is a public IP address which is attached to an Oracle Compute Cloud Service instance that requires access to or from the Internet. // An IP reservation is a public IP address which is attached to an Oracle Compute Cloud Service instance that requires access to or from the Internet.
@ -45,6 +48,7 @@ type IPAssociationInfo struct {
VCable string `json:"vcable"` VCable string `json:"vcable"`
} }
// CreateIPAssociationInput details the attributes neccessary to create an ip association
type CreateIPAssociationInput struct { type CreateIPAssociationInput struct {
// The type of IP Address to associate with this instance // The type of IP Address to associate with this instance
// for a Dynamic IP address specify `ippool:/oracle/public/ippool`. // for a Dynamic IP address specify `ippool:/oracle/public/ippool`.
@ -69,6 +73,7 @@ func (c *IPAssociationsClient) CreateIPAssociation(input *CreateIPAssociationInp
return c.success(&assocInfo) return c.success(&assocInfo)
} }
// GetIPAssociationInput details the attributes neccessary to retrieve an ip association
type GetIPAssociationInput struct { type GetIPAssociationInput struct {
// The three-part name of the IP Association // The three-part name of the IP Association
// Required. // Required.
@ -85,6 +90,7 @@ func (c *IPAssociationsClient) GetIPAssociation(input *GetIPAssociationInput) (*
return c.success(&assocInfo) return c.success(&assocInfo)
} }
// DeleteIPAssociationInput details the attributes neccessary to delete an ip association
type DeleteIPAssociationInput struct { type DeleteIPAssociationInput struct {
// The three-part name of the IP Association // The three-part name of the IP Association
// Required. // Required.
@ -112,7 +118,8 @@ func (c *IPAssociationsClient) unqualifyParentPoolName(parentpool *string) {
// Unqualifies identifiers // Unqualifies identifiers
func (c *IPAssociationsClient) success(assocInfo *IPAssociationInfo) (*IPAssociationInfo, error) { func (c *IPAssociationsClient) success(assocInfo *IPAssociationInfo) (*IPAssociationInfo, error) {
c.unqualify(&assocInfo.Name, &assocInfo.VCable) assocInfo.Name = c.getUnqualifiedName(assocInfo.FQDN)
c.unqualify(&assocInfo.VCable)
c.unqualifyParentPoolName(&assocInfo.ParentPool) c.unqualifyParentPoolName(&assocInfo.ParentPool)
return assocInfo, nil return assocInfo, nil
} }

View File

@ -1,24 +1,25 @@
package compute package compute
const ( const (
IPNetworkExchangeDescription = "ip network exchange" iPNetworkExchangeDescription = "ip network exchange"
IPNetworkExchangeContainerPath = "/network/v1/ipnetworkexchange/" iPNetworkExchangeContainerPath = "/network/v1/ipnetworkexchange/"
IPNetworkExchangeResourcePath = "/network/v1/ipnetworkexchange" iPNetworkExchangeResourcePath = "/network/v1/ipnetworkexchange"
) )
// IPNetworkExchangesClient details the ip network exchange client
type IPNetworkExchangesClient struct { type IPNetworkExchangesClient struct {
ResourceClient ResourceClient
} }
// IPNetworkExchanges() returns an IPNetworkExchangesClient that can be used to access the // IPNetworkExchanges returns an IPNetworkExchangesClient that can be used to access the
// necessary CRUD functions for IP Network Exchanges. // necessary CRUD functions for IP Network Exchanges.
func (c *ComputeClient) IPNetworkExchanges() *IPNetworkExchangesClient { func (c *Client) IPNetworkExchanges() *IPNetworkExchangesClient {
return &IPNetworkExchangesClient{ return &IPNetworkExchangesClient{
ResourceClient: ResourceClient{ ResourceClient: ResourceClient{
ComputeClient: c, Client: c,
ResourceDescription: IPNetworkExchangeDescription, ResourceDescription: iPNetworkExchangeDescription,
ContainerPath: IPNetworkExchangeContainerPath, ContainerPath: iPNetworkExchangeContainerPath,
ResourceRootPath: IPNetworkExchangeResourcePath, ResourceRootPath: iPNetworkExchangeResourcePath,
}, },
} }
} }
@ -26,16 +27,19 @@ func (c *ComputeClient) IPNetworkExchanges() *IPNetworkExchangesClient {
// IPNetworkExchangeInfo contains the exported fields necessary to hold all the information about an // IPNetworkExchangeInfo contains the exported fields necessary to hold all the information about an
// IP Network Exchange // IP Network Exchange
type IPNetworkExchangeInfo struct { type IPNetworkExchangeInfo struct {
// Fully Qualified Domain Name
FQDN string `json:"name"`
// The name of the IP Network Exchange // The name of the IP Network Exchange
Name string `json:"name"` Name string
// Description of the IP Network Exchange // Description of the IP Network Exchange
Description string `json:"description"` Description string `json:"description"`
// Slice of tags associated with the IP Network Exchange // Slice of tags associated with the IP Network Exchange
Tags []string `json:"tags"` Tags []string `json:"tags"`
// Uniform Resource Identifier for the IP Network Exchange // Uniform Resource Identifier for the IP Network Exchange
Uri string `json:"uri"` URI string `json:"uri"`
} }
// CreateIPNetworkExchangeInput details the attributes needed to create an ip network exchange
type CreateIPNetworkExchangeInput struct { type CreateIPNetworkExchangeInput struct {
// The name of the IP Network Exchange to create. Object names can only contain alphanumeric, // The name of the IP Network Exchange to create. Object names can only contain alphanumeric,
// underscore, dash, and period characters. Names are case-sensitive. // underscore, dash, and period characters. Names are case-sensitive.
@ -51,7 +55,7 @@ type CreateIPNetworkExchangeInput struct {
Tags []string `json:"tags"` Tags []string `json:"tags"`
} }
// Create a new IP Network Exchange from an IPNetworkExchangesClient and an input struct. // CreateIPNetworkExchange creates a new IP Network Exchange from an IPNetworkExchangesClient and an input struct.
// Returns a populated Info struct for the IP Network Exchange, and any errors // Returns a populated Info struct for the IP Network Exchange, and any errors
func (c *IPNetworkExchangesClient) CreateIPNetworkExchange(input *CreateIPNetworkExchangeInput) (*IPNetworkExchangeInfo, error) { func (c *IPNetworkExchangesClient) CreateIPNetworkExchange(input *CreateIPNetworkExchangeInput) (*IPNetworkExchangeInfo, error) {
input.Name = c.getQualifiedName(input.Name) input.Name = c.getQualifiedName(input.Name)
@ -64,13 +68,14 @@ func (c *IPNetworkExchangesClient) CreateIPNetworkExchange(input *CreateIPNetwor
return c.success(&ipInfo) return c.success(&ipInfo)
} }
// GetIPNetworkExchangeInput details the attributes needed to retrieve an ip network exchange
type GetIPNetworkExchangeInput struct { type GetIPNetworkExchangeInput struct {
// The name of the IP Network Exchange to query for. Case-sensitive // The name of the IP Network Exchange to query for. Case-sensitive
// Required // Required
Name string `json:"name"` Name string `json:"name"`
} }
// Returns a populated IPNetworkExchangeInfo struct from an input struct // GetIPNetworkExchange returns a populated IPNetworkExchangeInfo struct from an input struct
func (c *IPNetworkExchangesClient) GetIPNetworkExchange(input *GetIPNetworkExchangeInput) (*IPNetworkExchangeInfo, error) { func (c *IPNetworkExchangesClient) GetIPNetworkExchange(input *GetIPNetworkExchangeInput) (*IPNetworkExchangeInfo, error) {
input.Name = c.getQualifiedName(input.Name) input.Name = c.getQualifiedName(input.Name)
@ -82,18 +87,20 @@ func (c *IPNetworkExchangesClient) GetIPNetworkExchange(input *GetIPNetworkExcha
return c.success(&ipInfo) return c.success(&ipInfo)
} }
// DeleteIPNetworkExchangeInput details the attributes neccessary to delete an ip network exchange
type DeleteIPNetworkExchangeInput struct { type DeleteIPNetworkExchangeInput struct {
// The name of the IP Network Exchange to query for. Case-sensitive // The name of the IP Network Exchange to query for. Case-sensitive
// Required // Required
Name string `json:"name"` Name string `json:"name"`
} }
// DeleteIPNetworkExchange deletes the specified ip network exchange
func (c *IPNetworkExchangesClient) DeleteIPNetworkExchange(input *DeleteIPNetworkExchangeInput) error { func (c *IPNetworkExchangesClient) DeleteIPNetworkExchange(input *DeleteIPNetworkExchangeInput) error {
return c.deleteResource(input.Name) return c.deleteResource(input.Name)
} }
// Unqualifies any qualified fields in the IPNetworkExchangeInfo struct // Unqualifies any qualified fields in the IPNetworkExchangeInfo struct
func (c *IPNetworkExchangesClient) success(info *IPNetworkExchangeInfo) (*IPNetworkExchangeInfo, error) { func (c *IPNetworkExchangesClient) success(info *IPNetworkExchangeInfo) (*IPNetworkExchangeInfo, error) {
c.unqualify(&info.Name) info.Name = c.getUnqualifiedName(info.FQDN)
return info, nil return info, nil
} }

View File

@ -1,24 +1,25 @@
package compute package compute
const ( const (
IPNetworkDescription = "ip network" iPNetworkDescription = "ip network"
IPNetworkContainerPath = "/network/v1/ipnetwork/" iPNetworkContainerPath = "/network/v1/ipnetwork/"
IPNetworkResourcePath = "/network/v1/ipnetwork" iPNetworkResourcePath = "/network/v1/ipnetwork"
) )
// IPNetworksClient specifies the ip networks client
type IPNetworksClient struct { type IPNetworksClient struct {
ResourceClient ResourceClient
} }
// IPNetworks() returns an IPNetworksClient that can be used to access the // IPNetworks returns an IPNetworksClient that can be used to access the
// necessary CRUD functions for IP Networks. // necessary CRUD functions for IP Networks.
func (c *ComputeClient) IPNetworks() *IPNetworksClient { func (c *Client) IPNetworks() *IPNetworksClient {
return &IPNetworksClient{ return &IPNetworksClient{
ResourceClient: ResourceClient{ ResourceClient: ResourceClient{
ComputeClient: c, Client: c,
ResourceDescription: IPNetworkDescription, ResourceDescription: iPNetworkDescription,
ContainerPath: IPNetworkContainerPath, ContainerPath: iPNetworkContainerPath,
ResourceRootPath: IPNetworkResourcePath, ResourceRootPath: iPNetworkResourcePath,
}, },
} }
} }
@ -26,8 +27,10 @@ func (c *ComputeClient) IPNetworks() *IPNetworksClient {
// IPNetworkInfo contains the exported fields necessary to hold all the information about an // IPNetworkInfo contains the exported fields necessary to hold all the information about an
// IP Network // IP Network
type IPNetworkInfo struct { type IPNetworkInfo struct {
// Fully Qualified Domain Name
FQDN string `json:"name"`
// The name of the IP Network // The name of the IP Network
Name string `json:"name"` Name string
// The CIDR IPv4 prefix associated with the IP Network // The CIDR IPv4 prefix associated with the IP Network
IPAddressPrefix string `json:"ipAddressPrefix"` IPAddressPrefix string `json:"ipAddressPrefix"`
// Name of the IP Network Exchange associated with the IP Network // Name of the IP Network Exchange associated with the IP Network
@ -39,9 +42,10 @@ type IPNetworkInfo struct {
// Slice of tags associated with the IP Network // Slice of tags associated with the IP Network
Tags []string `json:"tags"` Tags []string `json:"tags"`
// Uniform Resource Identifier for the IP Network // Uniform Resource Identifier for the IP Network
Uri string `json:"uri"` URI string `json:"uri"`
} }
// CreateIPNetworkInput details the attributes needed to create an ip network
type CreateIPNetworkInput struct { type CreateIPNetworkInput struct {
// The name of the IP Network to create. Object names can only contain alphanumeric, // The name of the IP Network to create. Object names can only contain alphanumeric,
// underscore, dash, and period characters. Names are case-sensitive. // underscore, dash, and period characters. Names are case-sensitive.
@ -83,7 +87,7 @@ type CreateIPNetworkInput struct {
Tags []string `json:"tags"` Tags []string `json:"tags"`
} }
// Create a new IP Network from an IPNetworksClient and an input struct. // CreateIPNetwork creates a new IP Network from an IPNetworksClient and an input struct.
// Returns a populated Info struct for the IP Network, and any errors // Returns a populated Info struct for the IP Network, and any errors
func (c *IPNetworksClient) CreateIPNetwork(input *CreateIPNetworkInput) (*IPNetworkInfo, error) { func (c *IPNetworksClient) CreateIPNetwork(input *CreateIPNetworkInput) (*IPNetworkInfo, error) {
input.Name = c.getQualifiedName(input.Name) input.Name = c.getQualifiedName(input.Name)
@ -97,13 +101,14 @@ func (c *IPNetworksClient) CreateIPNetwork(input *CreateIPNetworkInput) (*IPNetw
return c.success(&ipInfo) return c.success(&ipInfo)
} }
// GetIPNetworkInput details the attributes needed to retrieve an ip network
type GetIPNetworkInput struct { type GetIPNetworkInput struct {
// The name of the IP Network to query for. Case-sensitive // The name of the IP Network to query for. Case-sensitive
// Required // Required
Name string `json:"name"` Name string `json:"name"`
} }
// Returns a populated IPNetworkInfo struct from an input struct // GetIPNetwork returns a populated IPNetworkInfo struct from an input struct
func (c *IPNetworksClient) GetIPNetwork(input *GetIPNetworkInput) (*IPNetworkInfo, error) { func (c *IPNetworksClient) GetIPNetwork(input *GetIPNetworkInput) (*IPNetworkInfo, error) {
input.Name = c.getQualifiedName(input.Name) input.Name = c.getQualifiedName(input.Name)
@ -115,6 +120,7 @@ func (c *IPNetworksClient) GetIPNetwork(input *GetIPNetworkInput) (*IPNetworkInf
return c.success(&ipInfo) return c.success(&ipInfo)
} }
// UpdateIPNetworkInput details the attributes needed to update an ip network
type UpdateIPNetworkInput struct { type UpdateIPNetworkInput struct {
// The name of the IP Network to update. Object names can only contain alphanumeric, // The name of the IP Network to update. Object names can only contain alphanumeric,
// underscore, dash, and period characters. Names are case-sensitive. // underscore, dash, and period characters. Names are case-sensitive.
@ -156,6 +162,7 @@ type UpdateIPNetworkInput struct {
Tags []string `json:"tags"` Tags []string `json:"tags"`
} }
// UpdateIPNetwork updates the specified ip network
func (c *IPNetworksClient) UpdateIPNetwork(input *UpdateIPNetworkInput) (*IPNetworkInfo, error) { func (c *IPNetworksClient) UpdateIPNetwork(input *UpdateIPNetworkInput) (*IPNetworkInfo, error) {
input.Name = c.getQualifiedName(input.Name) input.Name = c.getQualifiedName(input.Name)
input.IPNetworkExchange = c.getQualifiedName(input.IPNetworkExchange) input.IPNetworkExchange = c.getQualifiedName(input.IPNetworkExchange)
@ -168,19 +175,21 @@ func (c *IPNetworksClient) UpdateIPNetwork(input *UpdateIPNetworkInput) (*IPNetw
return c.success(&ipInfo) return c.success(&ipInfo)
} }
// DeleteIPNetworkInput specifies the attributes needed to delete an ip network
type DeleteIPNetworkInput struct { type DeleteIPNetworkInput struct {
// The name of the IP Network to query for. Case-sensitive // The name of the IP Network to query for. Case-sensitive
// Required // Required
Name string `json:"name"` Name string `json:"name"`
} }
// DeleteIPNetwork deletes the specified ip network
func (c *IPNetworksClient) DeleteIPNetwork(input *DeleteIPNetworkInput) error { func (c *IPNetworksClient) DeleteIPNetwork(input *DeleteIPNetworkInput) error {
return c.deleteResource(input.Name) return c.deleteResource(input.Name)
} }
// Unqualifies any qualified fields in the IPNetworkInfo struct // Unqualifies any qualified fields in the IPNetworkInfo struct
func (c *IPNetworksClient) success(info *IPNetworkInfo) (*IPNetworkInfo, error) { func (c *IPNetworksClient) success(info *IPNetworkInfo) (*IPNetworkInfo, error) {
c.unqualify(&info.Name) info.Name = c.getUnqualifiedName(info.FQDN)
c.unqualify(&info.IPNetworkExchange) c.unqualify(&info.IPNetworkExchange)
return info, nil return info, nil
} }

View File

@ -6,45 +6,49 @@ type IPReservationsClient struct {
} }
const ( const (
IPReservationDesc = "ip reservation" iPReservationDesc = "ip reservation"
IPReservationContainerPath = "/ip/reservation/" iPReservationContainerPath = "/ip/reservation/"
IPReservataionResourcePath = "/ip/reservation" iPReservataionResourcePath = "/ip/reservation"
) )
// IPReservations obtains an IPReservationsClient which can be used to access to the // IPReservations obtains an IPReservationsClient which can be used to access to the
// IP Reservations functions of the Compute API // IP Reservations functions of the Compute API
func (c *ComputeClient) IPReservations() *IPReservationsClient { func (c *Client) IPReservations() *IPReservationsClient {
return &IPReservationsClient{ return &IPReservationsClient{
ResourceClient: &ResourceClient{ ResourceClient: &ResourceClient{
ComputeClient: c, Client: c,
ResourceDescription: IPReservationDesc, ResourceDescription: iPReservationDesc,
ContainerPath: IPReservationContainerPath, ContainerPath: iPReservationContainerPath,
ResourceRootPath: IPReservataionResourcePath, ResourceRootPath: iPReservataionResourcePath,
}} }}
} }
// IPReservationPool details the constants for the ip reservation pool attribute
type IPReservationPool string type IPReservationPool string
const ( const (
// PublicReservationPool - /oracle/public/ippool
PublicReservationPool IPReservationPool = "/oracle/public/ippool" PublicReservationPool IPReservationPool = "/oracle/public/ippool"
) )
// IPReservationInput describes an existing IP reservation. // IPReservation describes an existing IP reservation.
type IPReservation struct { type IPReservation struct {
// Shows the default account for your identity domain. // Shows the default account for your identity domain.
Account string `json:"account"` Account string `json:"account"`
// Fully Qualified Domain Name
FQDN string `json:"name"`
// Public IP address. // Public IP address.
IP string `json:"ip"` IP string `json:"ip"`
// The three-part name of the IP Reservation (/Compute-identity_domain/user/object). // The three-part name of the IP Reservation (/Compute-identity_domain/user/object).
Name string `json:"name"` Name string
// Pool of public IP addresses // Pool of public IP addresses
ParentPool IPReservationPool `json:"parentpool"` ParentPool IPReservationPool `json:"parentpool"`
// Is the IP Reservation Persistent (i.e. static) or not (i.e. Dynamic)?
Permanent bool `json:"permanent"`
// A comma-separated list of strings which helps you to identify IP reservation. // A comma-separated list of strings which helps you to identify IP reservation.
Tags []string `json:"tags"` Tags []string `json:"tags"`
// Uniform Resource Identifier // Uniform Resource Identifier
Uri string `json:"uri"` URI string `json:"uri"`
// Is the IP Reservation Persistent (i.e. static) or not (i.e. Dynamic)?
Permanent bool `json:"permanent"`
// Is the IP reservation associated with an instance? // Is the IP reservation associated with an instance?
Used bool `json:"used"` Used bool `json:"used"`
} }
@ -142,6 +146,6 @@ func (c *IPReservationsClient) DeleteIPReservation(input *DeleteIPReservationInp
} }
func (c *IPReservationsClient) success(result *IPReservation) (*IPReservation, error) { func (c *IPReservationsClient) success(result *IPReservation) (*IPReservation, error) {
c.unqualify(&result.Name) result.Name = c.getUnqualifiedName(result.FQDN)
return result, nil return result, nil
} }

View File

@ -7,17 +7,17 @@ type MachineImagesClient struct {
// MachineImages obtains an MachineImagesClient which can be used to access to the // MachineImages obtains an MachineImagesClient which can be used to access to the
// MachineImage functions of the Compute API // MachineImage functions of the Compute API
func (c *ComputeClient) MachineImages() *MachineImagesClient { func (c *Client) MachineImages() *MachineImagesClient {
return &MachineImagesClient{ return &MachineImagesClient{
ResourceClient: ResourceClient{ ResourceClient: ResourceClient{
ComputeClient: c, Client: c,
ResourceDescription: "MachineImage", ResourceDescription: "MachineImage",
ContainerPath: "/machineimage/", ContainerPath: "/machineimage/",
ResourceRootPath: "/machineimage", ResourceRootPath: "/machineimage",
}} }}
} }
// MahineImage describes an existing Machine Image. // MachineImage describes an existing Machine Image.
type MachineImage struct { type MachineImage struct {
// account of the associated Object Storage Classic instance // account of the associated Object Storage Classic instance
Account string `json:"account"` Account string `json:"account"`
@ -34,6 +34,9 @@ type MachineImage struct {
// Description of the state of the machine image if there is an error // Description of the state of the machine image if there is an error
ErrorReason string `json:"error_reason"` ErrorReason string `json:"error_reason"`
// Fully Qualified Domain Name
FQDN string `json:"name"`
// dictionary of hypervisor-specific attributes // dictionary of hypervisor-specific attributes
Hypervisor map[string]interface{} `json:"hypervisor"` Hypervisor map[string]interface{} `json:"hypervisor"`
@ -44,7 +47,7 @@ type MachineImage struct {
File string `json:"file"` File string `json:"file"`
// name of the machine image // name of the machine image
Name string `json:"name"` Name string
// Indicates that the image file is available in Object Storage Classic // Indicates that the image file is available in Object Storage Classic
NoUpload bool `json:"no_upload"` NoUpload bool `json:"no_upload"`
@ -117,7 +120,7 @@ func (c *MachineImagesClient) DeleteMachineImage(deleteInput *DeleteMachineImage
return c.deleteResource(deleteInput.Name) return c.deleteResource(deleteInput.Name)
} }
// GetMachineList describes the MachineImage to get // GetMachineImageInput describes the MachineImage to get
type GetMachineImageInput struct { type GetMachineImageInput struct {
// account of the associated Object Storage Classic instance // account of the associated Object Storage Classic instance
Account string `json:"account"` Account string `json:"account"`
@ -138,6 +141,6 @@ func (c *MachineImagesClient) GetMachineImage(getInput *GetMachineImageInput) (*
} }
func (c *MachineImagesClient) success(result *MachineImage) (*MachineImage, error) { func (c *MachineImagesClient) success(result *MachineImage) (*MachineImage, error) {
c.unqualify(&result.Name) result.Name = c.getUnqualifiedName(result.FQDN)
return result, nil return result, nil
} }

View File

@ -7,8 +7,10 @@ import (
"github.com/hashicorp/go-oracle-terraform/client" "github.com/hashicorp/go-oracle-terraform/client"
) )
const WaitForOrchestrationActiveTimeout = time.Duration(3600 * time.Second) const waitForOrchestrationActivePollInterval = 10 * time.Second
const WaitForOrchestrationDeleteTimeout = time.Duration(3600 * time.Second) const waitForOrchestrationActiveTimeout = 3600 * time.Second
const waitForOrchestrationDeletePollInterval = 10 * time.Second
const waitForOrchestrationDeleteTimeout = 3600 * time.Second
// OrchestrationsClient is a client for the Orchestration functions of the Compute API. // OrchestrationsClient is a client for the Orchestration functions of the Compute API.
type OrchestrationsClient struct { type OrchestrationsClient struct {
@ -17,56 +19,73 @@ type OrchestrationsClient struct {
// Orchestrations obtains an OrchestrationsClient which can be used to access to the // Orchestrations obtains an OrchestrationsClient which can be used to access to the
// Orchestration functions of the Compute API // Orchestration functions of the Compute API
func (c *ComputeClient) Orchestrations() *OrchestrationsClient { func (c *Client) Orchestrations() *OrchestrationsClient {
return &OrchestrationsClient{ return &OrchestrationsClient{
ResourceClient: ResourceClient{ ResourceClient: ResourceClient{
ComputeClient: c, Client: c,
ResourceDescription: "Orchestration", ResourceDescription: "Orchestration",
ContainerPath: "/platform/v1/orchestration/", ContainerPath: "/platform/v1/orchestration/",
ResourceRootPath: "/platform/v1/orchestration", ResourceRootPath: "/platform/v1/orchestration",
}} }}
} }
// OrchestrationDesiredState defines the different desired states a orchestration can be in
type OrchestrationDesiredState string type OrchestrationDesiredState string
const ( const (
// * active: Creates all the orchestration objects defined in the orchestration. // OrchestrationDesiredStateActive - Creates all the orchestration objects defined in the orchestration.
OrchestrationDesiredStateActive OrchestrationDesiredState = "active" OrchestrationDesiredStateActive OrchestrationDesiredState = "active"
// * inactive: Adds the orchestration to Oracle Compute Cloud Service, but does not create any of the orchestration // OrchestrationDesiredStateInactive - Adds the orchestration to Oracle Compute Cloud Service, but does not create any of the orchestration
OrchestrationDesiredStateInactive OrchestrationDesiredState = "inactive" OrchestrationDesiredStateInactive OrchestrationDesiredState = "inactive"
// * suspended: Suspends all orchestration objects defined in the orchestration // OrchestrationDesiredStateSuspend - Suspends all orchestration objects defined in the orchestration
OrchestrationDesiredStateSuspend OrchestrationDesiredState = "suspend" OrchestrationDesiredStateSuspend OrchestrationDesiredState = "suspend"
) )
// OrchestrationStatus defines the different status a orchestration can be in
type OrchestrationStatus string type OrchestrationStatus string
const ( const (
OrchestrationStatusActive OrchestrationStatus = "active" // OrchestrationStatusActive - active
OrchestrationStatusInactive OrchestrationStatus = "inactive" OrchestrationStatusActive OrchestrationStatus = "active"
OrchestrationStatusSuspend OrchestrationStatus = "suspend" // OrchestrationStatusInactive - inactive
OrchestrationStatusActivating OrchestrationStatus = "activating" OrchestrationStatusInactive OrchestrationStatus = "inactive"
OrchestrationStatusDeleting OrchestrationStatus = "deleting" // OrchestrationStatusSuspend - suspend
OrchestrationStatusError OrchestrationStatus = "terminal_error" OrchestrationStatusSuspend OrchestrationStatus = "suspend"
OrchestrationStatusStopping OrchestrationStatus = "stopping" // OrchestrationStatusActivating - activating
OrchestrationStatusSuspending OrchestrationStatus = "suspending" OrchestrationStatusActivating OrchestrationStatus = "activating"
OrchestrationStatusStarting OrchestrationStatus = "starting" // OrchestrationStatusDeleting - deleting
OrchestrationStatusDeleting OrchestrationStatus = "deleting"
// OrchestrationStatusError - terminal_error
OrchestrationStatusError OrchestrationStatus = "terminal_error"
// OrchestrationStatusStopping - stopping
OrchestrationStatusStopping OrchestrationStatus = "stopping"
// OrchestrationStatusSuspending - suspending
OrchestrationStatusSuspending OrchestrationStatus = "suspending"
// OrchestrationStatusStarting - starting
OrchestrationStatusStarting OrchestrationStatus = "starting"
// OrchestrationStatusDeactivating - deactivating
OrchestrationStatusDeactivating OrchestrationStatus = "deactivating" OrchestrationStatusDeactivating OrchestrationStatus = "deactivating"
OrchestrationStatusSuspended OrchestrationStatus = "suspended" // OrchestrationStatusSuspended - suspended
OrchestrationStatusSuspended OrchestrationStatus = "suspended"
) )
// OrchestrationType defines the type of orchestrations that can be managed
type OrchestrationType string type OrchestrationType string
const ( const (
// OrchestrationTypeInstance - Instance
OrchestrationTypeInstance OrchestrationType = "Instance" OrchestrationTypeInstance OrchestrationType = "Instance"
) )
// OrchestrationRelationshipType defines the orchestration relationship type for an orchestration
type OrchestrationRelationshipType string type OrchestrationRelationshipType string
const ( const (
// OrchestrationRelationshipTypeDepends - the orchestration relationship depends on a resource
OrchestrationRelationshipTypeDepends OrchestrationRelationshipType = "depends" OrchestrationRelationshipTypeDepends OrchestrationRelationshipType = "depends"
) )
// OrchestrationInfo describes an existing Orchestration. // Orchestration describes an existing Orchestration.
type Orchestration struct { type Orchestration struct {
// The default Oracle Compute Cloud Service account, such as /Compute-acme/default. // The default Oracle Compute Cloud Service account, such as /Compute-acme/default.
Account string `json:"account"` Account string `json:"account"`
@ -76,8 +95,10 @@ type Orchestration struct {
DesiredState OrchestrationDesiredState `json:"desired_state"` DesiredState OrchestrationDesiredState `json:"desired_state"`
// Unique identifier of this orchestration // Unique identifier of this orchestration
ID string `json:"id"` ID string `json:"id"`
// The three-part name of the Orchestration (/Compute-identity_domain/user/object). // Fully Qualified Domain Name
Name string `json:"name"` FQDN string `json:"name"`
// The three-part name of the Orchestration
Name string
// List of orchestration objects // List of orchestration objects
Objects []Object `json:"objects"` Objects []Object `json:"objects"`
// Current status of this orchestration // Current status of this orchestration
@ -125,10 +146,13 @@ type CreateOrchestrationInput struct {
Tags []string `json:"tags,omitempty"` Tags []string `json:"tags,omitempty"`
// Version of this orchestration. It is automatically generated by the server. // Version of this orchestration. It is automatically generated by the server.
Version int `json:"version,omitempty"` Version int `json:"version,omitempty"`
// Time to wait between polls to check status
PollInterval time.Duration `json:"-"`
// Time to wait for an orchestration to be ready // Time to wait for an orchestration to be ready
Timeout time.Duration `json:"-"` Timeout time.Duration `json:"-"`
} }
// Object defines an object inside an orchestration
type Object struct { type Object struct {
// The default Oracle Compute Cloud Service account, such as /Compute-acme/default. // The default Oracle Compute Cloud Service account, such as /Compute-acme/default.
// Optional // Optional
@ -188,6 +212,7 @@ type Object struct {
Version int `json:"version,omitempty"` Version int `json:"version,omitempty"`
} }
// Health defines the health of an object
type Health struct { type Health struct {
// The status of the object // The status of the object
Status OrchestrationStatus `json:"status,omitempty"` Status OrchestrationStatus `json:"status,omitempty"`
@ -199,6 +224,7 @@ type Health struct {
Error string `json:"error,omitempty"` Error string `json:"error,omitempty"`
} }
// Relationship defines the relationship between objects
type Relationship struct { type Relationship struct {
// The type of Relationship // The type of Relationship
// The only type is depends // The only type is depends
@ -217,7 +243,7 @@ func (c *OrchestrationsClient) CreateOrchestration(input *CreateOrchestrationInp
for _, i := range input.Objects { for _, i := range input.Objects {
i.Orchestration = c.getQualifiedName(i.Orchestration) i.Orchestration = c.getQualifiedName(i.Orchestration)
if i.Type == OrchestrationTypeInstance { if i.Type == OrchestrationTypeInstance {
instanceClient := c.ComputeClient.Instances() instanceClient := c.Client.Instances()
instanceInput := i.Template.(*CreateInstanceInput) instanceInput := i.Template.(*CreateInstanceInput)
instanceInput.Name = c.getQualifiedName(instanceInput.Name) instanceInput.Name = c.getQualifiedName(instanceInput.Name)
@ -251,13 +277,16 @@ func (c *OrchestrationsClient) CreateOrchestration(input *CreateOrchestrationInp
Name: createdOrchestration.Name, Name: createdOrchestration.Name,
} }
if input.PollInterval == 0 {
input.PollInterval = waitForOrchestrationActivePollInterval
}
if input.Timeout == 0 { if input.Timeout == 0 {
input.Timeout = WaitForOrchestrationActiveTimeout input.Timeout = waitForOrchestrationActiveTimeout
} }
// Wait for orchestration to be ready and return the result // Wait for orchestration to be ready and return the result
// Don't have to unqualify any objects, as the GetOrchestration method will handle that // Don't have to unqualify any objects, as the GetOrchestration method will handle that
orchestrationInfo, orchestrationError := c.WaitForOrchestrationState(getInput, input.Timeout) orchestrationInfo, orchestrationError := c.WaitForOrchestrationState(getInput, input.PollInterval, input.Timeout)
if orchestrationError != nil { if orchestrationError != nil {
deleteInput := &DeleteOrchestrationInput{ deleteInput := &DeleteOrchestrationInput{
Name: createdOrchestration.Name, Name: createdOrchestration.Name,
@ -269,7 +298,7 @@ func (c *OrchestrationsClient) CreateOrchestration(input *CreateOrchestrationInp
return nil, fmt.Errorf("Error creating orchestration %s: %s", getInput.Name, orchestrationError) return nil, fmt.Errorf("Error creating orchestration %s: %s", getInput.Name, orchestrationError)
} }
return &orchestrationInfo, nil return orchestrationInfo, nil
} }
// GetOrchestrationInput describes the Orchestration to get // GetOrchestrationInput describes the Orchestration to get
@ -315,6 +344,8 @@ type UpdateOrchestrationInput struct {
Tags []string `json:"tags,omitempty"` Tags []string `json:"tags,omitempty"`
// Version of this orchestration. It is automatically generated by the server. // Version of this orchestration. It is automatically generated by the server.
Version int `json:"version,omitempty"` Version int `json:"version,omitempty"`
// Time to wait between polls to check status
PollInterval time.Duration `json:"-"`
// Time to wait for an orchestration to be ready // Time to wait for an orchestration to be ready
Timeout time.Duration `json:"-"` Timeout time.Duration `json:"-"`
} }
@ -340,18 +371,21 @@ func (c *OrchestrationsClient) UpdateOrchestration(input *UpdateOrchestrationInp
Name: updatedOrchestration.Name, Name: updatedOrchestration.Name,
} }
if input.PollInterval == 0 {
input.PollInterval = waitForOrchestrationActivePollInterval
}
if input.Timeout == 0 { if input.Timeout == 0 {
input.Timeout = WaitForOrchestrationActiveTimeout input.Timeout = waitForOrchestrationActiveTimeout
} }
// Wait for orchestration to be ready and return the result // Wait for orchestration to be ready and return the result
// Don't have to unqualify any objects, as the GetOrchestration method will handle that // Don't have to unqualify any objects, as the GetOrchestration method will handle that
orchestrationInfo, orchestrationError := c.WaitForOrchestrationState(getInput, input.Timeout) orchestrationInfo, orchestrationError := c.WaitForOrchestrationState(getInput, input.PollInterval, input.Timeout)
if orchestrationError != nil { if orchestrationError != nil {
return nil, orchestrationError return nil, orchestrationError
} }
return &orchestrationInfo, nil return orchestrationInfo, nil
} }
// DeleteOrchestrationInput describes the Orchestration to delete // DeleteOrchestrationInput describes the Orchestration to delete
@ -359,6 +393,8 @@ type DeleteOrchestrationInput struct {
// The three-part name of the Orchestration (/Compute-identity_domain/user/object). // The three-part name of the Orchestration (/Compute-identity_domain/user/object).
// Required // Required
Name string `json:"name"` Name string `json:"name"`
// Poll Interval for delete request
PollInterval time.Duration `json:"-"`
// Timeout for delete request // Timeout for delete request
Timeout time.Duration `json:"-"` Timeout time.Duration `json:"-"`
} }
@ -369,18 +405,21 @@ func (c *OrchestrationsClient) DeleteOrchestration(input *DeleteOrchestrationInp
return err return err
} }
if input.PollInterval == 0 {
input.PollInterval = waitForOrchestrationDeletePollInterval
}
if input.Timeout == 0 { if input.Timeout == 0 {
input.Timeout = WaitForOrchestrationDeleteTimeout input.Timeout = waitForOrchestrationDeleteTimeout
} }
return c.WaitForOrchestrationDeleted(input, input.Timeout) return c.WaitForOrchestrationDeleted(input, input.PollInterval, input.Timeout)
} }
func (c *OrchestrationsClient) success(info *Orchestration) (*Orchestration, error) { func (c *OrchestrationsClient) success(info *Orchestration) (*Orchestration, error) {
c.unqualify(&info.Name) info.Name = c.getUnqualifiedName(info.FQDN)
for _, i := range info.Objects { for _, i := range info.Objects {
c.unqualify(&i.Orchestration) c.unqualify(&i.Orchestration)
if OrchestrationType(i.Type) == OrchestrationTypeInstance { if i.Type == OrchestrationTypeInstance {
instanceInput := i.Template.(map[string]interface{}) instanceInput := i.Template.(map[string]interface{})
instanceInput["name"] = c.getUnqualifiedName(instanceInput["name"].(string)) instanceInput["name"] = c.getUnqualifiedName(instanceInput["name"].(string))
} }
@ -389,11 +428,11 @@ func (c *OrchestrationsClient) success(info *Orchestration) (*Orchestration, err
return info, nil return info, nil
} }
// WaitForOrchestrationActive waits for an orchestration to be completely initialized and available. // WaitForOrchestrationState waits for an orchestration to be in the specified state
func (c *OrchestrationsClient) WaitForOrchestrationState(input *GetOrchestrationInput, timeout time.Duration) (Orchestration, error) { func (c *OrchestrationsClient) WaitForOrchestrationState(input *GetOrchestrationInput, pollInterval, timeout time.Duration) (*Orchestration, error) {
var info *Orchestration var info *Orchestration
var getErr error var getErr error
err := c.client.WaitFor("orchestration to be ready", timeout, func() (bool, error) { err := c.client.WaitFor("orchestration to be ready", pollInterval, timeout, func() (bool, error) {
info, getErr = c.GetOrchestration(input) info, getErr = c.GetOrchestration(input)
if getErr != nil { if getErr != nil {
return false, getErr return false, getErr
@ -427,19 +466,18 @@ func (c *OrchestrationsClient) WaitForOrchestrationState(input *GetOrchestration
c.client.DebugLogString("Orchestration suspended") c.client.DebugLogString("Orchestration suspended")
if info.DesiredState == OrchestrationDesiredStateSuspend { if info.DesiredState == OrchestrationDesiredStateSuspend {
return true, nil return true, nil
} else {
return false, nil
} }
return false, nil
default: default:
return false, fmt.Errorf("Unknown orchestration state: %s, erroring", s) return false, fmt.Errorf("Unknown orchestration state: %s, erroring", s)
} }
}) })
return *info, err return info, err
} }
// WaitForOrchestrationDeleted waits for an orchestration to be fully deleted. // WaitForOrchestrationDeleted waits for an orchestration to be fully deleted.
func (c *OrchestrationsClient) WaitForOrchestrationDeleted(input *DeleteOrchestrationInput, timeout time.Duration) error { func (c *OrchestrationsClient) WaitForOrchestrationDeleted(input *DeleteOrchestrationInput, pollInterval, timeout time.Duration) error {
return c.client.WaitFor("orchestration to be deleted", timeout, func() (bool, error) { return c.client.WaitFor("orchestration to be deleted", pollInterval, timeout, func() (bool, error) {
var info Orchestration var info Orchestration
if err := c.getResource(input.Name, &info); err != nil { if err := c.getResource(input.Name, &info); err != nil {
if client.WasNotFoundError(err) { if client.WasNotFoundError(err) {

View File

@ -1,43 +1,49 @@
package compute package compute
const ( const (
RoutesDescription = "IP Network Route" routesDescription = "IP Network Route"
RoutesContainerPath = "/network/v1/route/" routesContainerPath = "/network/v1/route/"
RoutesResourcePath = "/network/v1/route" routesResourcePath = "/network/v1/route"
) )
// RoutesClient specifies the attributes of a route client
type RoutesClient struct { type RoutesClient struct {
ResourceClient ResourceClient
} }
func (c *ComputeClient) Routes() *RoutesClient { // Routes returns a route client
func (c *Client) Routes() *RoutesClient {
return &RoutesClient{ return &RoutesClient{
ResourceClient: ResourceClient{ ResourceClient: ResourceClient{
ComputeClient: c, Client: c,
ResourceDescription: RoutesDescription, ResourceDescription: routesDescription,
ContainerPath: RoutesContainerPath, ContainerPath: routesContainerPath,
ResourceRootPath: RoutesResourcePath, ResourceRootPath: routesResourcePath,
}, },
} }
} }
// RouteInfo details the attributes for a route
type RouteInfo struct { type RouteInfo struct {
// Admin distance associated with this route // Admin distance associated with this route
AdminDistance int `json:"adminDistance"` AdminDistance int `json:"adminDistance"`
// Description of the route // Description of the route
Description string `json:"description"` Description string `json:"description"`
// Fully Qualified Domain Name
FQDN string `json:"name"`
// CIDR IPv4 Prefix associated with this route // CIDR IPv4 Prefix associated with this route
IPAddressPrefix string `json:"ipAddressPrefix"` IPAddressPrefix string `json:"ipAddressPrefix"`
// Name of the route // Name of the route
Name string `json:"name"` Name string
// Name of the VNIC set associated with the route // Name of the VNIC set associated with the route
NextHopVnicSet string `json:"nextHopVnicSet"` NextHopVnicSet string `json:"nextHopVnicSet"`
// Slice of Tags associated with the route // Slice of Tags associated with the route
Tags []string `json:"tags,omitempty"` Tags []string `json:"tags,omitempty"`
// Uniform resource identifier associated with the route // Uniform resource identifier associated with the route
Uri string `json:"uri"` URI string `json:"uri"`
} }
// CreateRouteInput details the attributes needed to create a route
type CreateRouteInput struct { type CreateRouteInput struct {
// Specify 0,1, or 2 as the route's administrative distance. // Specify 0,1, or 2 as the route's administrative distance.
// If you do not specify a value, the default value is 0. // If you do not specify a value, the default value is 0.
@ -67,6 +73,7 @@ type CreateRouteInput struct {
Tags []string `json:"tags,omitempty"` Tags []string `json:"tags,omitempty"`
} }
// CreateRoute creates the requested route
func (c *RoutesClient) CreateRoute(input *CreateRouteInput) (*RouteInfo, error) { func (c *RoutesClient) CreateRoute(input *CreateRouteInput) (*RouteInfo, error) {
input.Name = c.getQualifiedName(input.Name) input.Name = c.getQualifiedName(input.Name)
input.NextHopVnicSet = c.getQualifiedName(input.NextHopVnicSet) input.NextHopVnicSet = c.getQualifiedName(input.NextHopVnicSet)
@ -79,12 +86,14 @@ func (c *RoutesClient) CreateRoute(input *CreateRouteInput) (*RouteInfo, error)
return c.success(&routeInfo) return c.success(&routeInfo)
} }
// GetRouteInput details the attributes needed to retrive a route
type GetRouteInput struct { type GetRouteInput struct {
// Name of the Route to query for. Case-sensitive // Name of the Route to query for. Case-sensitive
// Required // Required
Name string `json:"name"` Name string `json:"name"`
} }
// GetRoute retrieves the specified route
func (c *RoutesClient) GetRoute(input *GetRouteInput) (*RouteInfo, error) { func (c *RoutesClient) GetRoute(input *GetRouteInput) (*RouteInfo, error) {
input.Name = c.getQualifiedName(input.Name) input.Name = c.getQualifiedName(input.Name)
@ -95,6 +104,7 @@ func (c *RoutesClient) GetRoute(input *GetRouteInput) (*RouteInfo, error) {
return c.success(&routeInfo) return c.success(&routeInfo)
} }
// UpdateRouteInput details the attributes needed to update a route
type UpdateRouteInput struct { type UpdateRouteInput struct {
// Specify 0,1, or 2 as the route's administrative distance. // Specify 0,1, or 2 as the route's administrative distance.
// If you do not specify a value, the default value is 0. // If you do not specify a value, the default value is 0.
@ -124,6 +134,7 @@ type UpdateRouteInput struct {
Tags []string `json:"tags"` Tags []string `json:"tags"`
} }
// UpdateRoute updates the specified route
func (c *RoutesClient) UpdateRoute(input *UpdateRouteInput) (*RouteInfo, error) { func (c *RoutesClient) UpdateRoute(input *UpdateRouteInput) (*RouteInfo, error) {
input.Name = c.getQualifiedName(input.Name) input.Name = c.getQualifiedName(input.Name)
input.NextHopVnicSet = c.getQualifiedName(input.NextHopVnicSet) input.NextHopVnicSet = c.getQualifiedName(input.NextHopVnicSet)
@ -136,18 +147,20 @@ func (c *RoutesClient) UpdateRoute(input *UpdateRouteInput) (*RouteInfo, error)
return c.success(&routeInfo) return c.success(&routeInfo)
} }
// DeleteRouteInput details the route to delete
type DeleteRouteInput struct { type DeleteRouteInput struct {
// Name of the Route to delete. Case-sensitive // Name of the Route to delete. Case-sensitive
// Required // Required
Name string `json:"name"` Name string `json:"name"`
} }
// DeleteRoute deletes the specified route
func (c *RoutesClient) DeleteRoute(input *DeleteRouteInput) error { func (c *RoutesClient) DeleteRoute(input *DeleteRouteInput) error {
return c.deleteResource(input.Name) return c.deleteResource(input.Name)
} }
func (c *RoutesClient) success(info *RouteInfo) (*RouteInfo, error) { func (c *RoutesClient) success(info *RouteInfo) (*RouteInfo, error) {
c.unqualify(&info.Name) info.Name = c.getUnqualifiedName(info.FQDN)
c.unqualify(&info.NextHopVnicSet) c.unqualify(&info.NextHopVnicSet)
return info, nil return info, nil
} }

View File

@ -7,10 +7,10 @@ type SecRulesClient struct {
// SecRules obtains a SecRulesClient which can be used to access to the // SecRules obtains a SecRulesClient which can be used to access to the
// Sec Rules functions of the Compute API // Sec Rules functions of the Compute API
func (c *ComputeClient) SecRules() *SecRulesClient { func (c *Client) SecRules() *SecRulesClient {
return &SecRulesClient{ return &SecRulesClient{
ResourceClient: ResourceClient{ ResourceClient: ResourceClient{
ComputeClient: c, Client: c,
ResourceDescription: "security ip list", ResourceDescription: "security ip list",
ContainerPath: "/secrule/", ContainerPath: "/secrule/",
ResourceRootPath: "/secrule", ResourceRootPath: "/secrule",
@ -29,8 +29,10 @@ type SecRuleInfo struct {
Disabled bool `json:"disabled"` Disabled bool `json:"disabled"`
// The name of the destination security list or security IP list. // The name of the destination security list or security IP list.
DestinationList string `json:"dst_list"` DestinationList string `json:"dst_list"`
// Fully Qualified Domain Name
FQDN string `json:"name"`
// The name of the sec rule // The name of the sec rule
Name string `json:"name"` Name string
// The name of the source security list or security IP list. // The name of the source security list or security IP list.
SourceList string `json:"src_list"` SourceList string `json:"src_list"`
// Uniform Resource Identifier for the sec rule // Uniform Resource Identifier for the sec rule
@ -185,7 +187,7 @@ func (c *SecRulesClient) DeleteSecRule(deleteInput *DeleteSecRuleInput) error {
} }
func (c *SecRulesClient) success(ruleInfo *SecRuleInfo) (*SecRuleInfo, error) { func (c *SecRulesClient) success(ruleInfo *SecRuleInfo) (*SecRuleInfo, error) {
ruleInfo.Name = c.getUnqualifiedName(ruleInfo.Name) ruleInfo.Name = c.getUnqualifiedName(ruleInfo.FQDN)
ruleInfo.SourceList = c.unqualifyListName(ruleInfo.SourceList) ruleInfo.SourceList = c.unqualifyListName(ruleInfo.SourceList)
ruleInfo.DestinationList = c.unqualifyListName(ruleInfo.DestinationList) ruleInfo.DestinationList = c.unqualifyListName(ruleInfo.DestinationList)
ruleInfo.Application = c.getUnqualifiedName(ruleInfo.Application) ruleInfo.Application = c.getUnqualifiedName(ruleInfo.Application)

View File

@ -7,10 +7,10 @@ type SecurityApplicationsClient struct {
// SecurityApplications obtains a SecurityApplicationsClient which can be used to access to the // SecurityApplications obtains a SecurityApplicationsClient which can be used to access to the
// Security Application functions of the Compute API // Security Application functions of the Compute API
func (c *ComputeClient) SecurityApplications() *SecurityApplicationsClient { func (c *Client) SecurityApplications() *SecurityApplicationsClient {
return &SecurityApplicationsClient{ return &SecurityApplicationsClient{
ResourceClient: ResourceClient{ ResourceClient: ResourceClient{
ComputeClient: c, Client: c,
ResourceDescription: "security application", ResourceDescription: "security application",
ContainerPath: "/secapplication/", ContainerPath: "/secapplication/",
ResourceRootPath: "/secapplication", ResourceRootPath: "/secapplication",
@ -23,61 +23,92 @@ type SecurityApplicationInfo struct {
Description string `json:"description"` Description string `json:"description"`
// The TCP or UDP destination port number. This can be a port range, such as 5900-5999 for TCP. // The TCP or UDP destination port number. This can be a port range, such as 5900-5999 for TCP.
DPort string `json:"dport"` DPort string `json:"dport"`
// Fully Qualified Domain Name
FQDN string `json:"name"`
// The ICMP code. // The ICMP code.
ICMPCode SecurityApplicationICMPCode `json:"icmpcode"` ICMPCode SecurityApplicationICMPCode `json:"icmpcode"`
// The ICMP type. // The ICMP type.
ICMPType SecurityApplicationICMPType `json:"icmptype"` ICMPType SecurityApplicationICMPType `json:"icmptype"`
// The three-part name of the Security Application (/Compute-identity_domain/user/object). // The three-part name of the Security Application (/Compute-identity_domain/user/object).
Name string `json:"name"` Name string
// The protocol to use. // The protocol to use.
Protocol SecurityApplicationProtocol `json:"protocol"` Protocol SecurityApplicationProtocol `json:"protocol"`
// The Uniform Resource Identifier // The Uniform Resource Identifier
URI string `json:"uri"` URI string `json:"uri"`
} }
// SecurityApplicationProtocol defines the constants for a security application protocol
type SecurityApplicationProtocol string type SecurityApplicationProtocol string
const ( const (
All SecurityApplicationProtocol = "all" // All - all
AH SecurityApplicationProtocol = "ah" All SecurityApplicationProtocol = "all"
ESP SecurityApplicationProtocol = "esp" // AH - ah
ICMP SecurityApplicationProtocol = "icmp" AH SecurityApplicationProtocol = "ah"
// ESP - esp
ESP SecurityApplicationProtocol = "esp"
// ICMP - icmp
ICMP SecurityApplicationProtocol = "icmp"
// ICMPV6 - icmpv6
ICMPV6 SecurityApplicationProtocol = "icmpv6" ICMPV6 SecurityApplicationProtocol = "icmpv6"
IGMP SecurityApplicationProtocol = "igmp" // IGMP - igmp
IPIP SecurityApplicationProtocol = "ipip" IGMP SecurityApplicationProtocol = "igmp"
GRE SecurityApplicationProtocol = "gre" // IPIP - ipip
IPIP SecurityApplicationProtocol = "ipip"
// GRE - gre
GRE SecurityApplicationProtocol = "gre"
// MPLSIP - mplsip
MPLSIP SecurityApplicationProtocol = "mplsip" MPLSIP SecurityApplicationProtocol = "mplsip"
OSPF SecurityApplicationProtocol = "ospf" // OSPF - ospf
PIM SecurityApplicationProtocol = "pim" OSPF SecurityApplicationProtocol = "ospf"
RDP SecurityApplicationProtocol = "rdp" // PIM - pim
SCTP SecurityApplicationProtocol = "sctp" PIM SecurityApplicationProtocol = "pim"
TCP SecurityApplicationProtocol = "tcp" // RDP - rdp
UDP SecurityApplicationProtocol = "udp" RDP SecurityApplicationProtocol = "rdp"
// SCTP - sctp
SCTP SecurityApplicationProtocol = "sctp"
// TCP - tcp
TCP SecurityApplicationProtocol = "tcp"
// UDP - udp
UDP SecurityApplicationProtocol = "udp"
) )
// SecurityApplicationICMPCode defines the constants an icmp code can be
type SecurityApplicationICMPCode string type SecurityApplicationICMPCode string
const ( const (
Admin SecurityApplicationICMPCode = "admin" // Admin - admin
Df SecurityApplicationICMPCode = "df" Admin SecurityApplicationICMPCode = "admin"
Host SecurityApplicationICMPCode = "host" // Df - df
Network SecurityApplicationICMPCode = "network" Df SecurityApplicationICMPCode = "df"
Port SecurityApplicationICMPCode = "port" // Host - host
Host SecurityApplicationICMPCode = "host"
// Network - network
Network SecurityApplicationICMPCode = "network"
// Port - port
Port SecurityApplicationICMPCode = "port"
// Protocol - protocol
Protocol SecurityApplicationICMPCode = "protocol" Protocol SecurityApplicationICMPCode = "protocol"
) )
// SecurityApplicationICMPType defines the constants an icmp type can be
type SecurityApplicationICMPType string type SecurityApplicationICMPType string
const ( const (
Echo SecurityApplicationICMPType = "echo" // Echo - echo
Reply SecurityApplicationICMPType = "reply" Echo SecurityApplicationICMPType = "echo"
TTL SecurityApplicationICMPType = "ttl" // Reply - reply
TraceRoute SecurityApplicationICMPType = "traceroute" Reply SecurityApplicationICMPType = "reply"
// TTL - ttl
TTL SecurityApplicationICMPType = "ttl"
// TraceRoute - traceroute
TraceRoute SecurityApplicationICMPType = "traceroute"
// Unreachable - unreachable
Unreachable SecurityApplicationICMPType = "unreachable" Unreachable SecurityApplicationICMPType = "unreachable"
) )
func (c *SecurityApplicationsClient) success(result *SecurityApplicationInfo) (*SecurityApplicationInfo, error) { func (c *SecurityApplicationsClient) success(result *SecurityApplicationInfo) (*SecurityApplicationInfo, error) {
c.unqualify(&result.Name) result.Name = c.getUnqualifiedName(result.FQDN)
return result, nil return result, nil
} }

View File

@ -7,10 +7,10 @@ type SecurityAssociationsClient struct {
// SecurityAssociations obtains a SecurityAssociationsClient which can be used to access to the // SecurityAssociations obtains a SecurityAssociationsClient which can be used to access to the
// Security Association functions of the Compute API // Security Association functions of the Compute API
func (c *ComputeClient) SecurityAssociations() *SecurityAssociationsClient { func (c *Client) SecurityAssociations() *SecurityAssociationsClient {
return &SecurityAssociationsClient{ return &SecurityAssociationsClient{
ResourceClient: ResourceClient{ ResourceClient: ResourceClient{
ComputeClient: c, Client: c,
ResourceDescription: "security association", ResourceDescription: "security association",
ContainerPath: "/secassociation/", ContainerPath: "/secassociation/",
ResourceRootPath: "/secassociation", ResourceRootPath: "/secassociation",
@ -19,8 +19,10 @@ func (c *ComputeClient) SecurityAssociations() *SecurityAssociationsClient {
// SecurityAssociationInfo describes an existing security association. // SecurityAssociationInfo describes an existing security association.
type SecurityAssociationInfo struct { type SecurityAssociationInfo struct {
// The three-part name of the Security Association (/Compute-identity_domain/user/object). // Fully Qualified Domain Name
Name string `json:"name"` FQDN string `json:"name"`
// The three-part name of the Security Association
Name string
// The name of the Security List that you want to associate with the instance. // The name of the Security List that you want to associate with the instance.
SecList string `json:"seclist"` SecList string `json:"seclist"`
// vCable of the instance that you want to associate with the security list. // vCable of the instance that you want to associate with the security list.
@ -90,6 +92,7 @@ func (c *SecurityAssociationsClient) DeleteSecurityAssociation(deleteInput *Dele
} }
func (c *SecurityAssociationsClient) success(assocInfo *SecurityAssociationInfo) (*SecurityAssociationInfo, error) { func (c *SecurityAssociationsClient) success(assocInfo *SecurityAssociationInfo) (*SecurityAssociationInfo, error) {
c.unqualify(&assocInfo.Name, &assocInfo.SecList, &assocInfo.VCable) assocInfo.Name = c.getUnqualifiedName(assocInfo.FQDN)
c.unqualify(&assocInfo.SecList, &assocInfo.VCable)
return assocInfo, nil return assocInfo, nil
} }

View File

@ -7,10 +7,10 @@ type SecurityIPListsClient struct {
// SecurityIPLists obtains a SecurityIPListsClient which can be used to access to the // SecurityIPLists obtains a SecurityIPListsClient which can be used to access to the
// Security IP List functions of the Compute API // Security IP List functions of the Compute API
func (c *ComputeClient) SecurityIPLists() *SecurityIPListsClient { func (c *Client) SecurityIPLists() *SecurityIPListsClient {
return &SecurityIPListsClient{ return &SecurityIPListsClient{
ResourceClient: ResourceClient{ ResourceClient: ResourceClient{
ComputeClient: c, Client: c,
ResourceDescription: "security ip list", ResourceDescription: "security ip list",
ContainerPath: "/seciplist/", ContainerPath: "/seciplist/",
ResourceRootPath: "/seciplist", ResourceRootPath: "/seciplist",
@ -21,8 +21,10 @@ func (c *ComputeClient) SecurityIPLists() *SecurityIPListsClient {
type SecurityIPListInfo struct { type SecurityIPListInfo struct {
// A description of the security IP list. // A description of the security IP list.
Description string `json:"description"` Description string `json:"description"`
// The three-part name of the object (/Compute-identity_domain/user/object). // Fully Qualified Domain Name
Name string `json:"name"` FQDN string `json:"name"`
// The name of the object
Name string
// A comma-separated list of the subnets (in CIDR format) or IPv4 addresses for which you want to create this security IP list. // A comma-separated list of the subnets (in CIDR format) or IPv4 addresses for which you want to create this security IP list.
SecIPEntries []string `json:"secipentries"` SecIPEntries []string `json:"secipentries"`
// Uniform Resource Identifier // Uniform Resource Identifier
@ -108,6 +110,6 @@ func (c *SecurityIPListsClient) DeleteSecurityIPList(deleteInput *DeleteSecurity
} }
func (c *SecurityIPListsClient) success(listInfo *SecurityIPListInfo) (*SecurityIPListInfo, error) { func (c *SecurityIPListsClient) success(listInfo *SecurityIPListInfo) (*SecurityIPListInfo, error) {
c.unqualify(&listInfo.Name) listInfo.Name = c.getUnqualifiedName(listInfo.FQDN)
return listInfo, nil return listInfo, nil
} }

View File

@ -7,21 +7,25 @@ type SecurityListsClient struct {
// SecurityLists obtains a SecurityListsClient which can be used to access to the // SecurityLists obtains a SecurityListsClient which can be used to access to the
// Security List functions of the Compute API // Security List functions of the Compute API
func (c *ComputeClient) SecurityLists() *SecurityListsClient { func (c *Client) SecurityLists() *SecurityListsClient {
return &SecurityListsClient{ return &SecurityListsClient{
ResourceClient: ResourceClient{ ResourceClient: ResourceClient{
ComputeClient: c, Client: c,
ResourceDescription: "security list", ResourceDescription: "security list",
ContainerPath: "/seclist/", ContainerPath: "/seclist/",
ResourceRootPath: "/seclist", ResourceRootPath: "/seclist",
}} }}
} }
// SecurityListPolicy defines the constants a security list policy can be
type SecurityListPolicy string type SecurityListPolicy string
const ( const (
SecurityListPolicyDeny SecurityListPolicy = "deny" // SecurityListPolicyDeny - deny
SecurityListPolicyDeny SecurityListPolicy = "deny"
// SecurityListPolicyReject - reject
SecurityListPolicyReject SecurityListPolicy = "reject" SecurityListPolicyReject SecurityListPolicy = "reject"
// SecurityListPolicyPermit - permit
SecurityListPolicyPermit SecurityListPolicy = "permit" SecurityListPolicyPermit SecurityListPolicy = "permit"
) )
@ -31,8 +35,10 @@ type SecurityListInfo struct {
Account string `json:"account"` Account string `json:"account"`
// A description of the security list. // A description of the security list.
Description string `json:"description"` Description string `json:"description"`
// The three-part name of the security list (/Compute-identity_domain/user/object). // Fully Qualified Domain Name
Name string `json:"name"` FQDN string `json:"name"`
// The name of the security list
Name string
// The policy for outbound traffic from the security list. // The policy for outbound traffic from the security list.
OutboundCIDRPolicy SecurityListPolicy `json:"outbound_cidr_policy"` OutboundCIDRPolicy SecurityListPolicy `json:"outbound_cidr_policy"`
// The policy for inbound traffic to the security list // The policy for inbound traffic to the security list
@ -126,6 +132,6 @@ func (c *SecurityListsClient) DeleteSecurityList(deleteInput *DeleteSecurityList
} }
func (c *SecurityListsClient) success(listInfo *SecurityListInfo) (*SecurityListInfo, error) { func (c *SecurityListsClient) success(listInfo *SecurityListInfo) (*SecurityListInfo, error) {
c.unqualify(&listInfo.Name) listInfo.Name = c.getUnqualifiedName(listInfo.FQDN)
return listInfo, nil return listInfo, nil
} }

View File

@ -1,24 +1,25 @@
package compute package compute
const ( const (
SecurityProtocolDescription = "security protocol" securityProtocolDescription = "security protocol"
SecurityProtocolContainerPath = "/network/v1/secprotocol/" securityProtocolContainerPath = "/network/v1/secprotocol/"
SecurityProtocolResourcePath = "/network/v1/secprotocol" securityProtocolResourcePath = "/network/v1/secprotocol"
) )
// SecurityProtocolsClient details the security protocols client
type SecurityProtocolsClient struct { type SecurityProtocolsClient struct {
ResourceClient ResourceClient
} }
// SecurityProtocols() returns an SecurityProtocolsClient that can be used to access the // SecurityProtocols returns an SecurityProtocolsClient that can be used to access the
// necessary CRUD functions for Security Protocols. // necessary CRUD functions for Security Protocols.
func (c *ComputeClient) SecurityProtocols() *SecurityProtocolsClient { func (c *Client) SecurityProtocols() *SecurityProtocolsClient {
return &SecurityProtocolsClient{ return &SecurityProtocolsClient{
ResourceClient: ResourceClient{ ResourceClient: ResourceClient{
ComputeClient: c, Client: c,
ResourceDescription: SecurityProtocolDescription, ResourceDescription: securityProtocolDescription,
ContainerPath: SecurityProtocolContainerPath, ContainerPath: securityProtocolContainerPath,
ResourceRootPath: SecurityProtocolResourcePath, ResourceRootPath: securityProtocolResourcePath,
}, },
} }
} }
@ -32,16 +33,19 @@ type SecurityProtocolInfo struct {
IPProtocol string `json:"ipProtocol"` IPProtocol string `json:"ipProtocol"`
// List of port numbers or port range strings to match the packet's source port. // List of port numbers or port range strings to match the packet's source port.
SrcPortSet []string `json:"srcPortSet"` SrcPortSet []string `json:"srcPortSet"`
// Fully Qualified Domain Name
FQDN string `json:"name"`
// The name of the Security Protocol // The name of the Security Protocol
Name string `json:"name"` Name string
// Description of the Security Protocol // Description of the Security Protocol
Description string `json:"description"` Description string `json:"description"`
// Slice of tags associated with the Security Protocol // Slice of tags associated with the Security Protocol
Tags []string `json:"tags"` Tags []string `json:"tags"`
// Uniform Resource Identifier for the Security Protocol // Uniform Resource Identifier for the Security Protocol
Uri string `json:"uri"` URI string `json:"uri"`
} }
// CreateSecurityProtocolInput details the attributes of the security protocol to create
type CreateSecurityProtocolInput struct { type CreateSecurityProtocolInput struct {
// The name of the Security Protocol to create. Object names can only contain alphanumeric, // The name of the Security Protocol to create. Object names can only contain alphanumeric,
// underscore, dash, and period characters. Names are case-sensitive. // underscore, dash, and period characters. Names are case-sensitive.
@ -86,7 +90,7 @@ type CreateSecurityProtocolInput struct {
Tags []string `json:"tags"` Tags []string `json:"tags"`
} }
// Create a new Security Protocol from an SecurityProtocolsClient and an input struct. // CreateSecurityProtocol creates a new Security Protocol from an SecurityProtocolsClient and an input struct.
// Returns a populated Info struct for the Security Protocol, and any errors // Returns a populated Info struct for the Security Protocol, and any errors
func (c *SecurityProtocolsClient) CreateSecurityProtocol(input *CreateSecurityProtocolInput) (*SecurityProtocolInfo, error) { func (c *SecurityProtocolsClient) CreateSecurityProtocol(input *CreateSecurityProtocolInput) (*SecurityProtocolInfo, error) {
input.Name = c.getQualifiedName(input.Name) input.Name = c.getQualifiedName(input.Name)
@ -99,13 +103,14 @@ func (c *SecurityProtocolsClient) CreateSecurityProtocol(input *CreateSecurityPr
return c.success(&ipInfo) return c.success(&ipInfo)
} }
// GetSecurityProtocolInput details the security protocol input to retrive
type GetSecurityProtocolInput struct { type GetSecurityProtocolInput struct {
// The name of the Security Protocol to query for. Case-sensitive // The name of the Security Protocol to query for. Case-sensitive
// Required // Required
Name string `json:"name"` Name string `json:"name"`
} }
// Returns a populated SecurityProtocolInfo struct from an input struct // GetSecurityProtocol returns a populated SecurityProtocolInfo struct from an input struct
func (c *SecurityProtocolsClient) GetSecurityProtocol(input *GetSecurityProtocolInput) (*SecurityProtocolInfo, error) { func (c *SecurityProtocolsClient) GetSecurityProtocol(input *GetSecurityProtocolInput) (*SecurityProtocolInfo, error) {
input.Name = c.getQualifiedName(input.Name) input.Name = c.getQualifiedName(input.Name)
@ -170,18 +175,20 @@ func (c *SecurityProtocolsClient) UpdateSecurityProtocol(updateInput *UpdateSecu
return c.success(&ipInfo) return c.success(&ipInfo)
} }
// DeleteSecurityProtocolInput details the security protocal to delete
type DeleteSecurityProtocolInput struct { type DeleteSecurityProtocolInput struct {
// The name of the Security Protocol to query for. Case-sensitive // The name of the Security Protocol to query for. Case-sensitive
// Required // Required
Name string `json:"name"` Name string `json:"name"`
} }
// DeleteSecurityProtocol deletes the specified security protocol
func (c *SecurityProtocolsClient) DeleteSecurityProtocol(input *DeleteSecurityProtocolInput) error { func (c *SecurityProtocolsClient) DeleteSecurityProtocol(input *DeleteSecurityProtocolInput) error {
return c.deleteResource(input.Name) return c.deleteResource(input.Name)
} }
// Unqualifies any qualified fields in the SecurityProtocolInfo struct // Unqualifies any qualified fields in the SecurityProtocolInfo struct
func (c *SecurityProtocolsClient) success(info *SecurityProtocolInfo) (*SecurityProtocolInfo, error) { func (c *SecurityProtocolsClient) success(info *SecurityProtocolInfo) (*SecurityProtocolInfo, error) {
c.unqualify(&info.Name) info.Name = c.getUnqualifiedName(info.FQDN)
return info, nil return info, nil
} }

View File

@ -1,24 +1,25 @@
package compute package compute
const ( const (
SecurityRuleDescription = "security rules" securityRuleDescription = "security rules"
SecurityRuleContainerPath = "/network/v1/secrule/" securityRuleContainerPath = "/network/v1/secrule/"
SecurityRuleResourcePath = "/network/v1/secrule" securityRuleResourcePath = "/network/v1/secrule"
) )
// SecurityRuleClient defines the security rule client
type SecurityRuleClient struct { type SecurityRuleClient struct {
ResourceClient ResourceClient
} }
// SecurityRules() returns an SecurityRulesClient that can be used to access the // SecurityRules returns an SecurityRulesClient that can be used to access the
// necessary CRUD functions for Security Rules. // necessary CRUD functions for Security Rules.
func (c *ComputeClient) SecurityRules() *SecurityRuleClient { func (c *Client) SecurityRules() *SecurityRuleClient {
return &SecurityRuleClient{ return &SecurityRuleClient{
ResourceClient: ResourceClient{ ResourceClient: ResourceClient{
ComputeClient: c, Client: c,
ResourceDescription: SecurityRuleDescription, ResourceDescription: securityRuleDescription,
ContainerPath: SecurityRuleContainerPath, ContainerPath: securityRuleContainerPath,
ResourceRootPath: SecurityRuleResourcePath, ResourceRootPath: securityRuleResourcePath,
}, },
} }
} }
@ -31,27 +32,30 @@ type SecurityRuleInfo struct {
// Description of the Security Rule // Description of the Security Rule
Description string `json:"description"` Description string `json:"description"`
// List of IP address prefix set names to match the packet's destination IP address. // List of IP address prefix set names to match the packet's destination IP address.
DstIpAddressPrefixSets []string `json:"dstIpAddressPrefixSets"` DstIPAddressPrefixSets []string `json:"dstIpAddressPrefixSets"`
// Name of virtual NIC set containing the packet's destination virtual NIC. // Name of virtual NIC set containing the packet's destination virtual NIC.
DstVnicSet string `json:"dstVnicSet"` DstVnicSet string `json:"dstVnicSet"`
// Allows the security rule to be disabled. // Allows the security rule to be disabled.
Enabled bool `json:"enabledFlag"` Enabled bool `json:"enabledFlag"`
// Direction of the flow; Can be "egress" or "ingress". // Direction of the flow; Can be "egress" or "ingress".
FlowDirection string `json:"FlowDirection"` FlowDirection string `json:"FlowDirection"`
// Fully Qualified Domain Name
FQDN string `json:"name"`
// The name of the Security Rule // The name of the Security Rule
Name string `json:"name"` Name string
// List of security protocol names to match the packet's protocol and port. // List of security protocol names to match the packet's protocol and port.
SecProtocols []string `json:"secProtocols"` SecProtocols []string `json:"secProtocols"`
// List of multipart names of IP address prefix set to match the packet's source IP address. // List of multipart names of IP address prefix set to match the packet's source IP address.
SrcIpAddressPrefixSets []string `json:"srcIpAddressPrefixSets"` SrcIPAddressPrefixSets []string `json:"srcIpAddressPrefixSets"`
// Name of virtual NIC set containing the packet's source virtual NIC. // Name of virtual NIC set containing the packet's source virtual NIC.
SrcVnicSet string `json:"srcVnicSet"` SrcVnicSet string `json:"srcVnicSet"`
// Slice of tags associated with the Security Rule // Slice of tags associated with the Security Rule
Tags []string `json:"tags"` Tags []string `json:"tags"`
// Uniform Resource Identifier for the Security Rule // Uniform Resource Identifier for the Security Rule
Uri string `json:"uri"` URI string `json:"uri"`
} }
// CreateSecurityRuleInput defines the attributes needed to create a security rule
type CreateSecurityRuleInput struct { type CreateSecurityRuleInput struct {
//Select the name of the access control list (ACL) that you want to add this //Select the name of the access control list (ACL) that you want to add this
// security rule to. Security rules are applied to vNIC sets by using ACLs. // security rule to. Security rules are applied to vNIC sets by using ACLs.
@ -67,7 +71,7 @@ type CreateSecurityRuleInput struct {
// When no destination IP address prefix sets are specified, traffic to any // When no destination IP address prefix sets are specified, traffic to any
// IP address is permitted. // IP address is permitted.
// Optional // Optional
DstIpAddressPrefixSets []string `json:"dstIpAddressPrefixSets"` DstIPAddressPrefixSets []string `json:"dstIpAddressPrefixSets"`
// The vNICset to which you want to permit traffic. Only packets to vNICs in the // The vNICset to which you want to permit traffic. Only packets to vNICs in the
// specified vNICset are permitted. When no destination vNICset is specified, traffic // specified vNICset are permitted. When no destination vNICset is specified, traffic
@ -108,7 +112,7 @@ type CreateSecurityRuleInput struct {
// from IP addresses in the specified IP address prefix sets are permitted. When no source // from IP addresses in the specified IP address prefix sets are permitted. When no source
// IP address prefix sets are specified, traffic from any IP address is permitted. // IP address prefix sets are specified, traffic from any IP address is permitted.
// Optional // Optional
SrcIpAddressPrefixSets []string `json:"srcIpAddressPrefixSets"` SrcIPAddressPrefixSets []string `json:"srcIpAddressPrefixSets"`
// The vNICset from which you want to permit traffic. Only packets from vNICs in the // The vNICset from which you want to permit traffic. Only packets from vNICs in the
// specified vNICset are permitted. When no source vNICset is specified, traffic from any // specified vNICset are permitted. When no source vNICset is specified, traffic from any
@ -121,15 +125,15 @@ type CreateSecurityRuleInput struct {
Tags []string `json:"tags"` Tags []string `json:"tags"`
} }
// Create a new Security Rule from an SecurityRuleClient and an input struct. // CreateSecurityRule creates a new Security Rule from an SecurityRuleClient and an input struct.
// Returns a populated Info struct for the Security Rule, and any errors // Returns a populated Info struct for the Security Rule, and any errors
func (c *SecurityRuleClient) CreateSecurityRule(input *CreateSecurityRuleInput) (*SecurityRuleInfo, error) { func (c *SecurityRuleClient) CreateSecurityRule(input *CreateSecurityRuleInput) (*SecurityRuleInfo, error) {
input.Name = c.getQualifiedName(input.Name) input.Name = c.getQualifiedName(input.Name)
input.ACL = c.getQualifiedName(input.ACL) input.ACL = c.getQualifiedName(input.ACL)
input.SrcVnicSet = c.getQualifiedName(input.SrcVnicSet) input.SrcVnicSet = c.getQualifiedName(input.SrcVnicSet)
input.DstVnicSet = c.getQualifiedName(input.DstVnicSet) input.DstVnicSet = c.getQualifiedName(input.DstVnicSet)
input.SrcIpAddressPrefixSets = c.getQualifiedList(input.SrcIpAddressPrefixSets) input.SrcIPAddressPrefixSets = c.getQualifiedList(input.SrcIPAddressPrefixSets)
input.DstIpAddressPrefixSets = c.getQualifiedList(input.DstIpAddressPrefixSets) input.DstIPAddressPrefixSets = c.getQualifiedList(input.DstIPAddressPrefixSets)
input.SecProtocols = c.getQualifiedList(input.SecProtocols) input.SecProtocols = c.getQualifiedList(input.SecProtocols)
var securityRuleInfo SecurityRuleInfo var securityRuleInfo SecurityRuleInfo
@ -140,13 +144,14 @@ func (c *SecurityRuleClient) CreateSecurityRule(input *CreateSecurityRuleInput)
return c.success(&securityRuleInfo) return c.success(&securityRuleInfo)
} }
// GetSecurityRuleInput defines which security rule to obtain
type GetSecurityRuleInput struct { type GetSecurityRuleInput struct {
// The name of the Security Rule to query for. Case-sensitive // The name of the Security Rule to query for. Case-sensitive
// Required // Required
Name string `json:"name"` Name string `json:"name"`
} }
// Returns a populated SecurityRuleInfo struct from an input struct // GetSecurityRule returns a populated SecurityRuleInfo struct from an input struct
func (c *SecurityRuleClient) GetSecurityRule(input *GetSecurityRuleInput) (*SecurityRuleInfo, error) { func (c *SecurityRuleClient) GetSecurityRule(input *GetSecurityRuleInput) (*SecurityRuleInfo, error) {
input.Name = c.getQualifiedName(input.Name) input.Name = c.getQualifiedName(input.Name)
@ -174,7 +179,7 @@ type UpdateSecurityRuleInput struct {
// When no destination IP address prefix sets are specified, traffic to any // When no destination IP address prefix sets are specified, traffic to any
// IP address is permitted. // IP address is permitted.
// Optional // Optional
DstIpAddressPrefixSets []string `json:"dstIpAddressPrefixSets"` DstIPAddressPrefixSets []string `json:"dstIpAddressPrefixSets"`
// The vNICset to which you want to permit traffic. Only packets to vNICs in the // The vNICset to which you want to permit traffic. Only packets to vNICs in the
// specified vNICset are permitted. When no destination vNICset is specified, traffic // specified vNICset are permitted. When no destination vNICset is specified, traffic
@ -215,7 +220,7 @@ type UpdateSecurityRuleInput struct {
// from IP addresses in the specified IP address prefix sets are permitted. When no source // from IP addresses in the specified IP address prefix sets are permitted. When no source
// IP address prefix sets are specified, traffic from any IP address is permitted. // IP address prefix sets are specified, traffic from any IP address is permitted.
// Optional // Optional
SrcIpAddressPrefixSets []string `json:"srcIpAddressPrefixSets"` SrcIPAddressPrefixSets []string `json:"srcIpAddressPrefixSets"`
// The vNICset from which you want to permit traffic. Only packets from vNICs in the // The vNICset from which you want to permit traffic. Only packets from vNICs in the
// specified vNICset are permitted. When no source vNICset is specified, traffic from any // specified vNICset are permitted. When no source vNICset is specified, traffic from any
@ -228,14 +233,14 @@ type UpdateSecurityRuleInput struct {
Tags []string `json:"tags"` Tags []string `json:"tags"`
} }
// UpdateSecRule modifies the properties of the sec rule with the given name. // UpdateSecurityRule modifies the properties of the sec rule with the given name.
func (c *SecurityRuleClient) UpdateSecurityRule(updateInput *UpdateSecurityRuleInput) (*SecurityRuleInfo, error) { func (c *SecurityRuleClient) UpdateSecurityRule(updateInput *UpdateSecurityRuleInput) (*SecurityRuleInfo, error) {
updateInput.Name = c.getQualifiedName(updateInput.Name) updateInput.Name = c.getQualifiedName(updateInput.Name)
updateInput.ACL = c.getQualifiedName(updateInput.ACL) updateInput.ACL = c.getQualifiedName(updateInput.ACL)
updateInput.SrcVnicSet = c.getQualifiedName(updateInput.SrcVnicSet) updateInput.SrcVnicSet = c.getQualifiedName(updateInput.SrcVnicSet)
updateInput.DstVnicSet = c.getQualifiedName(updateInput.DstVnicSet) updateInput.DstVnicSet = c.getQualifiedName(updateInput.DstVnicSet)
updateInput.SrcIpAddressPrefixSets = c.getQualifiedList(updateInput.SrcIpAddressPrefixSets) updateInput.SrcIPAddressPrefixSets = c.getQualifiedList(updateInput.SrcIPAddressPrefixSets)
updateInput.DstIpAddressPrefixSets = c.getQualifiedList(updateInput.DstIpAddressPrefixSets) updateInput.DstIPAddressPrefixSets = c.getQualifiedList(updateInput.DstIPAddressPrefixSets)
updateInput.SecProtocols = c.getQualifiedList(updateInput.SecProtocols) updateInput.SecProtocols = c.getQualifiedList(updateInput.SecProtocols)
var securityRuleInfo SecurityRuleInfo var securityRuleInfo SecurityRuleInfo
@ -246,21 +251,24 @@ func (c *SecurityRuleClient) UpdateSecurityRule(updateInput *UpdateSecurityRuleI
return c.success(&securityRuleInfo) return c.success(&securityRuleInfo)
} }
// DeleteSecurityRuleInput specifies which security rule to delete
type DeleteSecurityRuleInput struct { type DeleteSecurityRuleInput struct {
// The name of the Security Rule to query for. Case-sensitive // The name of the Security Rule to query for. Case-sensitive
// Required // Required
Name string `json:"name"` Name string `json:"name"`
} }
// DeleteSecurityRule deletes the specifies security rule
func (c *SecurityRuleClient) DeleteSecurityRule(input *DeleteSecurityRuleInput) error { func (c *SecurityRuleClient) DeleteSecurityRule(input *DeleteSecurityRuleInput) error {
return c.deleteResource(input.Name) return c.deleteResource(input.Name)
} }
// Unqualifies any qualified fields in the IPNetworkExchangeInfo struct // Unqualifies any qualified fields in the IPNetworkExchangeInfo struct
func (c *SecurityRuleClient) success(info *SecurityRuleInfo) (*SecurityRuleInfo, error) { func (c *SecurityRuleClient) success(info *SecurityRuleInfo) (*SecurityRuleInfo, error) {
c.unqualify(&info.Name, &info.ACL, &info.SrcVnicSet, &info.DstVnicSet) info.Name = c.getUnqualifiedName(info.FQDN)
info.SrcIpAddressPrefixSets = c.getUnqualifiedList(info.SrcIpAddressPrefixSets) c.unqualify(&info.ACL, &info.SrcVnicSet, &info.DstVnicSet)
info.DstIpAddressPrefixSets = c.getUnqualifiedList(info.DstIpAddressPrefixSets) info.SrcIPAddressPrefixSets = c.getUnqualifiedList(info.SrcIPAddressPrefixSets)
info.DstIPAddressPrefixSets = c.getUnqualifiedList(info.DstIPAddressPrefixSets)
info.SecProtocols = c.getUnqualifiedList(info.SecProtocols) info.SecProtocols = c.getUnqualifiedList(info.SecProtocols)
return info, nil return info, nil
} }

View File

@ -5,7 +5,8 @@ import (
"time" "time"
) )
const WaitForSnapshotCompleteTimeout = time.Duration(600 * time.Second) const waitForSnapshotCompletePollInterval = 30 * time.Second
const waitForSnapshotCompleteTimeout = 600 * time.Second
// SnapshotsClient is a client for the Snapshot functions of the Compute API. // SnapshotsClient is a client for the Snapshot functions of the Compute API.
type SnapshotsClient struct { type SnapshotsClient struct {
@ -14,32 +15,39 @@ type SnapshotsClient struct {
// Snapshots obtains an SnapshotsClient which can be used to access to the // Snapshots obtains an SnapshotsClient which can be used to access to the
// Snapshot functions of the Compute API // Snapshot functions of the Compute API
func (c *ComputeClient) Snapshots() *SnapshotsClient { func (c *Client) Snapshots() *SnapshotsClient {
return &SnapshotsClient{ return &SnapshotsClient{
ResourceClient: ResourceClient{ ResourceClient: ResourceClient{
ComputeClient: c, Client: c,
ResourceDescription: "Snapshot", ResourceDescription: "Snapshot",
ContainerPath: "/snapshot/", ContainerPath: "/snapshot/",
ResourceRootPath: "/snapshot", ResourceRootPath: "/snapshot",
}} }}
} }
// SnapshotState defines the constant states a snapshot can be in
type SnapshotState string type SnapshotState string
const ( const (
SnapshotActive SnapshotState = "active" // SnapshotActive - active
SnapshotActive SnapshotState = "active"
// SnapshotComplete - complete
SnapshotComplete SnapshotState = "complete" SnapshotComplete SnapshotState = "complete"
SnapshotQueued SnapshotState = "queued" // SnapshotQueued - queued
SnapshotError SnapshotState = "error" SnapshotQueued SnapshotState = "queued"
// SnapshotError - error
SnapshotError SnapshotState = "error"
) )
// SnapshotDelay defines the constant values snapshot delay can be
type SnapshotDelay string type SnapshotDelay string
const ( const (
// SnapshotDelayShutdown - shutdown
SnapshotDelayShutdown SnapshotDelay = "shutdown" SnapshotDelayShutdown SnapshotDelay = "shutdown"
) )
// SnapshotInfo describes an existing Snapshot. // Snapshot describes an existing Snapshot.
type Snapshot struct { type Snapshot struct {
// Shows the default account for your identity domain. // Shows the default account for your identity domain.
Account string `json:"account"` Account string `json:"account"`
@ -49,12 +57,14 @@ type Snapshot struct {
Delay SnapshotDelay `json:"delay"` Delay SnapshotDelay `json:"delay"`
// A description of the reason this request entered "error" state. // A description of the reason this request entered "error" state.
ErrorReason string `json:"error_reason"` ErrorReason string `json:"error_reason"`
// Fully Qualified Domain Name
FQDN string `json:"name"`
// Name of the instance // Name of the instance
Instance string `json:"instance"` Instance string `json:"instance"`
// Name of the machine image generated from the instance snapshot request. // Name of the machine image generated from the instance snapshot request.
MachineImage string `json:"machineimage"` MachineImage string `json:"machineimage"`
// Name of the instance snapshot request. // Name of the instance snapshot request.
Name string `json:"name"` Name string
// Not used // Not used
Quota string `json:"quota"` Quota string `json:"quota"`
// The state of the request. // The state of the request.
@ -88,6 +98,8 @@ type CreateSnapshotInput struct {
// If you don't specify a name for this object, then the name is generated automatically. // If you don't specify a name for this object, then the name is generated automatically.
// Optional // Optional
MachineImage string `json:"machineimage,omitempty"` MachineImage string `json:"machineimage,omitempty"`
// Time to wait between polling snapshot status
PollInterval time.Duration `json:"-"`
// Time to wait for snapshot to be completed // Time to wait for snapshot to be completed
Timeout time.Duration `json:"-"` Timeout time.Duration `json:"-"`
} }
@ -108,12 +120,15 @@ func (c *SnapshotsClient) CreateSnapshot(input *CreateSnapshotInput) (*Snapshot,
Name: snapshotInfo.Name, Name: snapshotInfo.Name,
} }
if input.PollInterval == 0 {
input.PollInterval = waitForSnapshotCompletePollInterval
}
if input.Timeout == 0 { if input.Timeout == 0 {
input.Timeout = WaitForSnapshotCompleteTimeout input.Timeout = waitForSnapshotCompleteTimeout
} }
// Wait for snapshot to be complete and return the result // Wait for snapshot to be complete and return the result
return c.WaitForSnapshotComplete(getInput, input.Timeout) return c.WaitForSnapshotComplete(getInput, input.PollInterval, input.Timeout)
} }
// GetSnapshotInput describes the snapshot to get // GetSnapshotInput describes the snapshot to get
@ -142,6 +157,8 @@ type DeleteSnapshotInput struct {
// The name of the machine image // The name of the machine image
// Required // Required
MachineImage string MachineImage string
// Time to wait between polls to check snapshot status
PollInterval time.Duration
// Time to wait for snapshot to be deleted // Time to wait for snapshot to be deleted
Timeout time.Duration Timeout time.Duration
} }
@ -154,11 +171,14 @@ func (c *SnapshotsClient) DeleteSnapshot(machineImagesClient *MachineImagesClien
Name: input.Snapshot, Name: input.Snapshot,
} }
if input.PollInterval == 0 {
input.PollInterval = waitForSnapshotCompletePollInterval
}
if input.Timeout == 0 { if input.Timeout == 0 {
input.Timeout = WaitForSnapshotCompleteTimeout input.Timeout = waitForSnapshotCompleteTimeout
} }
if _, err := c.WaitForSnapshotComplete(getInput, input.Timeout); err != nil { if _, err := c.WaitForSnapshotComplete(getInput, input.PollInterval, input.Timeout); err != nil {
return fmt.Errorf("Could not delete snapshot: %s", err) return fmt.Errorf("Could not delete snapshot: %s", err)
} }
@ -186,11 +206,14 @@ func (c *SnapshotsClient) DeleteSnapshotResourceOnly(input *DeleteSnapshotInput)
Name: input.Snapshot, Name: input.Snapshot,
} }
if input.PollInterval == 0 {
input.PollInterval = waitForSnapshotCompletePollInterval
}
if input.Timeout == 0 { if input.Timeout == 0 {
input.Timeout = WaitForSnapshotCompleteTimeout input.Timeout = waitForSnapshotCompleteTimeout
} }
if _, err := c.WaitForSnapshotComplete(getInput, input.Timeout); err != nil { if _, err := c.WaitForSnapshotComplete(getInput, input.PollInterval, input.Timeout); err != nil {
return fmt.Errorf("Could not delete snapshot: %s", err) return fmt.Errorf("Could not delete snapshot: %s", err)
} }
@ -202,10 +225,10 @@ func (c *SnapshotsClient) DeleteSnapshotResourceOnly(input *DeleteSnapshotInput)
} }
// WaitForSnapshotComplete waits for an snapshot to be completely initialized and available. // WaitForSnapshotComplete waits for an snapshot to be completely initialized and available.
func (c *SnapshotsClient) WaitForSnapshotComplete(input *GetSnapshotInput, timeout time.Duration) (*Snapshot, error) { func (c *SnapshotsClient) WaitForSnapshotComplete(input *GetSnapshotInput, pollInterval, timeout time.Duration) (*Snapshot, error) {
var info *Snapshot var info *Snapshot
var getErr error var getErr error
err := c.client.WaitFor("snapshot to be complete", timeout, func() (bool, error) { err := c.client.WaitFor("snapshot to be complete", pollInterval, timeout, func() (bool, error) {
info, getErr = c.GetSnapshot(input) info, getErr = c.GetSnapshot(input)
if getErr != nil { if getErr != nil {
return false, getErr return false, getErr
@ -237,6 +260,6 @@ func (c *SnapshotsClient) success(snapshotInfo *Snapshot) (*Snapshot, error) {
c.unqualify(&snapshotInfo.Account) c.unqualify(&snapshotInfo.Account)
c.unqualify(&snapshotInfo.Instance) c.unqualify(&snapshotInfo.Instance)
c.unqualify(&snapshotInfo.MachineImage) c.unqualify(&snapshotInfo.MachineImage)
c.unqualify(&snapshotInfo.Name) snapshotInfo.Name = c.getUnqualifiedName(snapshotInfo.FQDN)
return snapshotInfo, nil return snapshotInfo, nil
} }

View File

@ -7,24 +7,26 @@ type SSHKeysClient struct {
// SSHKeys obtains an SSHKeysClient which can be used to access to the // SSHKeys obtains an SSHKeysClient which can be used to access to the
// SSH key functions of the Compute API // SSH key functions of the Compute API
func (c *ComputeClient) SSHKeys() *SSHKeysClient { func (c *Client) SSHKeys() *SSHKeysClient {
return &SSHKeysClient{ return &SSHKeysClient{
ResourceClient: ResourceClient{ ResourceClient: ResourceClient{
ComputeClient: c, Client: c,
ResourceDescription: "SSH key", ResourceDescription: "SSH key",
ContainerPath: "/sshkey/", ContainerPath: "/sshkey/",
ResourceRootPath: "/sshkey", ResourceRootPath: "/sshkey",
}} }}
} }
// SSHKeyInfo describes an existing SSH key. // SSHKey describes an existing SSH key.
type SSHKey struct { type SSHKey struct {
// Indicates whether the key is enabled (true) or disabled. // Indicates whether the key is enabled (true) or disabled.
Enabled bool `json:"enabled"` Enabled bool `json:"enabled"`
// Fully Qualified Domain Name
FQDN string `json:"name"`
// The SSH public key value. // The SSH public key value.
Key string `json:"key"` Key string `json:"key"`
// The three-part name of the SSH Key (/Compute-identity_domain/user/object). // The three-part name of the SSH Key (/Compute-identity_domain/user/object).
Name string `json:"name"` Name string
// Unique Resource Identifier // Unique Resource Identifier
URI string `json:"uri"` URI string `json:"uri"`
} }
@ -107,7 +109,7 @@ func (c *SSHKeysClient) UpdateSSHKey(updateInput *UpdateSSHKeyInput) (*SSHKey, e
return c.success(&keyInfo) return c.success(&keyInfo)
} }
// DeleteKeyInput describes the ssh key to delete // DeleteSSHKeyInput describes the ssh key to delete
type DeleteSSHKeyInput struct { type DeleteSSHKeyInput struct {
// The three-part name of the SSH Key (/Compute-identity_domain/user/object). // The three-part name of the SSH Key (/Compute-identity_domain/user/object).
Name string `json:"name"` Name string `json:"name"`
@ -119,6 +121,6 @@ func (c *SSHKeysClient) DeleteSSHKey(deleteInput *DeleteSSHKeyInput) error {
} }
func (c *SSHKeysClient) success(keyInfo *SSHKey) (*SSHKey, error) { func (c *SSHKeysClient) success(keyInfo *SSHKey) (*SSHKey, error) {
c.unqualify(&keyInfo.Name) keyInfo.Name = c.getUnqualifiedName(keyInfo.FQDN)
return keyInfo, nil return keyInfo, nil
} }

View File

@ -6,8 +6,10 @@ import (
"github.com/hashicorp/go-oracle-terraform/client" "github.com/hashicorp/go-oracle-terraform/client"
) )
const WaitForVolumeAttachmentDeleteTimeout = time.Duration(30 * time.Second) const waitForVolumeAttachmentDeletePollInterval = 1 * time.Second
const WaitForVolumeAttachmentReadyTimeout = time.Duration(30 * time.Second) const waitForVolumeAttachmentDeleteTimeout = 30 * time.Second
const waitForVolumeAttachmentReadyPollInterval = 1 * time.Second
const waitForVolumeAttachmentReadyTimeout = 30 * time.Second
// StorageAttachmentsClient is a client for the Storage Attachment functions of the Compute API. // StorageAttachmentsClient is a client for the Storage Attachment functions of the Compute API.
type StorageAttachmentsClient struct { type StorageAttachmentsClient struct {
@ -16,30 +18,39 @@ type StorageAttachmentsClient struct {
// StorageAttachments obtains a StorageAttachmentsClient which can be used to access to the // StorageAttachments obtains a StorageAttachmentsClient which can be used to access to the
// Storage Attachment functions of the Compute API // Storage Attachment functions of the Compute API
func (c *ComputeClient) StorageAttachments() *StorageAttachmentsClient { func (c *Client) StorageAttachments() *StorageAttachmentsClient {
return &StorageAttachmentsClient{ return &StorageAttachmentsClient{
ResourceClient: ResourceClient{ ResourceClient: ResourceClient{
ComputeClient: c, Client: c,
ResourceDescription: "storage volume attachment", ResourceDescription: "storage volume attachment",
ContainerPath: "/storage/attachment/", ContainerPath: "/storage/attachment/",
ResourceRootPath: "/storage/attachment", ResourceRootPath: "/storage/attachment",
}} }}
} }
// StorageAttachmentState defines all the storage attachment states
type StorageAttachmentState string type StorageAttachmentState string
const ( const (
Attaching StorageAttachmentState = "attaching" // Attaching - attaching
Attached StorageAttachmentState = "attached" Attaching StorageAttachmentState = "attaching"
Detaching StorageAttachmentState = "detaching" // Attached - attached
Attached StorageAttachmentState = "attached"
// Detaching - detaching
Detaching StorageAttachmentState = "detaching"
// Unavailable - unavailable
Unavailable StorageAttachmentState = "unavailable" Unavailable StorageAttachmentState = "unavailable"
Unknown StorageAttachmentState = "unknown" // Unknown - unkown
Unknown StorageAttachmentState = "unknown"
) )
// StorageAttachmentInfo describes an existing storage attachment. // StorageAttachmentInfo describes an existing storage attachment.
type StorageAttachmentInfo struct { type StorageAttachmentInfo struct {
// Fully Qualified Domain Name
FQDN string `json:"name"`
// Name of this attachment, generated by the server. // Name of this attachment, generated by the server.
Name string `json:"name"` Name string
// Index number for the volume. The allowed range is 1-10 // Index number for the volume. The allowed range is 1-10
// An attachment with index 1 is exposed to the instance as /dev/xvdb, an attachment with index 2 is exposed as /dev/xvdc, and so on. // An attachment with index 1 is exposed to the instance as /dev/xvdb, an attachment with index 2 is exposed as /dev/xvdc, and so on.
@ -56,10 +67,12 @@ type StorageAttachmentInfo struct {
} }
func (c *StorageAttachmentsClient) success(attachmentInfo *StorageAttachmentInfo) (*StorageAttachmentInfo, error) { func (c *StorageAttachmentsClient) success(attachmentInfo *StorageAttachmentInfo) (*StorageAttachmentInfo, error) {
c.unqualify(&attachmentInfo.Name, &attachmentInfo.InstanceName, &attachmentInfo.StorageVolumeName) attachmentInfo.Name = c.getQualifiedName(attachmentInfo.FQDN)
c.unqualify(&attachmentInfo.InstanceName, &attachmentInfo.StorageVolumeName)
return attachmentInfo, nil return attachmentInfo, nil
} }
// CreateStorageAttachmentInput defines the attributes to create a storage attachment
type CreateStorageAttachmentInput struct { type CreateStorageAttachmentInput struct {
// Index number for the volume. The allowed range is 1-10 // Index number for the volume. The allowed range is 1-10
// An attachment with index 1 is exposed to the instance as /dev/xvdb, an attachment with index 2 is exposed as /dev/xvdc, and so on. // An attachment with index 1 is exposed to the instance as /dev/xvdb, an attachment with index 2 is exposed as /dev/xvdc, and so on.
@ -74,6 +87,9 @@ type CreateStorageAttachmentInput struct {
// Required. // Required.
StorageVolumeName string `json:"storage_volume_name"` StorageVolumeName string `json:"storage_volume_name"`
// Time to wait between polls to check volume attachment status
PollInterval time.Duration `json:"-"`
// Time to wait for storage volume attachment // Time to wait for storage volume attachment
Timeout time.Duration `json:"-"` Timeout time.Duration `json:"-"`
} }
@ -88,11 +104,14 @@ func (c *StorageAttachmentsClient) CreateStorageAttachment(input *CreateStorageA
return nil, err return nil, err
} }
if input.PollInterval == 0 {
input.PollInterval = waitForVolumeAttachmentReadyPollInterval
}
if input.Timeout == 0 { if input.Timeout == 0 {
input.Timeout = WaitForVolumeAttachmentReadyTimeout input.Timeout = waitForVolumeAttachmentReadyTimeout
} }
return c.waitForStorageAttachmentToFullyAttach(attachmentInfo.Name, input.Timeout) return c.waitForStorageAttachmentToFullyAttach(attachmentInfo.Name, input.PollInterval, input.Timeout)
} }
// DeleteStorageAttachmentInput represents the body of an API request to delete a Storage Attachment. // DeleteStorageAttachmentInput represents the body of an API request to delete a Storage Attachment.
@ -101,6 +120,9 @@ type DeleteStorageAttachmentInput struct {
// Required // Required
Name string `json:"name"` Name string `json:"name"`
// Time to wait between polls to check volume attachment status
PollInterval time.Duration `json:"-"`
// Time to wait for storage volume snapshot // Time to wait for storage volume snapshot
Timeout time.Duration `json:"-"` Timeout time.Duration `json:"-"`
} }
@ -111,11 +133,14 @@ func (c *StorageAttachmentsClient) DeleteStorageAttachment(input *DeleteStorageA
return err return err
} }
if input.PollInterval == 0 {
input.PollInterval = waitForVolumeAttachmentDeletePollInterval
}
if input.Timeout == 0 { if input.Timeout == 0 {
input.Timeout = WaitForVolumeAttachmentDeleteTimeout input.Timeout = waitForVolumeAttachmentDeleteTimeout
} }
return c.waitForStorageAttachmentToBeDeleted(input.Name, input.Timeout) return c.waitForStorageAttachmentToBeDeleted(input.Name, input.PollInterval, input.Timeout)
} }
// GetStorageAttachmentInput represents the body of an API request to obtain a Storage Attachment. // GetStorageAttachmentInput represents the body of an API request to obtain a Storage Attachment.
@ -136,10 +161,10 @@ func (c *StorageAttachmentsClient) GetStorageAttachment(input *GetStorageAttachm
} }
// waitForStorageAttachmentToFullyAttach waits for the storage attachment with the given name to be fully attached, or times out. // waitForStorageAttachmentToFullyAttach waits for the storage attachment with the given name to be fully attached, or times out.
func (c *StorageAttachmentsClient) waitForStorageAttachmentToFullyAttach(name string, timeout time.Duration) (*StorageAttachmentInfo, error) { func (c *StorageAttachmentsClient) waitForStorageAttachmentToFullyAttach(name string, pollInterval, timeout time.Duration) (*StorageAttachmentInfo, error) {
var waitResult *StorageAttachmentInfo var waitResult *StorageAttachmentInfo
err := c.client.WaitFor("storage attachment to be attached", timeout, func() (bool, error) { err := c.client.WaitFor("storage attachment to be attached", pollInterval, timeout, func() (bool, error) {
input := &GetStorageAttachmentInput{ input := &GetStorageAttachmentInput{
Name: name, Name: name,
} }
@ -162,8 +187,8 @@ func (c *StorageAttachmentsClient) waitForStorageAttachmentToFullyAttach(name st
} }
// waitForStorageAttachmentToBeDeleted waits for the storage attachment with the given name to be fully deleted, or times out. // waitForStorageAttachmentToBeDeleted waits for the storage attachment with the given name to be fully deleted, or times out.
func (c *StorageAttachmentsClient) waitForStorageAttachmentToBeDeleted(name string, timeout time.Duration) error { func (c *StorageAttachmentsClient) waitForStorageAttachmentToBeDeleted(name string, pollInterval, timeout time.Duration) error {
return c.client.WaitFor("storage attachment to be deleted", timeout, func() (bool, error) { return c.client.WaitFor("storage attachment to be deleted", pollInterval, timeout, func() (bool, error) {
input := &GetStorageAttachmentInput{ input := &GetStorageAttachmentInput{
Name: name, Name: name,
} }

View File

@ -9,14 +9,16 @@ import (
) )
const ( const (
StorageVolumeSnapshotDescription = "storage volume snapshot" storageVolumeSnapshotDescription = "storage volume snapshot"
StorageVolumeSnapshotContainerPath = "/storage/snapshot/" storageVolumeSnapshotContainerPath = "/storage/snapshot/"
StorageVolumeSnapshotResourcePath = "/storage/snapshot" storageVolumeSnapshotResourcePath = "/storage/snapshot"
WaitForSnapshotCreateTimeout = time.Duration(2400 * time.Second) waitForSnapshotCreatePollInterval = 30 * time.Second
WaitForSnapshotDeleteTimeout = time.Duration(1500 * time.Second) waitForSnapshotCreateTimeout = 2400 * time.Second
waitForSnapshotDeletePollInterval = 30 * time.Second
waitForSnapshotDeleteTimeout = 1500 * time.Second
// Collocated Snapshot Property // SnapshotPropertyCollocated - Collocated Snapshot Property
SnapshotPropertyCollocated = "/oracle/private/storage/snapshot/collocated" SnapshotPropertyCollocated = "/oracle/private/storage/snapshot/collocated"
) )
@ -25,13 +27,14 @@ type StorageVolumeSnapshotClient struct {
ResourceClient ResourceClient
} }
func (c *ComputeClient) StorageVolumeSnapshots() *StorageVolumeSnapshotClient { // StorageVolumeSnapshots returns a storage volume snapshot client
func (c *Client) StorageVolumeSnapshots() *StorageVolumeSnapshotClient {
return &StorageVolumeSnapshotClient{ return &StorageVolumeSnapshotClient{
ResourceClient: ResourceClient{ ResourceClient: ResourceClient{
ComputeClient: c, Client: c,
ResourceDescription: StorageVolumeSnapshotDescription, ResourceDescription: storageVolumeSnapshotDescription,
ContainerPath: StorageVolumeSnapshotContainerPath, ContainerPath: storageVolumeSnapshotContainerPath,
ResourceRootPath: StorageVolumeSnapshotResourcePath, ResourceRootPath: storageVolumeSnapshotResourcePath,
}, },
} }
} }
@ -44,11 +47,14 @@ type StorageVolumeSnapshotInfo struct {
// Description of the snapshot // Description of the snapshot
Description string `json:"description"` Description string `json:"description"`
// Fully Qualified Domain Name
FQDN string `json:"name"`
// The name of the machine image that's used in the boot volume from which this snapshot is taken // The name of the machine image that's used in the boot volume from which this snapshot is taken
MachineImageName string `json:"machineimage_name"` MachineImageName string `json:"machineimage_name"`
// Name of the snapshot // Name of the snapshot
Name string `json:"name"` Name string
// String indicating whether the parent volume is bootable or not // String indicating whether the parent volume is bootable or not
ParentVolumeBootable string `json:"parent_volume_bootable"` ParentVolumeBootable string `json:"parent_volume_bootable"`
@ -116,6 +122,9 @@ type CreateStorageVolumeSnapshotInput struct {
// Required // Required
Volume string `json:"volume"` Volume string `json:"volume"`
// Timeout to wait between polling snapshot status. Will use default if unspecified
PollInterval time.Duration `json:"-"`
// Timeout to wait for snapshot to be completed. Will use default if unspecified // Timeout to wait for snapshot to be completed. Will use default if unspecified
Timeout time.Duration `json:"-"` Timeout time.Duration `json:"-"`
} }
@ -132,12 +141,15 @@ func (c *StorageVolumeSnapshotClient) CreateStorageVolumeSnapshot(input *CreateS
return nil, err return nil, err
} }
if input.PollInterval == 0 {
input.PollInterval = waitForSnapshotCreatePollInterval
}
if input.Timeout == 0 { if input.Timeout == 0 {
input.Timeout = WaitForSnapshotCreateTimeout input.Timeout = waitForSnapshotCreateTimeout
} }
// The name of the snapshot could have been generated. Use the response name as input // The name of the snapshot could have been generated. Use the response name as input
return c.waitForStorageSnapshotAvailable(storageSnapshotInfo.Name, input.Timeout) return c.waitForStorageSnapshotAvailable(storageSnapshotInfo.Name, input.PollInterval, input.Timeout)
} }
// GetStorageVolumeSnapshotInput represents the body of an API request to get information on a storage volume snapshot // GetStorageVolumeSnapshotInput represents the body of an API request to get information on a storage volume snapshot
@ -165,11 +177,14 @@ type DeleteStorageVolumeSnapshotInput struct {
// Name of the snapshot to delete // Name of the snapshot to delete
Name string `json:"name"` Name string `json:"name"`
// Timeout to wait between polling snapshot status, will use default if unspecified
PollInterval time.Duration `json:"-"`
// Timeout to wait for deletion, will use default if unspecified // Timeout to wait for deletion, will use default if unspecified
Timeout time.Duration `json:"-"` Timeout time.Duration `json:"-"`
} }
// DeleteStoragevolumeSnapshot makes an API request to delete a storage volume snapshot // DeleteStorageVolumeSnapshot makes an API request to delete a storage volume snapshot
func (c *StorageVolumeSnapshotClient) DeleteStorageVolumeSnapshot(input *DeleteStorageVolumeSnapshotInput) error { func (c *StorageVolumeSnapshotClient) DeleteStorageVolumeSnapshot(input *DeleteStorageVolumeSnapshotInput) error {
input.Name = c.getQualifiedName(input.Name) input.Name = c.getQualifiedName(input.Name)
@ -177,15 +192,18 @@ func (c *StorageVolumeSnapshotClient) DeleteStorageVolumeSnapshot(input *DeleteS
return err return err
} }
if input.PollInterval == 0 {
input.PollInterval = waitForSnapshotDeletePollInterval
}
if input.Timeout == 0 { if input.Timeout == 0 {
input.Timeout = WaitForSnapshotDeleteTimeout input.Timeout = waitForSnapshotDeleteTimeout
} }
return c.waitForStorageSnapshotDeleted(input.Name, input.Timeout) return c.waitForStorageSnapshotDeleted(input.Name, input.PollInterval, input.Timeout)
} }
func (c *StorageVolumeSnapshotClient) success(result *StorageVolumeSnapshotInfo) (*StorageVolumeSnapshotInfo, error) { func (c *StorageVolumeSnapshotClient) success(result *StorageVolumeSnapshotInfo) (*StorageVolumeSnapshotInfo, error) {
c.unqualify(&result.Name) result.Name = c.getUnqualifiedName(result.FQDN)
c.unqualify(&result.Volume) c.unqualify(&result.Volume)
sizeInGigaBytes, err := sizeInGigaBytes(result.Size) sizeInGigaBytes, err := sizeInGigaBytes(result.Size)
@ -198,11 +216,12 @@ func (c *StorageVolumeSnapshotClient) success(result *StorageVolumeSnapshotInfo)
} }
// Waits for a storage snapshot to become available // Waits for a storage snapshot to become available
func (c *StorageVolumeSnapshotClient) waitForStorageSnapshotAvailable(name string, timeout time.Duration) (*StorageVolumeSnapshotInfo, error) { func (c *StorageVolumeSnapshotClient) waitForStorageSnapshotAvailable(name string, pollInterval, timeout time.Duration) (*StorageVolumeSnapshotInfo, error) {
var result *StorageVolumeSnapshotInfo var result *StorageVolumeSnapshotInfo
err := c.client.WaitFor( err := c.client.WaitFor(
fmt.Sprintf("storage volume snapshot %s to become available", c.getQualifiedName(name)), fmt.Sprintf("storage volume snapshot %s to become available", c.getQualifiedName(name)),
pollInterval,
timeout, timeout,
func() (bool, error) { func() (bool, error) {
req := &GetStorageVolumeSnapshotInput{ req := &GetStorageVolumeSnapshotInput{
@ -229,9 +248,10 @@ func (c *StorageVolumeSnapshotClient) waitForStorageSnapshotAvailable(name strin
} }
// Waits for a storage snapshot to be deleted // Waits for a storage snapshot to be deleted
func (c *StorageVolumeSnapshotClient) waitForStorageSnapshotDeleted(name string, timeout time.Duration) error { func (c *StorageVolumeSnapshotClient) waitForStorageSnapshotDeleted(name string, pollInterval, timeout time.Duration) error {
return c.client.WaitFor( return c.client.WaitFor(
fmt.Sprintf("storage volume snapshot %s to be deleted", c.getQualifiedName(name)), fmt.Sprintf("storage volume snapshot %s to be deleted", c.getQualifiedName(name)),
pollInterval,
timeout, timeout,
func() (bool, error) { func() (bool, error) {
req := &GetStorageVolumeSnapshotInput{ req := &GetStorageVolumeSnapshotInput{

View File

@ -9,8 +9,10 @@ import (
"github.com/hashicorp/go-oracle-terraform/client" "github.com/hashicorp/go-oracle-terraform/client"
) )
const WaitForVolumeReadyTimeout = time.Duration(600 * time.Second) const waitForVolumeReadyPollInterval = 10 * time.Second
const WaitForVolumeDeleteTimeout = time.Duration(600 * time.Second) const waitForVolumeReadyTimeout = 600 * time.Second
const waitForVolumeDeletePollInterval = 10 * time.Second
const waitForVolumeDeleteTimeout = 600 * time.Second
// StorageVolumeClient is a client for the Storage Volume functions of the Compute API. // StorageVolumeClient is a client for the Storage Volume functions of the Compute API.
type StorageVolumeClient struct { type StorageVolumeClient struct {
@ -19,10 +21,10 @@ type StorageVolumeClient struct {
// StorageVolumes obtains a StorageVolumeClient which can be used to access to the // StorageVolumes obtains a StorageVolumeClient which can be used to access to the
// Storage Volume functions of the Compute API // Storage Volume functions of the Compute API
func (c *ComputeClient) StorageVolumes() *StorageVolumeClient { func (c *Client) StorageVolumes() *StorageVolumeClient {
return &StorageVolumeClient{ return &StorageVolumeClient{
ResourceClient: ResourceClient{ ResourceClient: ResourceClient{
ComputeClient: c, Client: c,
ResourceDescription: "storage volume", ResourceDescription: "storage volume",
ContainerPath: "/storage/volume/", ContainerPath: "/storage/volume/",
ResourceRootPath: "/storage/volume", ResourceRootPath: "/storage/volume",
@ -30,12 +32,16 @@ func (c *ComputeClient) StorageVolumes() *StorageVolumeClient {
} }
// StorageVolumeKind defines the kinds of storage volumes that can be managed
type StorageVolumeKind string type StorageVolumeKind string
const ( const (
// StorageVolumeKindDefault - "/oracle/public/storage/default"
StorageVolumeKindDefault StorageVolumeKind = "/oracle/public/storage/default" StorageVolumeKindDefault StorageVolumeKind = "/oracle/public/storage/default"
// StorageVolumeKindLatency - "/oracle/public/storage/latency"
StorageVolumeKindLatency StorageVolumeKind = "/oracle/public/storage/latency" StorageVolumeKindLatency StorageVolumeKind = "/oracle/public/storage/latency"
StorageVolumeKindSSD StorageVolumeKind = "/oracle/public/storage/ssd/gpl" // StorageVolumeKindSSD - "/oracle/public/storage/ssd/gpl"
StorageVolumeKindSSD StorageVolumeKind = "/oracle/public/storage/ssd/gpl"
) )
// StorageVolumeInfo represents information retrieved from the service about a Storage Volume. // StorageVolumeInfo represents information retrieved from the service about a Storage Volume.
@ -43,30 +49,23 @@ type StorageVolumeInfo struct {
// Shows the default account for your identity domain. // Shows the default account for your identity domain.
Account string `json:"account,omitempty"` Account string `json:"account,omitempty"`
// true indicates that the storage volume can also be used as a boot disk for an instance.
// If you set the value to true, then you must specify values for the `ImageList` and `ImageListEntry` fields.
Bootable bool `json:"bootable,omitempty"`
// The description of the storage volume. // The description of the storage volume.
Description string `json:"description,omitempty"` Description string `json:"description,omitempty"`
// Fully Qualified Domain Name
FQDN string `json:"name"`
// The hypervisor that this volume is compatible with. // The hypervisor that this volume is compatible with.
Hypervisor string `json:"hypervisor,omitempty"` Hypervisor string `json:"hypervisor,omitempty"`
// Name of machine image to extract onto this volume when created. This information is provided only for bootable storage volumes. // Name of machine image to extract onto this volume when created. This information is provided only for bootable storage volumes.
ImageList string `json:"imagelist,omitempty"` ImageList string `json:"imagelist,omitempty"`
// Specific imagelist entry version to extract.
ImageListEntry int `json:"imagelist_entry,omitempty"`
// Three-part name of the machine image. This information is available if the volume is a bootable storage volume. // Three-part name of the machine image. This information is available if the volume is a bootable storage volume.
MachineImage string `json:"machineimage_name,omitempty"` MachineImage string `json:"machineimage_name,omitempty"`
// All volumes are managed volumes. Default value is true.
Managed bool `json:"managed,omitempty"`
// The three-part name of the object (/Compute-identity_domain/user/object). // The three-part name of the object (/Compute-identity_domain/user/object).
Name string `json:"name"` Name string
// The OS platform this volume is compatible with. // The OS platform this volume is compatible with.
Platform string `json:"platform,omitempty"` Platform string `json:"platform,omitempty"`
@ -74,9 +73,6 @@ type StorageVolumeInfo struct {
// The storage-pool property: /oracle/public/storage/latency or /oracle/public/storage/default. // The storage-pool property: /oracle/public/storage/latency or /oracle/public/storage/default.
Properties []string `json:"properties,omitempty"` Properties []string `json:"properties,omitempty"`
// Boolean field indicating whether this volume can be attached as readonly. If set to False the volume will be attached as read-write.
ReadOnly bool `json:"readonly,omitempty"`
// The size of this storage volume in GB. // The size of this storage volume in GB.
Size string `json:"size"` Size string `json:"size"`
@ -107,6 +103,19 @@ type StorageVolumeInfo struct {
// Uniform Resource Identifier // Uniform Resource Identifier
URI string `json:"uri,omitempty"` URI string `json:"uri,omitempty"`
// true indicates that the storage volume can also be used as a boot disk for an instance.
// If you set the value to true, then you must specify values for the `ImageList` and `ImageListEntry` fields.
Bootable bool `json:"bootable,omitempty"`
// All volumes are managed volumes. Default value is true.
Managed bool `json:"managed,omitempty"`
// Boolean field indicating whether this volume can be attached as readonly. If set to False the volume will be attached as read-write.
ReadOnly bool `json:"readonly,omitempty"`
// Specific imagelist entry version to extract.
ImageListEntry int `json:"imagelist_entry,omitempty"`
} }
func (c *StorageVolumeClient) getStorageVolumePath(name string) string { func (c *StorageVolumeClient) getStorageVolumePath(name string) string {
@ -149,6 +158,9 @@ type CreateStorageVolumeInput struct {
// Comma-separated strings that tag the storage volume. // Comma-separated strings that tag the storage volume.
Tags []string `json:"tags,omitempty"` Tags []string `json:"tags,omitempty"`
// Timeout to wait polling storage volume status.
PollInterval time.Duration `json:"-"`
// Timeout to wait for storage volume creation. // Timeout to wait for storage volume creation.
Timeout time.Duration `json:"-"` Timeout time.Duration `json:"-"`
} }
@ -165,23 +177,26 @@ func (c *StorageVolumeClient) CreateStorageVolume(input *CreateStorageVolumeInpu
input.Size = sizeInBytes input.Size = sizeInBytes
var storageInfo StorageVolumeInfo var storageInfo StorageVolumeInfo
if err := c.createResource(&input, &storageInfo); err != nil { if err = c.createResource(&input, &storageInfo); err != nil {
return nil, err return nil, err
} }
// Should never be nil, as we set this in the provider; but protect against it anyways. // Should never be nil, as we set this in the provider; but protect against it anyways.
if input.PollInterval == 0 {
input.PollInterval = waitForVolumeReadyPollInterval
}
if input.Timeout == 0 { if input.Timeout == 0 {
input.Timeout = WaitForVolumeReadyTimeout input.Timeout = waitForVolumeReadyTimeout
} }
volume, err := c.waitForStorageVolumeToBecomeAvailable(input.Name, input.Timeout) volume, err := c.waitForStorageVolumeToBecomeAvailable(input.Name, input.PollInterval, input.Timeout)
if err != nil { if err != nil {
if volume != nil { if volume != nil {
deleteInput := &DeleteStorageVolumeInput{ deleteInput := &DeleteStorageVolumeInput{
Name: volume.Name, Name: volume.Name,
} }
if err := c.DeleteStorageVolume(deleteInput); err != nil { if err = c.DeleteStorageVolume(deleteInput); err != nil {
return nil, err return nil, err
} }
} }
@ -194,6 +209,9 @@ type DeleteStorageVolumeInput struct {
// The three-part name of the object (/Compute-identity_domain/user/object). // The three-part name of the object (/Compute-identity_domain/user/object).
Name string `json:"name"` Name string `json:"name"`
// Timeout to wait betweeon polling storage volume status
PollInterval time.Duration `json:"-"`
// Timeout to wait for storage volume deletion // Timeout to wait for storage volume deletion
Timeout time.Duration `json:"-"` Timeout time.Duration `json:"-"`
} }
@ -205,11 +223,14 @@ func (c *StorageVolumeClient) DeleteStorageVolume(input *DeleteStorageVolumeInpu
} }
// Should never be nil, but protect against it anyways // Should never be nil, but protect against it anyways
if input.PollInterval == 0 {
input.PollInterval = waitForVolumeDeletePollInterval
}
if input.Timeout == 0 { if input.Timeout == 0 {
input.Timeout = WaitForVolumeDeleteTimeout input.Timeout = waitForVolumeDeleteTimeout
} }
return c.waitForStorageVolumeToBeDeleted(input.Name, input.Timeout) return c.waitForStorageVolumeToBeDeleted(input.Name, input.PollInterval, input.Timeout)
} }
// GetStorageVolumeInput represents the body of an API request to obtain a Storage Volume. // GetStorageVolumeInput represents the body of an API request to obtain a Storage Volume.
@ -220,7 +241,7 @@ type GetStorageVolumeInput struct {
func (c *StorageVolumeClient) success(result *StorageVolumeInfo) (*StorageVolumeInfo, error) { func (c *StorageVolumeClient) success(result *StorageVolumeInfo) (*StorageVolumeInfo, error) {
c.unqualify(&result.ImageList) c.unqualify(&result.ImageList)
c.unqualify(&result.Name) result.Name = c.getUnqualifiedName(result.FQDN)
c.unqualify(&result.Snapshot) c.unqualify(&result.Snapshot)
sizeInMegaBytes, err := sizeInGigaBytes(result.Size) sizeInMegaBytes, err := sizeInGigaBytes(result.Size)
@ -278,6 +299,9 @@ type UpdateStorageVolumeInput struct {
// Comma-separated strings that tag the storage volume. // Comma-separated strings that tag the storage volume.
Tags []string `json:"tags,omitempty"` Tags []string `json:"tags,omitempty"`
// Time to wait between polling storage volume status
PollInterval time.Duration `json:"-"`
// Time to wait for storage volume ready // Time to wait for storage volume ready
Timeout time.Duration `json:"-"` Timeout time.Duration `json:"-"`
} }
@ -299,11 +323,14 @@ func (c *StorageVolumeClient) UpdateStorageVolume(input *UpdateStorageVolumeInpu
return nil, err return nil, err
} }
if input.PollInterval == 0 {
input.PollInterval = waitForVolumeReadyPollInterval
}
if input.Timeout == 0 { if input.Timeout == 0 {
input.Timeout = WaitForVolumeReadyTimeout input.Timeout = waitForVolumeReadyTimeout
} }
volumeInfo, err := c.waitForStorageVolumeToBecomeAvailable(input.Name, input.Timeout) volumeInfo, err := c.waitForStorageVolumeToBecomeAvailable(input.Name, input.PollInterval, input.Timeout)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -312,11 +339,12 @@ func (c *StorageVolumeClient) UpdateStorageVolume(input *UpdateStorageVolumeInpu
} }
// waitForStorageVolumeToBecomeAvailable waits until a new Storage Volume is available (i.e. has finished initialising or updating). // waitForStorageVolumeToBecomeAvailable waits until a new Storage Volume is available (i.e. has finished initialising or updating).
func (c *StorageVolumeClient) waitForStorageVolumeToBecomeAvailable(name string, timeout time.Duration) (*StorageVolumeInfo, error) { func (c *StorageVolumeClient) waitForStorageVolumeToBecomeAvailable(name string, pollInterval, timeout time.Duration) (*StorageVolumeInfo, error) {
var waitResult *StorageVolumeInfo var waitResult *StorageVolumeInfo
err := c.client.WaitFor( err := c.client.WaitFor(
fmt.Sprintf("storage volume %s to become available", c.getQualifiedName(name)), fmt.Sprintf("storage volume %s to become available", c.getQualifiedName(name)),
pollInterval,
timeout, timeout,
func() (bool, error) { func() (bool, error) {
getRequest := &GetStorageVolumeInput{ getRequest := &GetStorageVolumeInput{
@ -345,9 +373,10 @@ func (c *StorageVolumeClient) waitForStorageVolumeToBecomeAvailable(name string,
} }
// waitForStorageVolumeToBeDeleted waits until the specified storage volume has been deleted. // waitForStorageVolumeToBeDeleted waits until the specified storage volume has been deleted.
func (c *StorageVolumeClient) waitForStorageVolumeToBeDeleted(name string, timeout time.Duration) error { func (c *StorageVolumeClient) waitForStorageVolumeToBeDeleted(name string, pollInterval, timeout time.Duration) error {
return c.client.WaitFor( return c.client.WaitFor(
fmt.Sprintf("storage volume %s to be deleted", c.getQualifiedName(name)), fmt.Sprintf("storage volume %s to be deleted", c.getQualifiedName(name)),
pollInterval,
timeout, timeout,
func() (bool, error) { func() (bool, error) {
getRequest := &GetStorageVolumeInput{ getRequest := &GetStorageVolumeInput{

View File

@ -34,7 +34,7 @@ func newAuthenticatingServer(handler func(w http.ResponseWriter, r *http.Request
})) }))
} }
func getTestClient(c *opc.Config) (*ComputeClient, error) { func getTestClient(c *opc.Config) (*Client, error) {
// Build up config with default values if omitted // Build up config with default values if omitted
if c.APIEndpoint == nil { if c.APIEndpoint == nil {
if os.Getenv("OPC_ENDPOINT") == "" { if os.Getenv("OPC_ENDPOINT") == "" {
@ -73,7 +73,8 @@ func getTestClient(c *opc.Config) (*ComputeClient, error) {
return NewComputeClient(c) return NewComputeClient(c)
} }
func getBlankTestClient() (*ComputeClient, *httptest.Server, error) { // nolint: deadcode
func getBlankTestClient() (*Client, *httptest.Server, error) {
server := newAuthenticatingServer(func(w http.ResponseWriter, r *http.Request) { server := newAuthenticatingServer(func(w http.ResponseWriter, r *http.Request) {
}) })
@ -96,23 +97,26 @@ func getBlankTestClient() (*ComputeClient, *httptest.Server, error) {
} }
// Returns a stub client with default values, and a custom API Endpoint // Returns a stub client with default values, and a custom API Endpoint
func getStubClient(endpoint *url.URL) (*ComputeClient, error) { // nolint: deadcode
domain := "test" func getStubClient(endpoint *url.URL) (*Client, error) {
username := "test" testAttr := "test"
password := "test"
config := &opc.Config{ config := &opc.Config{
IdentityDomain: &domain, IdentityDomain: &testAttr,
Username: &username, Username: &testAttr,
Password: &password, Password: &testAttr,
APIEndpoint: endpoint, APIEndpoint: endpoint,
} }
return getTestClient(config) return getTestClient(config)
} }
// nolint: deadcode
func unmarshalRequestBody(t *testing.T, r *http.Request, target interface{}) { func unmarshalRequestBody(t *testing.T, r *http.Request, target interface{}) {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
buf.ReadFrom(r.Body) _, err := buf.ReadFrom(r.Body)
err := json.Unmarshal(buf.Bytes(), target) if err != nil {
t.Fatalf("Error reading buffer: %s", err)
}
err = json.Unmarshal(buf.Bytes(), target)
if err != nil { if err != nil {
t.Fatalf("Error marshalling request: %s", err) t.Fatalf("Error marshalling request: %s", err)
} }

View File

@ -1,13 +1,15 @@
package compute package compute
// VirtNICsClient defines a vritual nics client
type VirtNICsClient struct { type VirtNICsClient struct {
ResourceClient ResourceClient
} }
func (c *ComputeClient) VirtNICs() *VirtNICsClient { // VirtNICs returns a virtual nics client
func (c *Client) VirtNICs() *VirtNICsClient {
return &VirtNICsClient{ return &VirtNICsClient{
ResourceClient: ResourceClient{ ResourceClient: ResourceClient{
ComputeClient: c, Client: c,
ResourceDescription: "Virtual NIC", ResourceDescription: "Virtual NIC",
ContainerPath: "/network/v1/vnic/", ContainerPath: "/network/v1/vnic/",
ResourceRootPath: "/network/v1/vnic", ResourceRootPath: "/network/v1/vnic",
@ -15,28 +17,32 @@ func (c *ComputeClient) VirtNICs() *VirtNICsClient {
} }
} }
// VirtualNIC defines the attributes in a virtual nic
type VirtualNIC struct { type VirtualNIC struct {
// Description of the object. // Description of the object.
Description string `json:"description"` Description string `json:"description"`
// Fully Qualified Domain Name
FQDN string `json:"name"`
// MAC address of this VNIC. // MAC address of this VNIC.
MACAddress string `json:"macAddress"` MACAddress string `json:"macAddress"`
// The three-part name (/Compute-identity_domain/user/object) of the Virtual NIC. // The three-part name (/Compute-identity_domain/user/object) of the Virtual NIC.
Name string `json:"name"` Name string
// Tags associated with the object. // Tags associated with the object.
Tags []string `json:"tags"` Tags []string `json:"tags"`
// True if the VNIC is of type "transit". // True if the VNIC is of type "transit".
TransitFlag bool `json:"transitFlag"` TransitFlag bool `json:"transitFlag"`
// Uniform Resource Identifier // Uniform Resource Identifier
Uri string `json:"uri"` URI string `json:"uri"`
} }
// Can only GET a virtual NIC, not update, create, or delete // GetVirtualNICInput Can only GET a virtual NIC, not update, create, or delete
type GetVirtualNICInput struct { type GetVirtualNICInput struct {
// The three-part name (/Compute-identity_domain/user/object) of the Virtual NIC. // The three-part name (/Compute-identity_domain/user/object) of the Virtual NIC.
// Required // Required
Name string `json:"name"` Name string `json:"name"`
} }
// GetVirtualNIC returns the specified virtual nic
func (c *VirtNICsClient) GetVirtualNIC(input *GetVirtualNICInput) (*VirtualNIC, error) { func (c *VirtNICsClient) GetVirtualNIC(input *GetVirtualNICInput) (*VirtualNIC, error) {
var virtNIC VirtualNIC var virtNIC VirtualNIC
input.Name = c.getQualifiedName(input.Name) input.Name = c.getQualifiedName(input.Name)
@ -47,6 +53,6 @@ func (c *VirtNICsClient) GetVirtualNIC(input *GetVirtualNICInput) (*VirtualNIC,
} }
func (c *VirtNICsClient) success(info *VirtualNIC) (*VirtualNIC, error) { func (c *VirtNICsClient) success(info *VirtualNIC) (*VirtualNIC, error) {
c.unqualify(&info.Name) info.Name = c.getUnqualifiedName(info.FQDN)
return info, nil return info, nil
} }

View File

@ -1,13 +1,15 @@
package compute package compute
// VirtNICSetsClient defines a virtual set nic client
type VirtNICSetsClient struct { type VirtNICSetsClient struct {
ResourceClient ResourceClient
} }
func (c *ComputeClient) VirtNICSets() *VirtNICSetsClient { // VirtNICSets returns a virtual nic sets client
func (c *Client) VirtNICSets() *VirtNICSetsClient {
return &VirtNICSetsClient{ return &VirtNICSetsClient{
ResourceClient: ResourceClient{ ResourceClient: ResourceClient{
ComputeClient: c, Client: c,
ResourceDescription: "Virtual NIC Set", ResourceDescription: "Virtual NIC Set",
ContainerPath: "/network/v1/vnicset/", ContainerPath: "/network/v1/vnicset/",
ResourceRootPath: "/network/v1/vnicset", ResourceRootPath: "/network/v1/vnicset",
@ -15,22 +17,25 @@ func (c *ComputeClient) VirtNICSets() *VirtNICSetsClient {
} }
} }
// Describes an existing virtual nic set // VirtualNICSet describes an existing virtual nic set
type VirtualNICSet struct { type VirtualNICSet struct {
// List of ACLs applied to the VNICs in the set. // List of ACLs applied to the VNICs in the set.
AppliedACLs []string `json:"appliedAcls"` AppliedACLs []string `json:"appliedAcls"`
// Description of the VNIC Set. // Description of the VNIC Set.
Description string `json:"description"` Description string `json:"description"`
// Fully Qualified Domain Name
FQDN string `json:"name"`
// Name of the VNIC set. // Name of the VNIC set.
Name string `json:"name"` Name string
// The three-part name (/Compute-identity_domain/user/object) of the virtual NIC set. // The three-part name (/Compute-identity_domain/user/object) of the virtual NIC set.
Tags []string `json:"tags"` Tags []string `json:"tags"`
// Uniform Resource Identifier // Uniform Resource Identifier
Uri string `json:"uri"` URI string `json:"uri"`
// List of VNICs associated with this VNIC set. // List of VNICs associated with this VNIC set.
VirtualNICs []string `json:"vnics"` VirtualNICs []string `json:"vnics"`
} }
// CreateVirtualNICSetInput specifies the details of the virutal nic set to create
type CreateVirtualNICSetInput struct { type CreateVirtualNICSetInput struct {
// List of ACLs applied to the VNICs in the set. // List of ACLs applied to the VNICs in the set.
// Optional // Optional
@ -50,6 +55,7 @@ type CreateVirtualNICSetInput struct {
VirtualNICs []string `json:"vnics"` VirtualNICs []string `json:"vnics"`
} }
// CreateVirtualNICSet creates a new virtual nic set
func (c *VirtNICSetsClient) CreateVirtualNICSet(input *CreateVirtualNICSetInput) (*VirtualNICSet, error) { func (c *VirtNICSetsClient) CreateVirtualNICSet(input *CreateVirtualNICSetInput) (*VirtualNICSet, error) {
input.Name = c.getQualifiedName(input.Name) input.Name = c.getQualifiedName(input.Name)
input.AppliedACLs = c.getQualifiedAcls(input.AppliedACLs) input.AppliedACLs = c.getQualifiedAcls(input.AppliedACLs)
@ -66,12 +72,14 @@ func (c *VirtNICSetsClient) CreateVirtualNICSet(input *CreateVirtualNICSetInput)
return c.success(&virtNicSet) return c.success(&virtNicSet)
} }
// GetVirtualNICSetInput specifies which virutal nic to obtain
type GetVirtualNICSetInput struct { type GetVirtualNICSetInput struct {
// The three-part name (/Compute-identity_domain/user/object) of the virtual NIC set. // The three-part name (/Compute-identity_domain/user/object) of the virtual NIC set.
// Required // Required
Name string `json:"name"` Name string `json:"name"`
} }
// GetVirtualNICSet retrieves the specified virtual nic set
func (c *VirtNICSetsClient) GetVirtualNICSet(input *GetVirtualNICSetInput) (*VirtualNICSet, error) { func (c *VirtNICSetsClient) GetVirtualNICSet(input *GetVirtualNICSetInput) (*VirtualNICSet, error) {
var virtNicSet VirtualNICSet var virtNicSet VirtualNICSet
// Qualify Name // Qualify Name
@ -83,6 +91,7 @@ func (c *VirtNICSetsClient) GetVirtualNICSet(input *GetVirtualNICSetInput) (*Vir
return c.success(&virtNicSet) return c.success(&virtNicSet)
} }
// UpdateVirtualNICSetInput specifies the information that will be updated in the virtual nic set
type UpdateVirtualNICSetInput struct { type UpdateVirtualNICSetInput struct {
// List of ACLs applied to the VNICs in the set. // List of ACLs applied to the VNICs in the set.
// Optional // Optional
@ -102,6 +111,7 @@ type UpdateVirtualNICSetInput struct {
VirtualNICs []string `json:"vnics"` VirtualNICs []string `json:"vnics"`
} }
// UpdateVirtualNICSet updates the specified virtual nic set
func (c *VirtNICSetsClient) UpdateVirtualNICSet(input *UpdateVirtualNICSetInput) (*VirtualNICSet, error) { func (c *VirtNICSetsClient) UpdateVirtualNICSet(input *UpdateVirtualNICSetInput) (*VirtualNICSet, error) {
input.Name = c.getQualifiedName(input.Name) input.Name = c.getQualifiedName(input.Name)
input.AppliedACLs = c.getQualifiedAcls(input.AppliedACLs) input.AppliedACLs = c.getQualifiedAcls(input.AppliedACLs)
@ -119,12 +129,14 @@ func (c *VirtNICSetsClient) UpdateVirtualNICSet(input *UpdateVirtualNICSetInput)
return c.success(&virtNICSet) return c.success(&virtNICSet)
} }
// DeleteVirtualNICSetInput specifies the virtual nic set to delete
type DeleteVirtualNICSetInput struct { type DeleteVirtualNICSetInput struct {
// The name of the virtual NIC set. // The name of the virtual NIC set.
// Required // Required
Name string `json:"name"` Name string `json:"name"`
} }
// DeleteVirtualNICSet deletes the specified virtual nic set
func (c *VirtNICSetsClient) DeleteVirtualNICSet(input *DeleteVirtualNICSetInput) error { func (c *VirtNICSetsClient) DeleteVirtualNICSet(input *DeleteVirtualNICSetInput) error {
input.Name = c.getQualifiedName(input.Name) input.Name = c.getQualifiedName(input.Name)
return c.deleteResource(input.Name) return c.deleteResource(input.Name)
@ -147,7 +159,7 @@ func (c *VirtNICSetsClient) unqualifyAcls(acls []string) []string {
} }
func (c *VirtNICSetsClient) success(info *VirtualNICSet) (*VirtualNICSet, error) { func (c *VirtNICSetsClient) success(info *VirtualNICSet) (*VirtualNICSet, error) {
c.unqualify(&info.Name) info.Name = c.getUnqualifiedName(info.FQDN)
info.AppliedACLs = c.unqualifyAcls(info.AppliedACLs) info.AppliedACLs = c.unqualifyAcls(info.AppliedACLs)
info.VirtualNICs = c.getUnqualifiedList(info.VirtualNICs) info.VirtualNICs = c.getUnqualifiedList(info.VirtualNICs)
return info, nil return info, nil

View File

@ -0,0 +1,451 @@
package compute
import (
"fmt"
"time"
"github.com/hashicorp/go-oracle-terraform/client"
)
const (
vpnEndpointV2Description = "vpn endpoint v2"
vpnEndpointV2ContainerPath = "/vpnendpoint/v2/"
vpnEndpointV2ResourcePath = "/vpnendpoint/v2"
waitForVPNEndpointV2ActivePollInterval = 10 * time.Second
waitForVPNEndpointV2ActiveTimeout = 3600 * time.Second
waitForVPNEndpointV2DeletePollInterval = 10 * time.Second
waitForVPNEndpointV2DeleteTimeout = 3600 * time.Second
)
// VPNEndpointV2sClient returns a VPNEndpointV2 client
type VPNEndpointV2sClient struct {
ResourceClient
}
// VPNEndpointV2s returns an VPNEndpointV2sClient that can be used to access the
// necessary CRUD functions for VPN Endpoint V2s.
func (c *Client) VPNEndpointV2s() *VPNEndpointV2sClient {
return &VPNEndpointV2sClient{
ResourceClient: ResourceClient{
Client: c,
ResourceDescription: vpnEndpointV2Description,
ContainerPath: vpnEndpointV2ContainerPath,
ResourceRootPath: vpnEndpointV2ResourcePath,
},
}
}
// VPNEndpointTunnelStatus defines the different statuses a VPN Endpoint tunnel can be in
type VPNEndpointTunnelStatus string
const (
// VPNEndpointTunnelStatusPending - the tunnel is in a pending state
VPNEndpointTunnelStatusPending VPNEndpointTunnelStatus = "PENDING"
// VPNEndpointTunnelStatusUp - the tunnel is in a up state
VPNEndpointTunnelStatusUp VPNEndpointTunnelStatus = "UP"
// VPNEndpointTunnelStatusDown - the tunnel is in a down state
VPNEndpointTunnelStatusDown VPNEndpointTunnelStatus = "DOWN"
// VPNEndpointTunnelStatusError - the tunnel is in a error state
VPNEndpointTunnelStatusError VPNEndpointTunnelStatus = "ERROR"
)
// VPNEndpointLifeCycleState defines the different lifecycle states a VPN Endpoint can be in
type VPNEndpointLifeCycleState string
const (
// VPNEndpointLifeCycleStateProvisioning - the endpoint is in a provisioning state
VPNEndpointLifeCycleStateProvisioning VPNEndpointLifeCycleState = "provisioning"
// VPNEndpointLifeCycleStateReady - the endpoint is in a ready state
VPNEndpointLifeCycleStateReady VPNEndpointLifeCycleState = "ready"
// VPNEndpointLifeCycleStateDeleting - the endpoint is in a ready state
VPNEndpointLifeCycleStateDeleting VPNEndpointLifeCycleState = "deleting"
// VPNEndpointLifeCycleStateError - the endpoint is in a error state
VPNEndpointLifeCycleStateError VPNEndpointLifeCycleState = "error"
// VPNEndpointLifeCycleStateUpdating - the endpoint is in a updating state
VPNEndpointLifeCycleStateUpdating VPNEndpointLifeCycleState = "updating"
)
// VPNEndpointV2Info contains the exported fields necessary to hold all the information about an
// VPN Endpoint V2
type VPNEndpointV2Info struct {
// IP address of the VPN gateway in your data center through which you want
// to connect to the Oracle Cloud VPN gateway.
CustomerVPNGateway string `json:"customer_vpn_gateway"`
// Whether the tunnel is enabled or disabled
// Optional
Enabled bool `json:"enabled"`
// The Internet Key Exchange (IKE) ID that you have specified. The default
// value is the public IP address of the cloud gateway.
IKEIdentifier string `json:"ikeIdentifier"`
// The name of the IP network on which the cloud gateway is created by VPNaaS.
IPNetwork string `json:"ipNetwork"`
// The name of the VPN Endpoint V2
Name string `json:"name"`
// The lifecycle state the VPNEndpointV2 is in
LifeCycleState VPNEndpointLifeCycleState `json:"lifecycleState"`
// Indicates whether Perfect Forward Secrecy (PFS) is required and your third-party device supports PFS.
PFSFlag bool `json:"pfsFlag"`
// Settings for Phase 1 of protocol (IKE).
Phase1Settings Phase1Settings `json:"phase1Settings"`
// Settings for Phase 2 of protocol (IPSEC).
Phase2Settings Phase2Settings `json:"phase2Settings"`
// The pre-shared VPN key.
PSK string `json:"psk"`
// List of routes (CIDR prefixes) that are reachable through this VPN tunnel.
ReachableRoutes []string `json:"reachable_routes"`
// Current status of the tunnel. The tunnel can be in one of the following states:
// PENDING: indicates that your VPN connection is being set up.
// UP: indicates that your VPN connection is established.
// DOWN: indicates that your VPN connection is down.
// ERROR: indicates that your VPN connection is in the error state.
TunnelStatus VPNEndpointTunnelStatus `json:"tunnelStatus"`
// Uniform Resource Identifier for the VPN Endpoint V2
URI string `json:"uri"`
// Comma-separated list of vNIC sets. Traffic is allowed to and
// from these vNIC sets to the cloud gateway's vNIC set.
VNICSets []string `json:"vnicSets"`
}
// Phase1Settings define the attributes related to Phase 1 Protocol (IKE)
type Phase1Settings struct {
// Encryption options for IKE. Permissible values are aes128, aes192, aes256.
Encryption string `json:"encryption"`
// Authentication options for IKE. Permissible values are sha1, sha2_256, and md5.
Hash string `json:"hash"`
// Diffie-Hellman group for both IKE and ESP. It is applicable for ESP only if PFS is enabled.
// Permissible values are group5, group14, group22, group23, and group24.
DHGroup string `json:"dhGroup"`
}
// Phase2Settings define the attributes related to Phase 2 Protocol (IPSEC)
type Phase2Settings struct {
// Encryption options for IKE. Permissible values are aes128, aes192, aes256.
Encryption string `json:"encryption"`
// Authentication options for IKE. Permissible values are sha1, sha2_256, and md5.
Hash string `json:"hash"`
}
// CreateVPNEndpointV2Input define the attributes related to creating a vpn endpoint v2
type CreateVPNEndpointV2Input struct {
// Specify the IP address of the VPN gateway in your data center through which you want
// to connect to the Oracle Cloud VPN gateway. Your gateway device must support route-based
// VPN and IKE (Internet Key Exchange) configuration using pre-shared keys.
// Required
CustomerVPNGateway string `json:"customer_vpn_gateway"`
// Description of the VPN Endpoint
Description string `json:"description,omitempty"`
// Enable/Disable the tunnel
// Optional
Enabled bool `json:"enabled,omitempty"`
// The Internet Key Exchange (IKE) ID. If you don't specify a value, the default value is
// the public IP address of the cloud gateway. You can specify either an alternative IP address,
// or any text string that you want to use as the IKE ID. If you specify a text string, you must
// prefix the string with @. For example, if you want to specify the text IKEID-for-VPN1, specify
// @IKEID-for-VPN1 as the value in request body. If you specify an IP address, don't prefix it with @.
// The IKE ID is case sensitive and can contain a maximum of 255 ASCII alphanumeric characters
// including special characters, period (.), hyphen (-), and underscore (_). The IKE ID can't contain
// embedded space characters.
// Note: If you specify the IKE ID, ensure that you specify the Peer ID type as Domain Name on the
// third-party device in your data center. Other Peer ID types, such as email address, firewall
// identifier or key identifier, aren't supported.
// Optional
IKEIdentifier string `json:"ikeIdentifier,omitempty"`
// Specify the name of the IP network
// which you want to create the cloud gateway. When you send a request to create a VPN connection,
// a cloud gateway is created and this is assigned an available IP address from the IP network that
// you specify. So, the cloud gateway is directly connected to the IP network that you specify.
// You can only specify a single IP network. All other IP networks with are connected to the
// specified IP network through an IP network exchange are discovered and added automatically to
// the VPN connection.
// Required
IPNetwork string `json:"ipNetwork"`
// The name of the VPN Endpoint V2 to create. Object names can only contain alphanumeric,
// underscore, dash, and period characters. Names are case-sensitive.
// Required
Name string `json:"name"`
// This is enabled (set to true) by default. If your third-party device supports Perfect Forward
// Secrecy (PFS), set this parameter to true to require PFS.
// Optional. Default true
PFSFlag bool `json:"pfsFlag,omitmepty"`
// Settings for Phase 1 of protocol (IKE).
// Optional
Phase1Settings *Phase1Settings `json:"phase1Settings,omitempty"`
// Settings for Phase 2 of protocol (IPSEC).
// Optional
Phase2Settings *Phase2Settings `json:"phase2Settings,omitempty"`
// Pre-shared VPN key. This secret key is shared between your network gateway and
// the Oracle Cloud network for authentication. Specify the full path and name of
// the text file that contains the pre-shared key. Ensure that the permission level
// of the text file is set to 400. The pre-shared VPN key must not exceed 256 characters.
// Required
PSK string `json:"psk"`
// Specify a list of routes (CIDR prefixes) that are reachable through this VPN tunnel.
// You can specify a maximum of 20 IP subnet addresses. Specify IPv4 addresses in dot-decimal
// notation with or without mask.
// Required
ReachableRoutes []string `json:"reachable_routes"`
// An array of tags
Tags []string `json:"tags"`
// Comma-separated list of vNIC sets. Traffic is allowed to and from these vNIC sets to the
// cloud gateway's vNIC set.
// Required
VNICSets []string `json:"vnicSets"`
// Time to wait between polls to check status
PollInterval time.Duration `json:"-"`
// Time to wait for an vpn endoint v2 to be ready
Timeout time.Duration `json:"-"`
}
// CreateVPNEndpointV2 creates a new VPN Endpoint V2 from an VPNEndpointV2sClient and an input struct.
// Returns a populated Info struct for the VPN Endpoint V2, and any errors
func (c *VPNEndpointV2sClient) CreateVPNEndpointV2(input *CreateVPNEndpointV2Input) (*VPNEndpointV2Info, error) {
input.Name = c.getQualifiedName(input.Name)
input.IPNetwork = c.getQualifiedName(input.IPNetwork)
input.VNICSets = c.getQualifiedList(input.VNICSets)
var createdVPNEndpointV2Info VPNEndpointV2Info
if err := c.createResource(&input, &createdVPNEndpointV2Info); err != nil {
return nil, err
}
// Call wait for vpn endpoint ready now, as creating the vpn endpoint v2 takes time
getInput := &GetVPNEndpointV2Input{
Name: input.Name,
}
if input.PollInterval == 0 {
input.PollInterval = waitForVPNEndpointV2ActivePollInterval
}
if input.Timeout == 0 {
input.Timeout = waitForVPNEndpointV2ActiveTimeout
}
// Wait for vpn endpoint v2 to be ready and return the result
vpnEndpointV2Info, vpnEndpointV2InfoError := c.WaitForVPNEndpointV2Ready(getInput, input.PollInterval, input.Timeout)
if vpnEndpointV2InfoError != nil {
return nil, fmt.Errorf("Error creating VPNEndpointV2 %q: %s", getInput.Name, vpnEndpointV2InfoError)
}
return vpnEndpointV2Info, nil
}
// GetVPNEndpointV2Input specifies the information needed to retrive a VPNEndpointV2
type GetVPNEndpointV2Input struct {
// The name of the VPN Endpoint V2 to query for. Case-sensitive
// Required
Name string `json:"name"`
}
// GetVPNEndpointV2 returns a populated VPNEndpointV2Info struct from an input struct
func (c *VPNEndpointV2sClient) GetVPNEndpointV2(input *GetVPNEndpointV2Input) (*VPNEndpointV2Info, error) {
input.Name = c.getQualifiedName(input.Name)
var ipInfo VPNEndpointV2Info
if err := c.getResource(input.Name, &ipInfo); err != nil {
return nil, err
}
return c.success(&ipInfo)
}
// UpdateVPNEndpointV2Input defines what to update in a VPN Endpoint V2
// Only PSK and ReachableRoutes are updatable
type UpdateVPNEndpointV2Input struct {
// Specify the IP address of the VPN gateway in your data center through which you want
// to connect to the Oracle Cloud VPN gateway. Your gateway device must support route-based
// VPN and IKE (Internet Key Exchange) configuration using pre-shared keys.
// Required
CustomerVPNGateway string `json:"customer_vpn_gateway"`
// Description of the VPNGatewayV2
Description string `json:"description,omitempty"`
// Enable/Disable the tunnel
// Optional
Enabled bool `json:"enabled,omitempty"`
// The Internet Key Exchange (IKE) ID. If you don't specify a value, the default value is
// the public IP address of the cloud gateway. You can specify either an alternative IP address,
// or any text string that you want to use as the IKE ID. If you specify a text string, you must
// prefix the string with @. For example, if you want to specify the text IKEID-for-VPN1, specify
// @IKEID-for-VPN1 as the value in request body. If you specify an IP address, don't prefix it with @.
// The IKE ID is case sensitive and can contain a maximum of 255 ASCII alphanumeric characters
// including special characters, period (.), hyphen (-), and underscore (_). The IKE ID can't contain
// embedded space characters.
// Note: If you specify the IKE ID, ensure that you specify the Peer ID type as Domain Name on the
// third-party device in your data center. Other Peer ID types, such as email address, firewall
// identifier or key identifier, aren't supported.
// Optional
IKEIdentifier string `json:"ikeIdentifier,omitempty"`
// Specify the name of the IP network
// which you want to create the cloud gateway. When you send a request to create a VPN connection,
// a cloud gateway is created and this is assigned an available IP address from the IP network that
// you specify. So, the cloud gateway is directly connected to the IP network that you specify.
// You can only specify a single IP network. All other IP networks with are connected to the
// specified IP network through an IP network exchange are discovered and added automatically to
// the VPN connection.
// Required
IPNetwork string `json:"ipNetwork"`
// The name of the VPN Endpoint V2 to create. Object names can only contain alphanumeric,
// underscore, dash, and period characters. Names are case-sensitive.
// Required
Name string `json:"name"`
// This is enabled (set to true) by default. If your third-party device supports Perfect Forward
// Secrecy (PFS), set this parameter to true to require PFS.
// Optional. Default true
PFSFlag bool `json:"pfsFlag,omitempty"`
// Settings for Phase 1 of protocol (IKE).
// Optional
Phase1Settings *Phase1Settings `json:"phase1Settings,omitempty"`
// Settings for Phase 2 of protocol (IPSEC).
// Optional
Phase2Settings *Phase2Settings `json:"phase2Settings,omitempty"`
// Pre-shared VPN key. This secret key is shared between your network gateway and
// the Oracle Cloud network for authentication. Specify the full path and name of
// the text file that contains the pre-shared key. Ensure that the permission level
// of the text file is set to 400. The pre-shared VPN key must not exceed 256 characters.
// Required.
PSK string `json:"psk"`
// Specify a list of routes (CIDR prefixes) that are reachable through this VPN tunnel.
// You can specify a maximum of 20 IP subnet addresses. Specify IPv4 addresses in dot-decimal
// notation with or without mask.
// Required
ReachableRoutes []string `json:"reachable_routes"`
// Array of tags
Tags []string `json:"tags,omitempty"`
// Comma-separated list of vNIC sets. Traffic is allowed to and from these vNIC sets to the
// cloud gateway's vNIC set.
// Required
VNICSets []string `json:"vnicSets"`
// Time to wait between polls to check status
PollInterval time.Duration `json:"-"`
// Time to wait for an vpn endoint v2 to be ready
Timeout time.Duration `json:"-"`
}
// UpdateVPNEndpointV2 update the VPN Endpoint V2
func (c *VPNEndpointV2sClient) UpdateVPNEndpointV2(updateInput *UpdateVPNEndpointV2Input) (*VPNEndpointV2Info, error) {
updateInput.Name = c.getQualifiedName(updateInput.Name)
updateInput.IPNetwork = c.getQualifiedName(updateInput.IPNetwork)
updateInput.VNICSets = c.getQualifiedList(updateInput.VNICSets)
var ipInfo VPNEndpointV2Info
if err := c.updateResource(updateInput.Name, updateInput, &ipInfo); err != nil {
return nil, err
}
// Call wait for vpn endpoint ready now, as creating the vpn endpoint v2 takes time
getInput := &GetVPNEndpointV2Input{
Name: updateInput.Name,
}
if updateInput.PollInterval == 0 {
updateInput.PollInterval = waitForVPNEndpointV2ActivePollInterval
}
if updateInput.Timeout == 0 {
updateInput.Timeout = waitForVPNEndpointV2ActiveTimeout
}
// Wait for vpn endpoint v2 to be ready and return the result
vpnEndpointV2Info, vpnEndpointV2InfoError := c.WaitForVPNEndpointV2Ready(getInput, updateInput.PollInterval, updateInput.Timeout)
if vpnEndpointV2InfoError != nil {
return nil, fmt.Errorf("Error creating VPNEndpointV2 %q: %s", getInput.Name, vpnEndpointV2InfoError)
}
return vpnEndpointV2Info, nil
}
// DeleteVPNEndpointV2Input defines the attributes required for deleting a vpn endpoint v2
type DeleteVPNEndpointV2Input struct {
// The name of the VPN Endpoint V2 to query for. Case-sensitive
// Required
Name string `json:"name"`
// Poll Interval for delete request
PollInterval time.Duration `json:"-"`
// Timeout for delete request
Timeout time.Duration `json:"-"`
}
// DeleteVPNEndpointV2 deletes the specified vpn endpoint v2
func (c *VPNEndpointV2sClient) DeleteVPNEndpointV2(input *DeleteVPNEndpointV2Input) error {
if err := c.deleteResource(input.Name); err != nil {
return err
}
if input.PollInterval == 0 {
input.PollInterval = waitForVPNEndpointV2DeletePollInterval
}
if input.Timeout == 0 {
input.Timeout = waitForVPNEndpointV2DeleteTimeout
}
return c.WaitForVPNEndpointV2Deleted(input, input.PollInterval, input.Timeout)
}
// Unqualifies any qualified fields in the VPNEndpointV2Info struct
func (c *VPNEndpointV2sClient) success(info *VPNEndpointV2Info) (*VPNEndpointV2Info, error) {
c.unqualify(&info.Name)
c.unqualify(&info.IPNetwork)
info.VNICSets = c.getUnqualifiedList(info.VNICSets)
return info, nil
}
// WaitForVPNEndpointV2Ready waits for an vpn endpoint to be in an up or down state
func (c *VPNEndpointV2sClient) WaitForVPNEndpointV2Ready(input *GetVPNEndpointV2Input, pollInterval, timeout time.Duration) (*VPNEndpointV2Info, error) {
var info *VPNEndpointV2Info
var getErr error
err := c.client.WaitFor("vpn endpoint to be ready", pollInterval, timeout, func() (bool, error) {
info, getErr = c.GetVPNEndpointV2(input)
if getErr != nil {
return false, getErr
}
c.client.DebugLogString(fmt.Sprintf("VPNEndpointV2 name is %q, VPNEndpointV2 info is %+v", info.Name, info))
switch s := info.LifeCycleState; s {
case VPNEndpointLifeCycleStateProvisioning:
return false, nil
case VPNEndpointLifeCycleStateUpdating:
return false, nil
case VPNEndpointLifeCycleStateReady:
c.client.DebugLogString(fmt.Sprintf("VPNEndpointV2 %s", info.LifeCycleState))
return true, nil
case VPNEndpointLifeCycleStateError:
return false, fmt.Errorf("Error waiting for VPNEndpointV2 %q to be ready", info.Name)
case VPNEndpointLifeCycleStateDeleting:
return false, fmt.Errorf("Error waiting for VPNEndpointV2 %q to be ready", info.Name)
default:
return false, fmt.Errorf("Unknown VPNEndpointV2 lifecycle state: %s, erroring", s)
}
})
return info, err
}
// WaitForVPNEndpointV2Deleted waits for an VPNEndpointV2to be fully deleted.
func (c *VPNEndpointV2sClient) WaitForVPNEndpointV2Deleted(input *DeleteVPNEndpointV2Input, pollInterval, timeout time.Duration) error {
return c.client.WaitFor("VPNEndpointV2 to be deleted", pollInterval, timeout, func() (bool, error) {
var info VPNEndpointV2Info
if err := c.getResource(input.Name, &info); err != nil {
if client.WasNotFoundError(err) {
// VPNEndpointV2 could not be found, thus deleted
return true, nil
}
// Some other error occurred trying to get VPNEndpointV2, exit
return false, err
}
switch s := info.LifeCycleState; s {
case VPNEndpointLifeCycleStateProvisioning:
return false, fmt.Errorf("Error deleting VPNEndpointV2: %+v", info)
case VPNEndpointLifeCycleStateDeleting:
c.client.DebugLogString("VPNEndpointV2 deleting")
return false, nil
case VPNEndpointLifeCycleStateReady:
c.client.DebugLogString("VPNEndpointV2 deleting")
return false, nil
case VPNEndpointLifeCycleStateUpdating:
c.client.DebugLogString("VPNEndpointV2 updating")
return false, nil
case VPNEndpointLifeCycleStateError:
c.client.DebugLogString("VPNEndpointV2 deleting")
return false, nil
default:
return false, fmt.Errorf("Unknown VPNEndpointV2 lifecycle state: %s, erroring", s)
}
})
}

View File

@ -5,6 +5,7 @@ import (
"net/url" "net/url"
) )
// Config details the parameters needed to authenticate with Oracle Clouds API
type Config struct { type Config struct {
Username *string Username *string
Password *string Password *string
@ -17,6 +18,7 @@ type Config struct {
UserAgent *string UserAgent *string
} }
// NewConfig returns a blank config to populate with the neccessary fields to authenitcate with Oracle's API
func NewConfig() *Config { func NewConfig() *Config {
return &Config{} return &Config{}
} }

View File

@ -1,9 +1,11 @@
package opc package opc
// String returns a pointer to a string
func String(v string) *string { func String(v string) *string {
return &v return &v
} }
// Int returns a pointer to an int
func Int(v int) *int { func Int(v int) *int {
return &v return &v
} }

View File

@ -2,6 +2,7 @@ package opc
import "fmt" import "fmt"
// OracleError details the parameters of an error returned from Oracle's API
type OracleError struct { type OracleError struct {
StatusCode int StatusCode int
Message string Message string

View File

@ -8,10 +8,13 @@ import (
) )
const ( const (
LogOff LogLevelType = 0 // LogOff turns logging off
LogOff LogLevelType = 0
// LogDebug turns logging to debug
LogDebug LogLevelType = 1 LogDebug LogLevelType = 1
) )
// LogLevelType details the constants that log level can be in
type LogLevelType uint type LogLevelType uint
// Logger interface. Should be satisfied by Terraform's logger as well as the Default logger // Logger interface. Should be satisfied by Terraform's logger as well as the Default logger
@ -19,13 +22,15 @@ type Logger interface {
Log(...interface{}) Log(...interface{})
} }
// LoggerFunc details the logger functions
type LoggerFunc func(...interface{}) type LoggerFunc func(...interface{})
// Log logs the specified messages
func (f LoggerFunc) Log(args ...interface{}) { func (f LoggerFunc) Log(args ...interface{}) {
f(args...) f(args...)
} }
// Returns a default logger if one isn't specified during configuration // NewDefaultLogger returns a default logger if one isn't specified during configuration
func NewDefaultLogger() Logger { func NewDefaultLogger() Logger {
logWriter, err := LogOutput() logWriter, err := LogOutput()
if err != nil { if err != nil {
@ -45,6 +50,7 @@ func (l defaultLogger) Log(args ...interface{}) {
l.logger.Println(args...) l.logger.Println(args...)
} }
// LogOutput outputs the requested messages
func LogOutput() (logOutput io.Writer, err error) { func LogOutput() (logOutput io.Writer, err error) {
// Default to nil // Default to nil
logOutput = ioutil.Discard logOutput = ioutil.Discard
@ -59,12 +65,11 @@ func LogOutput() (logOutput io.Writer, err error) {
return return
} }
// Gets current Log Level from the ORACLE_LOG env var // LogLevel gets current Log Level from the ORACLE_LOG env var
func LogLevel() LogLevelType { func LogLevel() LogLevelType {
envLevel := os.Getenv("ORACLE_LOG") envLevel := os.Getenv("ORACLE_LOG")
if envLevel == "" { if envLevel == "" {
return LogOff return LogOff
} else {
return LogDebug
} }
return LogDebug
} }

18
vendor/vendor.json vendored
View File

@ -1020,22 +1020,22 @@
"revision": "d30f09973e19c1dfcd120b2d9c4f168e68d6b5d5" "revision": "d30f09973e19c1dfcd120b2d9c4f168e68d6b5d5"
}, },
{ {
"checksumSHA1": "hjQfXn32Tvuu6IJACOTsMzm+AbA=", "checksumSHA1": "2HXZQWsVNPQme21DBmz9P3n9NNc=",
"path": "github.com/hashicorp/go-oracle-terraform/client", "path": "github.com/hashicorp/go-oracle-terraform/client",
"revision": "62e2241f9c4154d5603a3678adc912991a47a468", "revision": "007121241b797759456e4cdcb8ae98f431fdb7e0",
"revisionTime": "2018-01-31T23:42:02Z" "revisionTime": "2018-10-15T20:29:09Z"
}, },
{ {
"checksumSHA1": "yoA7SyeQNJ8XxwC7IcXdJ2kOTqg=", "checksumSHA1": "+7uE289ixv+xwEk99EiXKecZt5k=",
"path": "github.com/hashicorp/go-oracle-terraform/compute", "path": "github.com/hashicorp/go-oracle-terraform/compute",
"revision": "62e2241f9c4154d5603a3678adc912991a47a468", "revision": "007121241b797759456e4cdcb8ae98f431fdb7e0",
"revisionTime": "2018-01-31T23:42:02Z" "revisionTime": "2018-10-15T20:29:09Z"
}, },
{ {
"checksumSHA1": "NuObCk0/ybL3w++EnltgrB1GQRc=", "checksumSHA1": "aeXObRk6gAfuvnT+puKaEib7dm0=",
"path": "github.com/hashicorp/go-oracle-terraform/opc", "path": "github.com/hashicorp/go-oracle-terraform/opc",
"revision": "62e2241f9c4154d5603a3678adc912991a47a468", "revision": "007121241b797759456e4cdcb8ae98f431fdb7e0",
"revisionTime": "2018-01-31T23:42:02Z" "revisionTime": "2018-10-15T20:29:09Z"
}, },
{ {
"checksumSHA1": "/yKfFSspjuDzyOe/bBslrPzyfYM=", "checksumSHA1": "/yKfFSspjuDzyOe/bBslrPzyfYM=",