Migrate to go-acme/lego.
This commit is contained in:
parent
4a68d29ce2
commit
87da7520de
286 changed files with 14021 additions and 2501 deletions
339
vendor/github.com/oracle/oci-go-sdk/common/client.go
generated
vendored
Normal file
339
vendor/github.com/oracle/oci-go-sdk/common/client.go
generated
vendored
Normal file
|
@ -0,0 +1,339 @@
|
|||
// Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
|
||||
// Package common provides supporting functions and structs used by service packages
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultHostURLTemplate The default url template for service hosts
|
||||
DefaultHostURLTemplate = "%s.%s.oraclecloud.com"
|
||||
|
||||
// requestHeaderAccept The key for passing a header to indicate Accept
|
||||
requestHeaderAccept = "Accept"
|
||||
|
||||
// requestHeaderAuthorization The key for passing a header to indicate Authorization
|
||||
requestHeaderAuthorization = "Authorization"
|
||||
|
||||
// requestHeaderContentLength The key for passing a header to indicate Content Length
|
||||
requestHeaderContentLength = "Content-Length"
|
||||
|
||||
// requestHeaderContentType The key for passing a header to indicate Content Type
|
||||
requestHeaderContentType = "Content-Type"
|
||||
|
||||
// requestHeaderDate The key for passing a header to indicate Date
|
||||
requestHeaderDate = "Date"
|
||||
|
||||
// requestHeaderIfMatch The key for passing a header to indicate If Match
|
||||
requestHeaderIfMatch = "if-match"
|
||||
|
||||
// requestHeaderOpcClientInfo The key for passing a header to indicate OPC Client Info
|
||||
requestHeaderOpcClientInfo = "opc-client-info"
|
||||
|
||||
// requestHeaderOpcRetryToken The key for passing a header to indicate OPC Retry Token
|
||||
requestHeaderOpcRetryToken = "opc-retry-token"
|
||||
|
||||
// requestHeaderOpcRequestID The key for unique Oracle-assigned identifier for the request.
|
||||
requestHeaderOpcRequestID = "opc-request-id"
|
||||
|
||||
// requestHeaderOpcClientRequestID The key for unique Oracle-assigned identifier for the request.
|
||||
requestHeaderOpcClientRequestID = "opc-client-request-id"
|
||||
|
||||
// requestHeaderUserAgent The key for passing a header to indicate User Agent
|
||||
requestHeaderUserAgent = "User-Agent"
|
||||
|
||||
// requestHeaderXContentSHA256 The key for passing a header to indicate SHA256 hash
|
||||
requestHeaderXContentSHA256 = "X-Content-SHA256"
|
||||
|
||||
// private constants
|
||||
defaultScheme = "https"
|
||||
defaultSDKMarker = "Oracle-GoSDK"
|
||||
defaultUserAgentTemplate = "%s/%s (%s/%s; go/%s)" //SDK/SDKVersion (OS/OSVersion; Lang/LangVersion)
|
||||
defaultTimeout = 60 * time.Second
|
||||
defaultConfigFileName = "config"
|
||||
defaultConfigDirName = ".oci"
|
||||
secondaryConfigDirName = ".oraclebmc"
|
||||
maxBodyLenForDebug = 1024 * 1000
|
||||
)
|
||||
|
||||
// RequestInterceptor function used to customize the request before calling the underlying service
|
||||
type RequestInterceptor func(*http.Request) error
|
||||
|
||||
// HTTPRequestDispatcher wraps the execution of a http request, it is generally implemented by
|
||||
// http.Client.Do, but can be customized for testing
|
||||
type HTTPRequestDispatcher interface {
|
||||
Do(req *http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
// BaseClient struct implements all basic operations to call oci web services.
|
||||
type BaseClient struct {
|
||||
//HTTPClient performs the http network operations
|
||||
HTTPClient HTTPRequestDispatcher
|
||||
|
||||
//Signer performs auth operation
|
||||
Signer HTTPRequestSigner
|
||||
|
||||
//A request interceptor can be used to customize the request before signing and dispatching
|
||||
Interceptor RequestInterceptor
|
||||
|
||||
//The host of the service
|
||||
Host string
|
||||
|
||||
//The user agent
|
||||
UserAgent string
|
||||
|
||||
//Base path for all operations of this client
|
||||
BasePath string
|
||||
}
|
||||
|
||||
func defaultUserAgent() string {
|
||||
userAgent := fmt.Sprintf(defaultUserAgentTemplate, defaultSDKMarker, Version(), runtime.GOOS, runtime.GOARCH, runtime.Version())
|
||||
return userAgent
|
||||
}
|
||||
|
||||
var clientCounter int64
|
||||
|
||||
func getNextSeed() int64 {
|
||||
newCounterValue := atomic.AddInt64(&clientCounter, 1)
|
||||
return newCounterValue + time.Now().UnixNano()
|
||||
}
|
||||
|
||||
func newBaseClient(signer HTTPRequestSigner, dispatcher HTTPRequestDispatcher) BaseClient {
|
||||
rand.Seed(getNextSeed())
|
||||
return BaseClient{
|
||||
UserAgent: defaultUserAgent(),
|
||||
Interceptor: nil,
|
||||
Signer: signer,
|
||||
HTTPClient: dispatcher,
|
||||
}
|
||||
}
|
||||
|
||||
func defaultHTTPDispatcher() http.Client {
|
||||
httpClient := http.Client{
|
||||
Timeout: defaultTimeout,
|
||||
}
|
||||
return httpClient
|
||||
}
|
||||
|
||||
func defaultBaseClient(provider KeyProvider) BaseClient {
|
||||
dispatcher := defaultHTTPDispatcher()
|
||||
signer := DefaultRequestSigner(provider)
|
||||
return newBaseClient(signer, &dispatcher)
|
||||
}
|
||||
|
||||
//DefaultBaseClientWithSigner creates a default base client with a given signer
|
||||
func DefaultBaseClientWithSigner(signer HTTPRequestSigner) BaseClient {
|
||||
dispatcher := defaultHTTPDispatcher()
|
||||
return newBaseClient(signer, &dispatcher)
|
||||
}
|
||||
|
||||
// NewClientWithConfig Create a new client with a configuration provider, the configuration provider
|
||||
// will be used for the default signer as well as reading the region
|
||||
// This function does not check for valid regions to implement forward compatibility
|
||||
func NewClientWithConfig(configProvider ConfigurationProvider) (client BaseClient, err error) {
|
||||
var ok bool
|
||||
if ok, err = IsConfigurationProviderValid(configProvider); !ok {
|
||||
err = fmt.Errorf("can not create client, bad configuration: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
client = defaultBaseClient(configProvider)
|
||||
return
|
||||
}
|
||||
|
||||
func getHomeFolder() string {
|
||||
current, e := user.Current()
|
||||
if e != nil {
|
||||
//Give up and try to return something sensible
|
||||
home := os.Getenv("HOME")
|
||||
if home == "" {
|
||||
home = os.Getenv("USERPROFILE")
|
||||
}
|
||||
return home
|
||||
}
|
||||
return current.HomeDir
|
||||
}
|
||||
|
||||
// DefaultConfigProvider returns the default config provider. The default config provider
|
||||
// will look for configurations in 3 places: file in $HOME/.oci/config, HOME/.obmcs/config and
|
||||
// variables names starting with the string TF_VAR. If the same configuration is found in multiple
|
||||
// places the provider will prefer the first one.
|
||||
func DefaultConfigProvider() ConfigurationProvider {
|
||||
homeFolder := getHomeFolder()
|
||||
defaultConfigFile := path.Join(homeFolder, defaultConfigDirName, defaultConfigFileName)
|
||||
secondaryConfigFile := path.Join(homeFolder, secondaryConfigDirName, defaultConfigFileName)
|
||||
|
||||
defaultFileProvider, _ := ConfigurationProviderFromFile(defaultConfigFile, "")
|
||||
secondaryFileProvider, _ := ConfigurationProviderFromFile(secondaryConfigFile, "")
|
||||
environmentProvider := environmentConfigurationProvider{EnvironmentVariablePrefix: "TF_VAR"}
|
||||
|
||||
provider, _ := ComposingConfigurationProvider([]ConfigurationProvider{defaultFileProvider, secondaryFileProvider, environmentProvider})
|
||||
Debugf("Configuration provided by: %s", provider)
|
||||
return provider
|
||||
}
|
||||
|
||||
func (client *BaseClient) prepareRequest(request *http.Request) (err error) {
|
||||
if client.UserAgent == "" {
|
||||
return fmt.Errorf("user agent can not be blank")
|
||||
}
|
||||
|
||||
if request.Header == nil {
|
||||
request.Header = http.Header{}
|
||||
}
|
||||
request.Header.Set(requestHeaderUserAgent, client.UserAgent)
|
||||
request.Header.Set(requestHeaderDate, time.Now().UTC().Format(http.TimeFormat))
|
||||
|
||||
if !strings.Contains(client.Host, "http") &&
|
||||
!strings.Contains(client.Host, "https") {
|
||||
client.Host = fmt.Sprintf("%s://%s", defaultScheme, client.Host)
|
||||
}
|
||||
|
||||
clientURL, err := url.Parse(client.Host)
|
||||
if err != nil {
|
||||
return fmt.Errorf("host is invalid. %s", err.Error())
|
||||
}
|
||||
request.URL.Host = clientURL.Host
|
||||
request.URL.Scheme = clientURL.Scheme
|
||||
currentPath := request.URL.Path
|
||||
if !strings.Contains(currentPath, fmt.Sprintf("/%s", client.BasePath)) {
|
||||
request.URL.Path = path.Clean(fmt.Sprintf("/%s/%s", client.BasePath, currentPath))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (client BaseClient) intercept(request *http.Request) (err error) {
|
||||
if client.Interceptor != nil {
|
||||
err = client.Interceptor(request)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func checkForSuccessfulResponse(res *http.Response) error {
|
||||
familyStatusCode := res.StatusCode / 100
|
||||
if familyStatusCode == 4 || familyStatusCode == 5 {
|
||||
return newServiceFailureFromResponse(res)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// OCIRequest is any request made to an OCI service.
|
||||
type OCIRequest interface {
|
||||
// HTTPRequest assembles an HTTP request.
|
||||
HTTPRequest(method, path string) (http.Request, error)
|
||||
}
|
||||
|
||||
// RequestMetadata is metadata about an OCIRequest. This structure represents the behavior exhibited by the SDK when
|
||||
// issuing (or reissuing) a request.
|
||||
type RequestMetadata struct {
|
||||
// RetryPolicy is the policy for reissuing the request. If no retry policy is set on the request,
|
||||
// then the request will be issued exactly once.
|
||||
RetryPolicy *RetryPolicy
|
||||
}
|
||||
|
||||
// OCIResponse is the response from issuing a request to an OCI service.
|
||||
type OCIResponse interface {
|
||||
// HTTPResponse returns the raw HTTP response.
|
||||
HTTPResponse() *http.Response
|
||||
}
|
||||
|
||||
// OCIOperation is the generalization of a request-response cycle undergone by an OCI service.
|
||||
type OCIOperation func(context.Context, OCIRequest) (OCIResponse, error)
|
||||
|
||||
//ClientCallDetails a set of settings used by the a single Call operation of the http Client
|
||||
type ClientCallDetails struct {
|
||||
Signer HTTPRequestSigner
|
||||
}
|
||||
|
||||
// Call executes the http request with the given context
|
||||
func (client BaseClient) Call(ctx context.Context, request *http.Request) (response *http.Response, err error) {
|
||||
return client.CallWithDetails(ctx, request, ClientCallDetails{Signer: client.Signer})
|
||||
}
|
||||
|
||||
// CallWithDetails executes the http request, the given context using details specified in the paremeters, this function
|
||||
// provides a way to override some settings present in the client
|
||||
func (client BaseClient) CallWithDetails(ctx context.Context, request *http.Request, details ClientCallDetails) (response *http.Response, err error) {
|
||||
Debugln("Atempting to call downstream service")
|
||||
request = request.WithContext(ctx)
|
||||
|
||||
err = client.prepareRequest(request)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
//Intercept
|
||||
err = client.intercept(request)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
//Sign the request
|
||||
err = details.Signer.Sign(request)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
IfDebug(func() {
|
||||
dumpBody := true
|
||||
if request.ContentLength > maxBodyLenForDebug {
|
||||
Debugf("not dumping body too big\n")
|
||||
dumpBody = false
|
||||
}
|
||||
dumpBody = dumpBody && defaultLogger.LogLevel() == verboseLogging
|
||||
if dump, e := httputil.DumpRequestOut(request, dumpBody); e == nil {
|
||||
Debugf("Dump Request %s", string(dump))
|
||||
} else {
|
||||
Debugf("%v\n", e)
|
||||
}
|
||||
})
|
||||
|
||||
//Execute the http request
|
||||
response, err = client.HTTPClient.Do(request)
|
||||
|
||||
IfDebug(func() {
|
||||
if err != nil {
|
||||
Debugf("%v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
dumpBody := true
|
||||
if response.ContentLength > maxBodyLenForDebug {
|
||||
Debugf("not dumping body too big\n")
|
||||
dumpBody = false
|
||||
}
|
||||
|
||||
dumpBody = dumpBody && defaultLogger.LogLevel() == verboseLogging
|
||||
if dump, e := httputil.DumpResponse(response, dumpBody); e == nil {
|
||||
Debugf("Dump Response %s", string(dump))
|
||||
} else {
|
||||
Debugf("%v\n", e)
|
||||
}
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = checkForSuccessfulResponse(response)
|
||||
return
|
||||
}
|
||||
|
||||
//CloseBodyIfValid closes the body of an http response if the response and the body are valid
|
||||
func CloseBodyIfValid(httpResponse *http.Response) {
|
||||
if httpResponse != nil && httpResponse.Body != nil {
|
||||
httpResponse.Body.Close()
|
||||
}
|
||||
}
|
137
vendor/github.com/oracle/oci-go-sdk/common/common.go
generated
vendored
Normal file
137
vendor/github.com/oracle/oci-go-sdk/common/common.go
generated
vendored
Normal file
|
@ -0,0 +1,137 @@
|
|||
// Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//Region type for regions
|
||||
type Region string
|
||||
|
||||
const (
|
||||
//RegionSEA region SEA
|
||||
RegionSEA Region = "sea"
|
||||
//RegionCAToronto1 region for toronto
|
||||
RegionCAToronto1 Region = "ca-toronto-1"
|
||||
//RegionPHX region PHX
|
||||
RegionPHX Region = "us-phoenix-1"
|
||||
//RegionIAD region IAD
|
||||
RegionIAD Region = "us-ashburn-1"
|
||||
//RegionFRA region FRA
|
||||
RegionFRA Region = "eu-frankfurt-1"
|
||||
//RegionLHR region LHR
|
||||
RegionLHR Region = "uk-london-1"
|
||||
|
||||
//RegionUSLangley1 region for langley
|
||||
RegionUSLangley1 Region = "us-langley-1"
|
||||
//RegionUSLuke1 region for luke
|
||||
RegionUSLuke1 Region = "us-luke-1"
|
||||
|
||||
//RegionUSGovAshburn1 region for langley
|
||||
RegionUSGovAshburn1 Region = "us-gov-ashburn-1"
|
||||
//RegionUSGovChicago1 region for luke
|
||||
RegionUSGovChicago1 Region = "us-gov-chicago-1"
|
||||
//RegionUSGovPhoenix1 region for luke
|
||||
RegionUSGovPhoenix1 Region = "us-gov-phoenix-1"
|
||||
)
|
||||
|
||||
var realm = map[string]string{
|
||||
"oc1": "oraclecloud.com",
|
||||
"oc2": "oraclegovcloud.com",
|
||||
"oc3": "oraclegovcloud.com",
|
||||
}
|
||||
|
||||
var regionRealm = map[Region]string{
|
||||
RegionPHX: "oc1",
|
||||
RegionIAD: "oc1",
|
||||
RegionFRA: "oc1",
|
||||
RegionLHR: "oc1",
|
||||
RegionCAToronto1: "oc1",
|
||||
|
||||
RegionUSLangley1: "oc2",
|
||||
RegionUSLuke1: "oc2",
|
||||
|
||||
RegionUSGovAshburn1: "oc3",
|
||||
RegionUSGovChicago1: "oc3",
|
||||
RegionUSGovPhoenix1: "oc3",
|
||||
}
|
||||
|
||||
// Endpoint returns a endpoint for a service
|
||||
func (region Region) Endpoint(service string) string {
|
||||
return fmt.Sprintf("%s.%s.%s", service, region, region.secondLevelDomain())
|
||||
}
|
||||
|
||||
// EndpointForTemplate returns a endpoint for a service based on template
|
||||
func (region Region) EndpointForTemplate(service string, serviceEndpointTemplate string) string {
|
||||
if serviceEndpointTemplate == "" {
|
||||
return region.Endpoint(service)
|
||||
}
|
||||
|
||||
// replace service prefix
|
||||
endpoint := strings.Replace(serviceEndpointTemplate, "{serviceEndpointPrefix}", service, 1)
|
||||
|
||||
// replace region
|
||||
endpoint = strings.Replace(endpoint, "{region}", string(region), 1)
|
||||
|
||||
// replace second level domain
|
||||
endpoint = strings.Replace(endpoint, "{secondLevelDomain}", region.secondLevelDomain(), 1)
|
||||
|
||||
return endpoint
|
||||
}
|
||||
|
||||
func (region Region) secondLevelDomain() string {
|
||||
if realmID, ok := regionRealm[region]; ok {
|
||||
if secondLevelDomain, ok := realm[realmID]; ok {
|
||||
return secondLevelDomain
|
||||
}
|
||||
}
|
||||
|
||||
Debugf("cannot find realm for region : %s, return default realm value.", region)
|
||||
return realm["oc1"]
|
||||
}
|
||||
|
||||
//StringToRegion convert a string to Region type
|
||||
func StringToRegion(stringRegion string) (r Region) {
|
||||
switch strings.ToLower(stringRegion) {
|
||||
case "sea":
|
||||
r = RegionSEA
|
||||
case "ca-toronto-1":
|
||||
r = RegionCAToronto1
|
||||
case "phx", "us-phoenix-1":
|
||||
r = RegionPHX
|
||||
case "iad", "us-ashburn-1":
|
||||
r = RegionIAD
|
||||
case "fra", "eu-frankfurt-1":
|
||||
r = RegionFRA
|
||||
case "lhr", "uk-london-1":
|
||||
r = RegionLHR
|
||||
case "us-langley-1":
|
||||
r = RegionUSLangley1
|
||||
case "us-luke-1":
|
||||
r = RegionUSLuke1
|
||||
case "us-gov-ashburn-1":
|
||||
r = RegionUSGovAshburn1
|
||||
case "us-gov-chicago-1":
|
||||
r = RegionUSGovChicago1
|
||||
case "us-gov-phoenix-1":
|
||||
r = RegionUSGovPhoenix1
|
||||
default:
|
||||
r = Region(stringRegion)
|
||||
Debugf("region named: %s, is not recognized", stringRegion)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// canStringBeRegion test if the string can be a region, if it can, returns the string as is, otherwise it
|
||||
// returns an error
|
||||
var blankRegex = regexp.MustCompile("\\s")
|
||||
|
||||
func canStringBeRegion(stringRegion string) (region string, err error) {
|
||||
if blankRegex.MatchString(stringRegion) || stringRegion == "" {
|
||||
return "", fmt.Errorf("region can not be empty or have spaces")
|
||||
}
|
||||
return stringRegion, nil
|
||||
}
|
535
vendor/github.com/oracle/oci-go-sdk/common/configuration.go
generated
vendored
Normal file
535
vendor/github.com/oracle/oci-go-sdk/common/configuration.go
generated
vendored
Normal file
|
@ -0,0 +1,535 @@
|
|||
// Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ConfigurationProvider wraps information about the account owner
|
||||
type ConfigurationProvider interface {
|
||||
KeyProvider
|
||||
TenancyOCID() (string, error)
|
||||
UserOCID() (string, error)
|
||||
KeyFingerprint() (string, error)
|
||||
Region() (string, error)
|
||||
}
|
||||
|
||||
// IsConfigurationProviderValid Tests all parts of the configuration provider do not return an error
|
||||
func IsConfigurationProviderValid(conf ConfigurationProvider) (ok bool, err error) {
|
||||
baseFn := []func() (string, error){conf.TenancyOCID, conf.UserOCID, conf.KeyFingerprint, conf.Region, conf.KeyID}
|
||||
for _, fn := range baseFn {
|
||||
_, err = fn()
|
||||
ok = err == nil
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
_, err = conf.PrivateRSAKey()
|
||||
ok = err == nil
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// rawConfigurationProvider allows a user to simply construct a configuration provider from raw values.
|
||||
type rawConfigurationProvider struct {
|
||||
tenancy string
|
||||
user string
|
||||
region string
|
||||
fingerprint string
|
||||
privateKey string
|
||||
privateKeyPassphrase *string
|
||||
}
|
||||
|
||||
// NewRawConfigurationProvider will create a rawConfigurationProvider
|
||||
func NewRawConfigurationProvider(tenancy, user, region, fingerprint, privateKey string, privateKeyPassphrase *string) ConfigurationProvider {
|
||||
return rawConfigurationProvider{tenancy, user, region, fingerprint, privateKey, privateKeyPassphrase}
|
||||
}
|
||||
|
||||
func (p rawConfigurationProvider) PrivateRSAKey() (key *rsa.PrivateKey, err error) {
|
||||
return PrivateKeyFromBytes([]byte(p.privateKey), p.privateKeyPassphrase)
|
||||
}
|
||||
|
||||
func (p rawConfigurationProvider) KeyID() (keyID string, err error) {
|
||||
tenancy, err := p.TenancyOCID()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
user, err := p.UserOCID()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fingerprint, err := p.KeyFingerprint()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s/%s/%s", tenancy, user, fingerprint), nil
|
||||
}
|
||||
|
||||
func (p rawConfigurationProvider) TenancyOCID() (string, error) {
|
||||
if p.tenancy == "" {
|
||||
return "", fmt.Errorf("tenancy OCID can not be empty")
|
||||
}
|
||||
return p.tenancy, nil
|
||||
}
|
||||
|
||||
func (p rawConfigurationProvider) UserOCID() (string, error) {
|
||||
if p.user == "" {
|
||||
return "", fmt.Errorf("user OCID can not be empty")
|
||||
}
|
||||
return p.user, nil
|
||||
}
|
||||
|
||||
func (p rawConfigurationProvider) KeyFingerprint() (string, error) {
|
||||
if p.fingerprint == "" {
|
||||
return "", fmt.Errorf("fingerprint can not be empty")
|
||||
}
|
||||
return p.fingerprint, nil
|
||||
}
|
||||
|
||||
func (p rawConfigurationProvider) Region() (string, error) {
|
||||
return canStringBeRegion(p.region)
|
||||
}
|
||||
|
||||
// environmentConfigurationProvider reads configuration from environment variables
|
||||
type environmentConfigurationProvider struct {
|
||||
PrivateKeyPassword string
|
||||
EnvironmentVariablePrefix string
|
||||
}
|
||||
|
||||
// ConfigurationProviderEnvironmentVariables creates a ConfigurationProvider from a uniform set of environment variables starting with a prefix
|
||||
// The env variables should look like: [prefix]_private_key_path, [prefix]_tenancy_ocid, [prefix]_user_ocid, [prefix]_fingerprint
|
||||
// [prefix]_region
|
||||
func ConfigurationProviderEnvironmentVariables(environmentVariablePrefix, privateKeyPassword string) ConfigurationProvider {
|
||||
return environmentConfigurationProvider{EnvironmentVariablePrefix: environmentVariablePrefix,
|
||||
PrivateKeyPassword: privateKeyPassword}
|
||||
}
|
||||
|
||||
func (p environmentConfigurationProvider) String() string {
|
||||
return fmt.Sprintf("Configuration provided by environment variables prefixed with: %s", p.EnvironmentVariablePrefix)
|
||||
}
|
||||
|
||||
func (p environmentConfigurationProvider) PrivateRSAKey() (key *rsa.PrivateKey, err error) {
|
||||
environmentVariable := fmt.Sprintf("%s_%s", p.EnvironmentVariablePrefix, "private_key_path")
|
||||
var ok bool
|
||||
var value string
|
||||
if value, ok = os.LookupEnv(environmentVariable); !ok {
|
||||
return nil, fmt.Errorf("can not read PrivateKey from env variable: %s", environmentVariable)
|
||||
}
|
||||
|
||||
expandedPath := expandPath(value)
|
||||
pemFileContent, err := ioutil.ReadFile(expandedPath)
|
||||
if err != nil {
|
||||
Debugln("Can not read PrivateKey location from environment variable: " + environmentVariable)
|
||||
return
|
||||
}
|
||||
|
||||
key, err = PrivateKeyFromBytes(pemFileContent, &p.PrivateKeyPassword)
|
||||
return
|
||||
}
|
||||
|
||||
func (p environmentConfigurationProvider) KeyID() (keyID string, err error) {
|
||||
ocid, err := p.TenancyOCID()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
userocid, err := p.UserOCID()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fingerprint, err := p.KeyFingerprint()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s/%s/%s", ocid, userocid, fingerprint), nil
|
||||
}
|
||||
|
||||
func (p environmentConfigurationProvider) TenancyOCID() (value string, err error) {
|
||||
environmentVariable := fmt.Sprintf("%s_%s", p.EnvironmentVariablePrefix, "tenancy_ocid")
|
||||
var ok bool
|
||||
if value, ok = os.LookupEnv(environmentVariable); !ok {
|
||||
err = fmt.Errorf("can not read Tenancy from environment variable %s", environmentVariable)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p environmentConfigurationProvider) UserOCID() (value string, err error) {
|
||||
environmentVariable := fmt.Sprintf("%s_%s", p.EnvironmentVariablePrefix, "user_ocid")
|
||||
var ok bool
|
||||
if value, ok = os.LookupEnv(environmentVariable); !ok {
|
||||
err = fmt.Errorf("can not read user id from environment variable %s", environmentVariable)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p environmentConfigurationProvider) KeyFingerprint() (value string, err error) {
|
||||
environmentVariable := fmt.Sprintf("%s_%s", p.EnvironmentVariablePrefix, "fingerprint")
|
||||
var ok bool
|
||||
if value, ok = os.LookupEnv(environmentVariable); !ok {
|
||||
err = fmt.Errorf("can not read fingerprint from environment variable %s", environmentVariable)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p environmentConfigurationProvider) Region() (value string, err error) {
|
||||
environmentVariable := fmt.Sprintf("%s_%s", p.EnvironmentVariablePrefix, "region")
|
||||
var ok bool
|
||||
if value, ok = os.LookupEnv(environmentVariable); !ok {
|
||||
err = fmt.Errorf("can not read region from environment variable %s", environmentVariable)
|
||||
return value, err
|
||||
}
|
||||
|
||||
return canStringBeRegion(value)
|
||||
}
|
||||
|
||||
// fileConfigurationProvider. reads configuration information from a file
|
||||
type fileConfigurationProvider struct {
|
||||
//The path to the configuration file
|
||||
ConfigPath string
|
||||
|
||||
//The password for the private key
|
||||
PrivateKeyPassword string
|
||||
|
||||
//The profile for the configuration
|
||||
Profile string
|
||||
|
||||
//ConfigFileInfo
|
||||
FileInfo *configFileInfo
|
||||
}
|
||||
|
||||
// ConfigurationProviderFromFile creates a configuration provider from a configuration file
|
||||
// by reading the "DEFAULT" profile
|
||||
func ConfigurationProviderFromFile(configFilePath, privateKeyPassword string) (ConfigurationProvider, error) {
|
||||
if configFilePath == "" {
|
||||
return nil, fmt.Errorf("config file path can not be empty")
|
||||
}
|
||||
|
||||
return fileConfigurationProvider{
|
||||
ConfigPath: configFilePath,
|
||||
PrivateKeyPassword: privateKeyPassword,
|
||||
Profile: "DEFAULT"}, nil
|
||||
}
|
||||
|
||||
// ConfigurationProviderFromFileWithProfile creates a configuration provider from a configuration file
|
||||
// and the given profile
|
||||
func ConfigurationProviderFromFileWithProfile(configFilePath, profile, privateKeyPassword string) (ConfigurationProvider, error) {
|
||||
if configFilePath == "" {
|
||||
return nil, fmt.Errorf("config file path can not be empty")
|
||||
}
|
||||
|
||||
return fileConfigurationProvider{
|
||||
ConfigPath: configFilePath,
|
||||
PrivateKeyPassword: privateKeyPassword,
|
||||
Profile: profile}, nil
|
||||
}
|
||||
|
||||
type configFileInfo struct {
|
||||
UserOcid, Fingerprint, KeyFilePath, TenancyOcid, Region, Passphrase string
|
||||
PresentConfiguration byte
|
||||
}
|
||||
|
||||
const (
|
||||
hasTenancy = 1 << iota
|
||||
hasUser
|
||||
hasFingerprint
|
||||
hasRegion
|
||||
hasKeyFile
|
||||
hasPassphrase
|
||||
none
|
||||
)
|
||||
|
||||
var profileRegex = regexp.MustCompile(`^\[(.*)\]`)
|
||||
|
||||
func parseConfigFile(data []byte, profile string) (info *configFileInfo, err error) {
|
||||
|
||||
if len(data) == 0 {
|
||||
return nil, fmt.Errorf("configuration file content is empty")
|
||||
}
|
||||
|
||||
content := string(data)
|
||||
splitContent := strings.Split(content, "\n")
|
||||
|
||||
//Look for profile
|
||||
for i, line := range splitContent {
|
||||
if match := profileRegex.FindStringSubmatch(line); match != nil && len(match) > 1 && match[1] == profile {
|
||||
start := i + 1
|
||||
return parseConfigAtLine(start, splitContent)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("configuration file did not contain profile: %s", profile)
|
||||
}
|
||||
|
||||
func parseConfigAtLine(start int, content []string) (info *configFileInfo, err error) {
|
||||
var configurationPresent byte
|
||||
info = &configFileInfo{}
|
||||
for i := start; i < len(content); i++ {
|
||||
line := content[i]
|
||||
if profileRegex.MatchString(line) {
|
||||
break
|
||||
}
|
||||
|
||||
if !strings.Contains(line, "=") {
|
||||
continue
|
||||
}
|
||||
|
||||
splits := strings.Split(line, "=")
|
||||
switch key, value := strings.TrimSpace(splits[0]), strings.TrimSpace(splits[1]); strings.ToLower(key) {
|
||||
case "passphrase", "pass_phrase":
|
||||
configurationPresent = configurationPresent | hasPassphrase
|
||||
info.Passphrase = value
|
||||
case "user":
|
||||
configurationPresent = configurationPresent | hasUser
|
||||
info.UserOcid = value
|
||||
case "fingerprint":
|
||||
configurationPresent = configurationPresent | hasFingerprint
|
||||
info.Fingerprint = value
|
||||
case "key_file":
|
||||
configurationPresent = configurationPresent | hasKeyFile
|
||||
info.KeyFilePath = value
|
||||
case "tenancy":
|
||||
configurationPresent = configurationPresent | hasTenancy
|
||||
info.TenancyOcid = value
|
||||
case "region":
|
||||
configurationPresent = configurationPresent | hasRegion
|
||||
info.Region = value
|
||||
}
|
||||
}
|
||||
info.PresentConfiguration = configurationPresent
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
// cleans and expands the path if it contains a tilde , returns the expanded path or the input path as is if not expansion
|
||||
// was performed
|
||||
func expandPath(filepath string) (expandedPath string) {
|
||||
cleanedPath := path.Clean(filepath)
|
||||
expandedPath = cleanedPath
|
||||
if strings.HasPrefix(cleanedPath, "~") {
|
||||
rest := cleanedPath[2:]
|
||||
expandedPath = path.Join(getHomeFolder(), rest)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func openConfigFile(configFilePath string) (data []byte, err error) {
|
||||
expandedPath := expandPath(configFilePath)
|
||||
data, err = ioutil.ReadFile(expandedPath)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("can not read config file: %s due to: %s", configFilePath, err.Error())
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (p fileConfigurationProvider) String() string {
|
||||
return fmt.Sprintf("Configuration provided by file: %s", p.ConfigPath)
|
||||
}
|
||||
|
||||
func (p fileConfigurationProvider) readAndParseConfigFile() (info *configFileInfo, err error) {
|
||||
if p.FileInfo != nil {
|
||||
return p.FileInfo, nil
|
||||
}
|
||||
|
||||
if p.ConfigPath == "" {
|
||||
return nil, fmt.Errorf("configuration path can not be empty")
|
||||
}
|
||||
|
||||
data, err := openConfigFile(p.ConfigPath)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error while parsing config file: %s. Due to: %s", p.ConfigPath, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
p.FileInfo, err = parseConfigFile(data, p.Profile)
|
||||
return p.FileInfo, err
|
||||
}
|
||||
|
||||
func presentOrError(value string, expectedConf, presentConf byte, confMissing string) (string, error) {
|
||||
if presentConf&expectedConf == expectedConf {
|
||||
return value, nil
|
||||
}
|
||||
return "", errors.New(confMissing + " configuration is missing from file")
|
||||
}
|
||||
|
||||
func (p fileConfigurationProvider) TenancyOCID() (value string, err error) {
|
||||
info, err := p.readAndParseConfigFile()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("can not read tenancy configuration due to: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
value, err = presentOrError(info.TenancyOcid, hasTenancy, info.PresentConfiguration, "tenancy")
|
||||
return
|
||||
}
|
||||
|
||||
func (p fileConfigurationProvider) UserOCID() (value string, err error) {
|
||||
info, err := p.readAndParseConfigFile()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("can not read tenancy configuration due to: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
value, err = presentOrError(info.UserOcid, hasUser, info.PresentConfiguration, "user")
|
||||
return
|
||||
}
|
||||
|
||||
func (p fileConfigurationProvider) KeyFingerprint() (value string, err error) {
|
||||
info, err := p.readAndParseConfigFile()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("can not read tenancy configuration due to: %s", err.Error())
|
||||
return
|
||||
}
|
||||
value, err = presentOrError(info.Fingerprint, hasFingerprint, info.PresentConfiguration, "fingerprint")
|
||||
return
|
||||
}
|
||||
|
||||
func (p fileConfigurationProvider) KeyID() (keyID string, err error) {
|
||||
info, err := p.readAndParseConfigFile()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("can not read tenancy configuration due to: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s/%s/%s", info.TenancyOcid, info.UserOcid, info.Fingerprint), nil
|
||||
}
|
||||
|
||||
func (p fileConfigurationProvider) PrivateRSAKey() (key *rsa.PrivateKey, err error) {
|
||||
info, err := p.readAndParseConfigFile()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("can not read tenancy configuration due to: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
filePath, err := presentOrError(info.KeyFilePath, hasKeyFile, info.PresentConfiguration, "key file path")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
expandedPath := expandPath(filePath)
|
||||
pemFileContent, err := ioutil.ReadFile(expandedPath)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("can not read PrivateKey from configuration file due to: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
password := p.PrivateKeyPassword
|
||||
|
||||
if password == "" && ((info.PresentConfiguration & hasPassphrase) == hasPassphrase) {
|
||||
password = info.Passphrase
|
||||
}
|
||||
|
||||
key, err = PrivateKeyFromBytes(pemFileContent, &password)
|
||||
return
|
||||
}
|
||||
|
||||
func (p fileConfigurationProvider) Region() (value string, err error) {
|
||||
info, err := p.readAndParseConfigFile()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("can not read region configuration due to: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
value, err = presentOrError(info.Region, hasRegion, info.PresentConfiguration, "region")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return canStringBeRegion(value)
|
||||
}
|
||||
|
||||
// A configuration provider that look for information in multiple configuration providers
|
||||
type composingConfigurationProvider struct {
|
||||
Providers []ConfigurationProvider
|
||||
}
|
||||
|
||||
// ComposingConfigurationProvider creates a composing configuration provider with the given slice of configuration providers
|
||||
// A composing provider will return the configuration of the first provider that has the required property
|
||||
// if no provider has the property it will return an error.
|
||||
func ComposingConfigurationProvider(providers []ConfigurationProvider) (ConfigurationProvider, error) {
|
||||
if len(providers) == 0 {
|
||||
return nil, fmt.Errorf("providers can not be an empty slice")
|
||||
}
|
||||
|
||||
for i, p := range providers {
|
||||
if p == nil {
|
||||
return nil, fmt.Errorf("provider in position: %d is nil. ComposingConfiurationProvider does not support nil values", i)
|
||||
}
|
||||
}
|
||||
return composingConfigurationProvider{Providers: providers}, nil
|
||||
}
|
||||
|
||||
func (c composingConfigurationProvider) TenancyOCID() (string, error) {
|
||||
for _, p := range c.Providers {
|
||||
val, err := p.TenancyOCID()
|
||||
if err == nil {
|
||||
return val, nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("did not find a proper configuration for tenancy")
|
||||
}
|
||||
|
||||
func (c composingConfigurationProvider) UserOCID() (string, error) {
|
||||
for _, p := range c.Providers {
|
||||
val, err := p.UserOCID()
|
||||
if err == nil {
|
||||
return val, nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("did not find a proper configuration for user")
|
||||
}
|
||||
|
||||
func (c composingConfigurationProvider) KeyFingerprint() (string, error) {
|
||||
for _, p := range c.Providers {
|
||||
val, err := p.KeyFingerprint()
|
||||
if err == nil {
|
||||
return val, nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("did not find a proper configuration for keyFingerprint")
|
||||
}
|
||||
func (c composingConfigurationProvider) Region() (string, error) {
|
||||
for _, p := range c.Providers {
|
||||
val, err := p.Region()
|
||||
if err == nil {
|
||||
return val, nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("did not find a proper configuration for region")
|
||||
}
|
||||
|
||||
func (c composingConfigurationProvider) KeyID() (string, error) {
|
||||
for _, p := range c.Providers {
|
||||
val, err := p.KeyID()
|
||||
if err == nil {
|
||||
return val, nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("did not find a proper configuration for key id")
|
||||
}
|
||||
|
||||
func (c composingConfigurationProvider) PrivateRSAKey() (*rsa.PrivateKey, error) {
|
||||
for _, p := range c.Providers {
|
||||
val, err := p.PrivateRSAKey()
|
||||
if err == nil {
|
||||
return val, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("did not find a proper configuration for private key")
|
||||
}
|
98
vendor/github.com/oracle/oci-go-sdk/common/errors.go
generated
vendored
Normal file
98
vendor/github.com/oracle/oci-go-sdk/common/errors.go
generated
vendored
Normal file
|
@ -0,0 +1,98 @@
|
|||
// Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// ServiceError models all potential errors generated the service call
|
||||
type ServiceError interface {
|
||||
// The http status code of the error
|
||||
GetHTTPStatusCode() int
|
||||
|
||||
// The human-readable error string as sent by the service
|
||||
GetMessage() string
|
||||
|
||||
// A short error code that defines the error, meant for programmatic parsing.
|
||||
// See https://docs.cloud.oracle.com/Content/API/References/apierrors.htm
|
||||
GetCode() string
|
||||
|
||||
// Unique Oracle-assigned identifier for the request.
|
||||
// If you need to contact Oracle about a particular request, please provide the request ID.
|
||||
GetOpcRequestID() string
|
||||
}
|
||||
|
||||
type servicefailure struct {
|
||||
StatusCode int
|
||||
Code string `json:"code,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
OpcRequestID string `json:"opc-request-id"`
|
||||
}
|
||||
|
||||
func newServiceFailureFromResponse(response *http.Response) error {
|
||||
var err error
|
||||
|
||||
se := servicefailure{
|
||||
StatusCode: response.StatusCode,
|
||||
Code: "BadErrorResponse",
|
||||
OpcRequestID: response.Header.Get("opc-request-id")}
|
||||
|
||||
//If there is an error consume the body, entirely
|
||||
body, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
se.Message = fmt.Sprintf("The body of the response was not readable, due to :%s", err.Error())
|
||||
return se
|
||||
}
|
||||
|
||||
err = json.Unmarshal(body, &se)
|
||||
if err != nil {
|
||||
Debugf("Error response could not be parsed due to: %s", err.Error())
|
||||
se.Message = fmt.Sprintf("Failed to parse json from response body due to: %s. With response body %s.", err.Error(), string(body[:]))
|
||||
return se
|
||||
}
|
||||
return se
|
||||
}
|
||||
|
||||
func (se servicefailure) Error() string {
|
||||
return fmt.Sprintf("Service error:%s. %s. http status code: %d. Opc request id: %s",
|
||||
se.Code, se.Message, se.StatusCode, se.OpcRequestID)
|
||||
}
|
||||
|
||||
func (se servicefailure) GetHTTPStatusCode() int {
|
||||
return se.StatusCode
|
||||
|
||||
}
|
||||
|
||||
func (se servicefailure) GetMessage() string {
|
||||
return se.Message
|
||||
}
|
||||
|
||||
func (se servicefailure) GetCode() string {
|
||||
return se.Code
|
||||
}
|
||||
|
||||
func (se servicefailure) GetOpcRequestID() string {
|
||||
return se.OpcRequestID
|
||||
}
|
||||
|
||||
// IsServiceError returns false if the error is not service side, otherwise true
|
||||
// additionally it returns an interface representing the ServiceError
|
||||
func IsServiceError(err error) (failure ServiceError, ok bool) {
|
||||
failure, ok = err.(servicefailure)
|
||||
return
|
||||
}
|
||||
|
||||
type deadlineExceededByBackoffError struct{}
|
||||
|
||||
func (deadlineExceededByBackoffError) Error() string {
|
||||
return "now() + computed backoff duration exceeds request deadline"
|
||||
}
|
||||
|
||||
// DeadlineExceededByBackoff is the error returned by Call() when GetNextDuration() returns a time.Duration that would
|
||||
// force the user to wait past the request deadline before re-issuing a request. This enables us to exit early, since
|
||||
// we cannot succeed based on the configured retry policy.
|
||||
var DeadlineExceededByBackoff error = deadlineExceededByBackoffError{}
|
245
vendor/github.com/oracle/oci-go-sdk/common/helpers.go
generated
vendored
Normal file
245
vendor/github.com/oracle/oci-go-sdk/common/helpers.go
generated
vendored
Normal file
|
@ -0,0 +1,245 @@
|
|||
// Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// String returns a pointer to the provided string
|
||||
func String(value string) *string {
|
||||
return &value
|
||||
}
|
||||
|
||||
// Int returns a pointer to the provided int
|
||||
func Int(value int) *int {
|
||||
return &value
|
||||
}
|
||||
|
||||
// Int64 returns a pointer to the provided int64
|
||||
func Int64(value int64) *int64 {
|
||||
return &value
|
||||
}
|
||||
|
||||
// Uint returns a pointer to the provided uint
|
||||
func Uint(value uint) *uint {
|
||||
return &value
|
||||
}
|
||||
|
||||
//Float32 returns a pointer to the provided float32
|
||||
func Float32(value float32) *float32 {
|
||||
return &value
|
||||
}
|
||||
|
||||
//Float64 returns a pointer to the provided float64
|
||||
func Float64(value float64) *float64 {
|
||||
return &value
|
||||
}
|
||||
|
||||
//Bool returns a pointer to the provided bool
|
||||
func Bool(value bool) *bool {
|
||||
return &value
|
||||
}
|
||||
|
||||
//PointerString prints the values of pointers in a struct
|
||||
//Producing a human friendly string for an struct with pointers.
|
||||
//useful when debugging the values of a struct
|
||||
func PointerString(datastruct interface{}) (representation string) {
|
||||
val := reflect.ValueOf(datastruct)
|
||||
typ := reflect.TypeOf(datastruct)
|
||||
all := make([]string, 2)
|
||||
all = append(all, "{")
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
sf := typ.Field(i)
|
||||
|
||||
//unexported
|
||||
if sf.PkgPath != "" && !sf.Anonymous {
|
||||
continue
|
||||
}
|
||||
|
||||
sv := val.Field(i)
|
||||
stringValue := ""
|
||||
if isNil(sv) {
|
||||
stringValue = fmt.Sprintf("%s=<nil>", sf.Name)
|
||||
} else {
|
||||
if sv.Type().Kind() == reflect.Ptr {
|
||||
sv = sv.Elem()
|
||||
}
|
||||
stringValue = fmt.Sprintf("%s=%v", sf.Name, sv)
|
||||
}
|
||||
all = append(all, stringValue)
|
||||
}
|
||||
all = append(all, "}")
|
||||
representation = strings.TrimSpace(strings.Join(all, " "))
|
||||
return
|
||||
}
|
||||
|
||||
// SDKTime a struct that parses/renders to/from json using RFC339 date-time information
|
||||
type SDKTime struct {
|
||||
time.Time
|
||||
}
|
||||
|
||||
// SDKDate a struct that parses/renders to/from json using only date information
|
||||
type SDKDate struct {
|
||||
//Date date information
|
||||
Date time.Time
|
||||
}
|
||||
|
||||
func sdkTimeFromTime(t time.Time) SDKTime {
|
||||
return SDKTime{t}
|
||||
}
|
||||
|
||||
func sdkDateFromTime(t time.Time) SDKDate {
|
||||
return SDKDate{Date: t}
|
||||
}
|
||||
|
||||
func formatTime(t SDKTime) string {
|
||||
return t.Format(sdkTimeFormat)
|
||||
}
|
||||
|
||||
func formatDate(t SDKDate) string {
|
||||
return t.Date.Format(sdkDateFormat)
|
||||
}
|
||||
|
||||
func now() *SDKTime {
|
||||
t := SDKTime{time.Now()}
|
||||
return &t
|
||||
}
|
||||
|
||||
var timeType = reflect.TypeOf(SDKTime{})
|
||||
var timeTypePtr = reflect.TypeOf(&SDKTime{})
|
||||
|
||||
var sdkDateType = reflect.TypeOf(SDKDate{})
|
||||
var sdkDateTypePtr = reflect.TypeOf(&SDKDate{})
|
||||
|
||||
//Formats for sdk supported time representations
|
||||
const sdkTimeFormat = time.RFC3339Nano
|
||||
const rfc1123OptionalLeadingDigitsInDay = "Mon, _2 Jan 2006 15:04:05 MST"
|
||||
const sdkDateFormat = "2006-01-02"
|
||||
|
||||
func tryParsingTimeWithValidFormatsForHeaders(data []byte, headerName string) (t time.Time, err error) {
|
||||
header := strings.ToLower(headerName)
|
||||
switch header {
|
||||
case "lastmodified", "date":
|
||||
t, err = tryParsing(data, time.RFC3339Nano, time.RFC3339, time.RFC1123, rfc1123OptionalLeadingDigitsInDay, time.RFC850, time.ANSIC)
|
||||
return
|
||||
default: //By default we parse with RFC3339
|
||||
t, err = time.Parse(sdkTimeFormat, string(data))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func tryParsing(data []byte, layouts ...string) (tm time.Time, err error) {
|
||||
datestring := string(data)
|
||||
for _, l := range layouts {
|
||||
tm, err = time.Parse(l, datestring)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
err = fmt.Errorf("Could not parse time: %s with formats: %s", datestring, layouts[:])
|
||||
return
|
||||
}
|
||||
|
||||
// String returns string representation of SDKDate
|
||||
func (t *SDKDate) String() string {
|
||||
return t.Date.Format(sdkDateFormat)
|
||||
}
|
||||
|
||||
// NewSDKDateFromString parses the dateString into SDKDate
|
||||
func NewSDKDateFromString(dateString string) (*SDKDate, error) {
|
||||
parsedTime, err := time.Parse(sdkDateFormat, dateString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &SDKDate{Date: parsedTime}, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals from json
|
||||
func (t *SDKTime) UnmarshalJSON(data []byte) (e error) {
|
||||
s := string(data)
|
||||
if s == "null" {
|
||||
t.Time = time.Time{}
|
||||
} else {
|
||||
//Try parsing with RFC3339
|
||||
t.Time, e = time.Parse(`"`+sdkTimeFormat+`"`, string(data))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalJSON marshals to JSON
|
||||
func (t *SDKTime) MarshalJSON() (buff []byte, e error) {
|
||||
s := t.Format(sdkTimeFormat)
|
||||
buff = []byte(`"` + s + `"`)
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals from json
|
||||
func (t *SDKDate) UnmarshalJSON(data []byte) (e error) {
|
||||
if string(data) == `"null"` {
|
||||
t.Date = time.Time{}
|
||||
return
|
||||
}
|
||||
|
||||
t.Date, e = tryParsing(data,
|
||||
strconv.Quote(sdkDateFormat),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalJSON marshals to JSON
|
||||
func (t *SDKDate) MarshalJSON() (buff []byte, e error) {
|
||||
s := t.Date.Format(sdkDateFormat)
|
||||
buff = []byte(strconv.Quote(s))
|
||||
return
|
||||
}
|
||||
|
||||
// PrivateKeyFromBytes is a helper function that will produce a RSA private
|
||||
// key from bytes.
|
||||
func PrivateKeyFromBytes(pemData []byte, password *string) (key *rsa.PrivateKey, e error) {
|
||||
if pemBlock, _ := pem.Decode(pemData); pemBlock != nil {
|
||||
decrypted := pemBlock.Bytes
|
||||
if x509.IsEncryptedPEMBlock(pemBlock) {
|
||||
if password == nil {
|
||||
e = fmt.Errorf("private_key_password is required for encrypted private keys")
|
||||
return
|
||||
}
|
||||
if decrypted, e = x509.DecryptPEMBlock(pemBlock, []byte(*password)); e != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
key, e = x509.ParsePKCS1PrivateKey(decrypted)
|
||||
|
||||
} else {
|
||||
e = fmt.Errorf("PEM data was not found in buffer")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func generateRandUUID() (string, error) {
|
||||
b := make([]byte, 16)
|
||||
_, err := rand.Read(b)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
uuid := fmt.Sprintf("%x%x%x%x%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
|
||||
|
||||
return uuid, nil
|
||||
}
|
||||
|
||||
func makeACopy(original []string) []string {
|
||||
tmp := make([]string, len(original))
|
||||
copy(tmp, original)
|
||||
return tmp
|
||||
}
|
961
vendor/github.com/oracle/oci-go-sdk/common/http.go
generated
vendored
Normal file
961
vendor/github.com/oracle/oci-go-sdk/common/http.go
generated
vendored
Normal file
|
@ -0,0 +1,961 @@
|
|||
// Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//Request Marshaling
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func isNil(v reflect.Value) bool {
|
||||
return v.Kind() == reflect.Ptr && v.IsNil()
|
||||
}
|
||||
|
||||
// Returns the string representation of a reflect.Value
|
||||
// Only transforms primitive values
|
||||
func toStringValue(v reflect.Value, field reflect.StructField) (string, error) {
|
||||
if v.Kind() == reflect.Ptr {
|
||||
if v.IsNil() {
|
||||
return "", fmt.Errorf("can not marshal a nil pointer")
|
||||
}
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
if v.Type() == timeType {
|
||||
t := v.Interface().(SDKTime)
|
||||
return formatTime(t), nil
|
||||
}
|
||||
|
||||
if v.Type() == sdkDateType {
|
||||
t := v.Interface().(SDKDate)
|
||||
return formatDate(t), nil
|
||||
}
|
||||
|
||||
switch v.Kind() {
|
||||
case reflect.Bool:
|
||||
return strconv.FormatBool(v.Bool()), nil
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return strconv.FormatInt(v.Int(), 10), nil
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return strconv.FormatUint(v.Uint(), 10), nil
|
||||
case reflect.String:
|
||||
return v.String(), nil
|
||||
case reflect.Float32:
|
||||
return strconv.FormatFloat(v.Float(), 'f', 6, 32), nil
|
||||
case reflect.Float64:
|
||||
return strconv.FormatFloat(v.Float(), 'f', 6, 64), nil
|
||||
default:
|
||||
return "", fmt.Errorf("marshaling structure to a http.Request does not support field named: %s of type: %v",
|
||||
field.Name, v.Type().String())
|
||||
}
|
||||
}
|
||||
|
||||
func addBinaryBody(request *http.Request, value reflect.Value) (e error) {
|
||||
readCloser, ok := value.Interface().(io.ReadCloser)
|
||||
if !ok {
|
||||
e = fmt.Errorf("body of the request needs to be an io.ReadCloser interface. Can not marshal body of binary request")
|
||||
return
|
||||
}
|
||||
|
||||
request.Body = readCloser
|
||||
|
||||
//Set the default content type to application/octet-stream if not set
|
||||
if request.Header.Get(requestHeaderContentType) == "" {
|
||||
request.Header.Set(requestHeaderContentType, "application/octet-stream")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getTaggedNilFieldNameOrError, evaluates if a field with json and non mandatory tags is nil
|
||||
// returns the json tag name, or an error if the tags are incorrectly present
|
||||
func getTaggedNilFieldNameOrError(field reflect.StructField, fieldValue reflect.Value) (bool, string, error) {
|
||||
currentTag := field.Tag
|
||||
jsonTag := currentTag.Get("json")
|
||||
|
||||
if jsonTag == "" {
|
||||
return false, "", fmt.Errorf("json tag is not valid for field %s", field.Name)
|
||||
}
|
||||
|
||||
partsJSONTag := strings.Split(jsonTag, ",")
|
||||
nameJSONField := partsJSONTag[0]
|
||||
|
||||
if _, ok := currentTag.Lookup("mandatory"); !ok {
|
||||
//No mandatory field set, no-op
|
||||
return false, nameJSONField, nil
|
||||
}
|
||||
isMandatory, err := strconv.ParseBool(currentTag.Get("mandatory"))
|
||||
if err != nil {
|
||||
return false, "", fmt.Errorf("mandatory tag is not valid for field %s", field.Name)
|
||||
}
|
||||
|
||||
// If the field is marked as mandatory, no-op
|
||||
if isMandatory {
|
||||
return false, nameJSONField, nil
|
||||
}
|
||||
|
||||
Debugf("Adjusting tag: mandatory is false and json tag is valid on field: %s", field.Name)
|
||||
|
||||
// If the field can not be nil, then no-op
|
||||
if !isNillableType(&fieldValue) {
|
||||
Debugf("WARNING json field is tagged with mandatory flags, but the type can not be nil, field name: %s", field.Name)
|
||||
return false, nameJSONField, nil
|
||||
}
|
||||
|
||||
// If field value is nil, tag it as omitEmpty
|
||||
return fieldValue.IsNil(), nameJSONField, nil
|
||||
|
||||
}
|
||||
|
||||
// isNillableType returns true if the filed can be nil
|
||||
func isNillableType(value *reflect.Value) bool {
|
||||
k := value.Kind()
|
||||
switch k {
|
||||
case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Slice:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// omitNilFieldsInJSON, removes json keys whose struct value is nil, and the field is tagged with the json and
|
||||
// mandatory:false tags
|
||||
func omitNilFieldsInJSON(data interface{}, value reflect.Value) (interface{}, error) {
|
||||
switch value.Kind() {
|
||||
case reflect.Struct:
|
||||
jsonMap := data.(map[string]interface{})
|
||||
fieldType := value.Type()
|
||||
for i := 0; i < fieldType.NumField(); i++ {
|
||||
currentField := fieldType.Field(i)
|
||||
//unexported skip
|
||||
if currentField.PkgPath != "" {
|
||||
continue
|
||||
}
|
||||
|
||||
//Does not have json tag, no-op
|
||||
if _, ok := currentField.Tag.Lookup("json"); !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
currentFieldValue := value.Field(i)
|
||||
ok, jsonFieldName, err := getTaggedNilFieldNameOrError(currentField, currentFieldValue)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can not omit nil fields for field: %s, due to: %s",
|
||||
currentField.Name, err.Error())
|
||||
}
|
||||
|
||||
//Delete the struct field from the json representation
|
||||
if ok {
|
||||
delete(jsonMap, jsonFieldName)
|
||||
continue
|
||||
}
|
||||
|
||||
// Check to make sure the field is part of the json representation of the value
|
||||
if _, contains := jsonMap[jsonFieldName]; !contains {
|
||||
Debugf("Field %s is not present in json, omitting", jsonFieldName)
|
||||
continue
|
||||
}
|
||||
|
||||
if currentFieldValue.Type() == timeType || currentFieldValue.Type() == timeTypePtr ||
|
||||
currentField.Type == sdkDateType || currentField.Type == sdkDateTypePtr {
|
||||
continue
|
||||
}
|
||||
// does it need to be adjusted?
|
||||
var adjustedValue interface{}
|
||||
adjustedValue, err = omitNilFieldsInJSON(jsonMap[jsonFieldName], currentFieldValue)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can not omit nil fields for field: %s, due to: %s",
|
||||
currentField.Name, err.Error())
|
||||
}
|
||||
jsonMap[jsonFieldName] = adjustedValue
|
||||
}
|
||||
return jsonMap, nil
|
||||
case reflect.Slice, reflect.Array:
|
||||
// Special case: a []byte may have been marshalled as a string
|
||||
if data != nil && reflect.TypeOf(data).Kind() == reflect.String && value.Type().Elem().Kind() == reflect.Uint8 {
|
||||
return data, nil
|
||||
}
|
||||
jsonList, ok := data.([]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("can not omit nil fields, data was expected to be a not-nil list")
|
||||
}
|
||||
newList := make([]interface{}, len(jsonList))
|
||||
var err error
|
||||
for i, val := range jsonList {
|
||||
newList[i], err = omitNilFieldsInJSON(val, value.Index(i))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return newList, nil
|
||||
case reflect.Map:
|
||||
jsonMap, ok := data.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("can not omit nil fields, data was expected to be a not-nil map")
|
||||
}
|
||||
newMap := make(map[string]interface{}, len(jsonMap))
|
||||
var err error
|
||||
for key, val := range jsonMap {
|
||||
newMap[key], err = omitNilFieldsInJSON(val, value.MapIndex(reflect.ValueOf(key)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return newMap, nil
|
||||
case reflect.Ptr, reflect.Interface:
|
||||
valPtr := value.Elem()
|
||||
return omitNilFieldsInJSON(data, valPtr)
|
||||
default:
|
||||
//Otherwise no-op
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
|
||||
// removeNilFieldsInJSONWithTaggedStruct remove struct fields tagged with json and mandatory false
|
||||
// that are nil
|
||||
func removeNilFieldsInJSONWithTaggedStruct(rawJSON []byte, value reflect.Value) ([]byte, error) {
|
||||
var rawInterface interface{}
|
||||
decoder := json.NewDecoder(bytes.NewBuffer(rawJSON))
|
||||
decoder.UseNumber()
|
||||
var err error
|
||||
if err = decoder.Decode(&rawInterface); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fixedMap, err := omitNilFieldsInJSON(rawInterface, value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(fixedMap)
|
||||
}
|
||||
|
||||
func addToBody(request *http.Request, value reflect.Value, field reflect.StructField) (e error) {
|
||||
Debugln("Marshaling to body from field:", field.Name)
|
||||
if request.Body != nil {
|
||||
Logf("The body of the request is already set. Structure: %s will overwrite it\n", field.Name)
|
||||
}
|
||||
tag := field.Tag
|
||||
encoding := tag.Get("encoding")
|
||||
|
||||
if encoding == "binary" {
|
||||
return addBinaryBody(request, value)
|
||||
}
|
||||
|
||||
rawJSON, e := json.Marshal(value.Interface())
|
||||
if e != nil {
|
||||
return
|
||||
}
|
||||
marshaled, e := removeNilFieldsInJSONWithTaggedStruct(rawJSON, value)
|
||||
if e != nil {
|
||||
return
|
||||
}
|
||||
bodyBytes := bytes.NewReader(marshaled)
|
||||
request.ContentLength = int64(bodyBytes.Len())
|
||||
request.Header.Set(requestHeaderContentLength, strconv.FormatInt(request.ContentLength, 10))
|
||||
request.Header.Set(requestHeaderContentType, "application/json")
|
||||
request.Body = ioutil.NopCloser(bodyBytes)
|
||||
request.GetBody = func() (io.ReadCloser, error) {
|
||||
return ioutil.NopCloser(bodyBytes), nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func addToQuery(request *http.Request, value reflect.Value, field reflect.StructField) (e error) {
|
||||
Debugln("Marshaling to query from field: ", field.Name)
|
||||
if request.URL == nil {
|
||||
request.URL = &url.URL{}
|
||||
}
|
||||
query := request.URL.Query()
|
||||
var queryParameterValue, queryParameterName string
|
||||
|
||||
if queryParameterName = field.Tag.Get("name"); queryParameterName == "" {
|
||||
return fmt.Errorf("marshaling request to a query requires the 'name' tag for field: %s ", field.Name)
|
||||
}
|
||||
|
||||
mandatory, _ := strconv.ParseBool(strings.ToLower(field.Tag.Get("mandatory")))
|
||||
|
||||
//If mandatory and nil. Error out
|
||||
if mandatory && isNil(value) {
|
||||
return fmt.Errorf("marshaling request to a header requires not nil pointer for field: %s", field.Name)
|
||||
}
|
||||
|
||||
//if not mandatory and nil. Omit
|
||||
if !mandatory && isNil(value) {
|
||||
Debugf("Query parameter value is not mandatory and is nil pointer in field: %s. Skipping query", field.Name)
|
||||
return
|
||||
}
|
||||
|
||||
encoding := strings.ToLower(field.Tag.Get("collectionFormat"))
|
||||
var collectionFormatStringValues []string
|
||||
switch encoding {
|
||||
case "csv", "multi":
|
||||
if value.Kind() != reflect.Slice && value.Kind() != reflect.Array {
|
||||
e = fmt.Errorf("query parameter is tagged as csv or multi yet its type is neither an Array nor a Slice: %s", field.Name)
|
||||
break
|
||||
}
|
||||
|
||||
numOfElements := value.Len()
|
||||
collectionFormatStringValues = make([]string, numOfElements)
|
||||
for i := 0; i < numOfElements; i++ {
|
||||
collectionFormatStringValues[i], e = toStringValue(value.Index(i), field)
|
||||
if e != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
queryParameterValue = strings.Join(collectionFormatStringValues, ",")
|
||||
case "":
|
||||
queryParameterValue, e = toStringValue(value, field)
|
||||
default:
|
||||
e = fmt.Errorf("encoding of type %s is not supported for query param: %s", encoding, field.Name)
|
||||
}
|
||||
|
||||
if e != nil {
|
||||
return
|
||||
}
|
||||
|
||||
//check for tag "omitEmpty", this is done to accomodate unset fields that do not
|
||||
//support an empty string: enums in query params
|
||||
if omitEmpty, present := field.Tag.Lookup("omitEmpty"); present {
|
||||
omitEmptyBool, _ := strconv.ParseBool(strings.ToLower(omitEmpty))
|
||||
if queryParameterValue != "" || !omitEmptyBool {
|
||||
addToQueryForEncoding(&query, encoding, queryParameterName, queryParameterValue, collectionFormatStringValues)
|
||||
} else {
|
||||
Debugf("Omitting %s, is empty and omitEmpty tag is set", field.Name)
|
||||
}
|
||||
} else {
|
||||
addToQueryForEncoding(&query, encoding, queryParameterName, queryParameterValue, collectionFormatStringValues)
|
||||
}
|
||||
|
||||
request.URL.RawQuery = query.Encode()
|
||||
return
|
||||
}
|
||||
|
||||
func addToQueryForEncoding(query *url.Values, encoding string, queryParameterName string, queryParameterValue string, collectionFormatStringValues []string) {
|
||||
if encoding == "multi" {
|
||||
for _, stringValue := range collectionFormatStringValues {
|
||||
query.Add(queryParameterName, stringValue)
|
||||
}
|
||||
} else {
|
||||
query.Set(queryParameterName, queryParameterValue)
|
||||
}
|
||||
}
|
||||
|
||||
// Adds to the path of the url in the order they appear in the structure
|
||||
func addToPath(request *http.Request, value reflect.Value, field reflect.StructField) (e error) {
|
||||
var additionalURLPathPart string
|
||||
if additionalURLPathPart, e = toStringValue(value, field); e != nil {
|
||||
return fmt.Errorf("can not marshal to path in request for field %s. Due to %s", field.Name, e.Error())
|
||||
}
|
||||
|
||||
// path should not be empty for any operations
|
||||
if len(additionalURLPathPart) == 0 {
|
||||
return fmt.Errorf("value cannot be empty for field %s in path", field.Name)
|
||||
}
|
||||
|
||||
if request.URL == nil {
|
||||
request.URL = &url.URL{}
|
||||
request.URL.Path = ""
|
||||
}
|
||||
var currentURLPath = request.URL.Path
|
||||
|
||||
var templatedPathRegex, _ = regexp.Compile(".*{.+}.*")
|
||||
if !templatedPathRegex.MatchString(currentURLPath) {
|
||||
Debugln("Marshaling request to path by appending field:", field.Name)
|
||||
allPath := []string{currentURLPath, additionalURLPathPart}
|
||||
request.URL.Path = strings.Join(allPath, "/")
|
||||
} else {
|
||||
var fieldName string
|
||||
if fieldName = field.Tag.Get("name"); fieldName == "" {
|
||||
e = fmt.Errorf("marshaling request to path name and template requires a 'name' tag for field: %s", field.Name)
|
||||
return
|
||||
}
|
||||
urlTemplate := currentURLPath
|
||||
Debugln("Marshaling to path from field: ", field.Name, " in template: ", urlTemplate)
|
||||
request.URL.Path = strings.Replace(urlTemplate, "{"+fieldName+"}", additionalURLPathPart, -1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setWellKnownHeaders(request *http.Request, headerName, headerValue string) (e error) {
|
||||
switch strings.ToLower(headerName) {
|
||||
case "content-length":
|
||||
var len int
|
||||
len, e = strconv.Atoi(headerValue)
|
||||
if e != nil {
|
||||
return
|
||||
}
|
||||
request.ContentLength = int64(len)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func addToHeader(request *http.Request, value reflect.Value, field reflect.StructField) (e error) {
|
||||
Debugln("Marshaling to header from field: ", field.Name)
|
||||
if request.Header == nil {
|
||||
request.Header = http.Header{}
|
||||
}
|
||||
|
||||
var headerName, headerValue string
|
||||
if headerName = field.Tag.Get("name"); headerName == "" {
|
||||
return fmt.Errorf("marshaling request to a header requires the 'name' tag for field: %s", field.Name)
|
||||
}
|
||||
|
||||
mandatory, _ := strconv.ParseBool(strings.ToLower(field.Tag.Get("mandatory")))
|
||||
//If mandatory and nil. Error out
|
||||
if mandatory && isNil(value) {
|
||||
return fmt.Errorf("marshaling request to a header requires not nil pointer for field: %s", field.Name)
|
||||
}
|
||||
|
||||
// generate opc-request-id if header value is nil and header name matches
|
||||
value = generateOpcRequestID(headerName, value)
|
||||
|
||||
//if not mandatory and nil. Omit
|
||||
if !mandatory && isNil(value) {
|
||||
Debugf("Header value is not mandatory and is nil pointer in field: %s. Skipping header", field.Name)
|
||||
return
|
||||
}
|
||||
|
||||
//Otherwise get value and set header
|
||||
if headerValue, e = toStringValue(value, field); e != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if e = setWellKnownHeaders(request, headerName, headerValue); e != nil {
|
||||
return
|
||||
}
|
||||
|
||||
request.Header.Add(headerName, headerValue)
|
||||
return
|
||||
}
|
||||
|
||||
// Header collection is a map of string to string that gets rendered as individual headers with a given prefix
|
||||
func addToHeaderCollection(request *http.Request, value reflect.Value, field reflect.StructField) (e error) {
|
||||
Debugln("Marshaling to header-collection from field:", field.Name)
|
||||
if request.Header == nil {
|
||||
request.Header = http.Header{}
|
||||
}
|
||||
|
||||
var headerPrefix string
|
||||
if headerPrefix = field.Tag.Get("prefix"); headerPrefix == "" {
|
||||
return fmt.Errorf("marshaling request to a header requires the 'prefix' tag for field: %s", field.Name)
|
||||
}
|
||||
|
||||
mandatory, _ := strconv.ParseBool(strings.ToLower(field.Tag.Get("mandatory")))
|
||||
//If mandatory and nil. Error out
|
||||
if mandatory && isNil(value) {
|
||||
return fmt.Errorf("marshaling request to a header requires not nil pointer for field: %s", field.Name)
|
||||
}
|
||||
|
||||
//if not mandatory and nil. Omit
|
||||
if !mandatory && isNil(value) {
|
||||
Debugf("Header value is not mandatory and is nil pointer in field: %s. Skipping header", field.Name)
|
||||
return
|
||||
}
|
||||
|
||||
//cast to map
|
||||
headerValues, ok := value.Interface().(map[string]string)
|
||||
if !ok {
|
||||
e = fmt.Errorf("header fields need to be of type map[string]string")
|
||||
return
|
||||
}
|
||||
|
||||
for k, v := range headerValues {
|
||||
headerName := fmt.Sprintf("%s%s", headerPrefix, k)
|
||||
request.Header.Set(headerName, v)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Makes sure the incoming structure is able to be marshalled
|
||||
// to a request
|
||||
func checkForValidRequestStruct(s interface{}) (*reflect.Value, error) {
|
||||
val := reflect.ValueOf(s)
|
||||
for val.Kind() == reflect.Ptr {
|
||||
if val.IsNil() {
|
||||
return nil, fmt.Errorf("can not marshal to request a pointer to structure")
|
||||
}
|
||||
val = val.Elem()
|
||||
}
|
||||
|
||||
if s == nil {
|
||||
return nil, fmt.Errorf("can not marshal to request a nil structure")
|
||||
}
|
||||
|
||||
if val.Kind() != reflect.Struct {
|
||||
return nil, fmt.Errorf("can not marshal to request, expects struct input. Got %v", val.Kind())
|
||||
}
|
||||
|
||||
return &val, nil
|
||||
}
|
||||
|
||||
// Populates the parts of a request by reading tags in the passed structure
|
||||
// nested structs are followed recursively depth-first.
|
||||
func structToRequestPart(request *http.Request, val reflect.Value) (err error) {
|
||||
typ := val.Type()
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
sf := typ.Field(i)
|
||||
//unexported
|
||||
if sf.PkgPath != "" && !sf.Anonymous {
|
||||
continue
|
||||
}
|
||||
|
||||
sv := val.Field(i)
|
||||
tag := sf.Tag.Get("contributesTo")
|
||||
switch tag {
|
||||
case "header":
|
||||
err = addToHeader(request, sv, sf)
|
||||
case "header-collection":
|
||||
err = addToHeaderCollection(request, sv, sf)
|
||||
case "path":
|
||||
err = addToPath(request, sv, sf)
|
||||
case "query":
|
||||
err = addToQuery(request, sv, sf)
|
||||
case "body":
|
||||
err = addToBody(request, sv, sf)
|
||||
case "":
|
||||
Debugln(sf.Name, " does not contain contributes tag. Skipping.")
|
||||
default:
|
||||
err = fmt.Errorf("can not marshal field: %s. It needs to contain valid contributesTo tag", sf.Name)
|
||||
}
|
||||
}
|
||||
|
||||
//If headers are and the content type was not set, we default to application/json
|
||||
if request.Header != nil && request.Header.Get(requestHeaderContentType) == "" {
|
||||
request.Header.Set(requestHeaderContentType, "application/json")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// HTTPRequestMarshaller marshals a structure to an http request using tag values in the struct
|
||||
// The marshaller tag should like the following
|
||||
// type A struct {
|
||||
// ANumber string `contributesTo="query" name="number"`
|
||||
// TheBody `contributesTo="body"`
|
||||
// }
|
||||
// where the contributesTo tag can be: header, path, query, body
|
||||
// and the 'name' tag is the name of the value used in the http request(not applicable for path)
|
||||
// If path is specified as part of the tag, the values are appened to the url path
|
||||
// in the order they appear in the structure
|
||||
// The current implementation only supports primitive types, except for the body tag, which needs a struct type.
|
||||
// The body of a request will be marshaled using the tags of the structure
|
||||
func HTTPRequestMarshaller(requestStruct interface{}, httpRequest *http.Request) (err error) {
|
||||
var val *reflect.Value
|
||||
if val, err = checkForValidRequestStruct(requestStruct); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
Debugln("Marshaling to Request: ", val.Type().Name())
|
||||
err = structToRequestPart(httpRequest, *val)
|
||||
return
|
||||
}
|
||||
|
||||
// MakeDefaultHTTPRequest creates the basic http request with the necessary headers set
|
||||
func MakeDefaultHTTPRequest(method, path string) (httpRequest http.Request) {
|
||||
httpRequest = http.Request{
|
||||
Proto: "HTTP/1.1",
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 1,
|
||||
Header: make(http.Header),
|
||||
URL: &url.URL{},
|
||||
}
|
||||
|
||||
httpRequest.Header.Set(requestHeaderContentLength, "0")
|
||||
httpRequest.Header.Set(requestHeaderDate, time.Now().UTC().Format(http.TimeFormat))
|
||||
httpRequest.Header.Set(requestHeaderOpcClientInfo, strings.Join([]string{defaultSDKMarker, Version()}, "/"))
|
||||
httpRequest.Header.Set(requestHeaderAccept, "*/*")
|
||||
httpRequest.Method = method
|
||||
httpRequest.URL.Path = path
|
||||
return
|
||||
}
|
||||
|
||||
// MakeDefaultHTTPRequestWithTaggedStruct creates an http request from an struct with tagged fields, see HTTPRequestMarshaller
|
||||
// for more information
|
||||
func MakeDefaultHTTPRequestWithTaggedStruct(method, path string, requestStruct interface{}) (httpRequest http.Request, err error) {
|
||||
httpRequest = MakeDefaultHTTPRequest(method, path)
|
||||
err = HTTPRequestMarshaller(requestStruct, &httpRequest)
|
||||
return
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//Request UnMarshaling
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Makes sure the incoming structure is able to be unmarshaled
|
||||
// to a request
|
||||
func checkForValidResponseStruct(s interface{}) (*reflect.Value, error) {
|
||||
val := reflect.ValueOf(s)
|
||||
for val.Kind() == reflect.Ptr {
|
||||
if val.IsNil() {
|
||||
return nil, fmt.Errorf("can not unmarshal to response a pointer to nil structure")
|
||||
}
|
||||
val = val.Elem()
|
||||
}
|
||||
|
||||
if s == nil {
|
||||
return nil, fmt.Errorf("can not unmarshal to response a nil structure")
|
||||
}
|
||||
|
||||
if val.Kind() != reflect.Struct {
|
||||
return nil, fmt.Errorf("can not unmarshal to response, expects struct input. Got %v", val.Kind())
|
||||
}
|
||||
|
||||
return &val, nil
|
||||
}
|
||||
|
||||
func intSizeFromKind(kind reflect.Kind) int {
|
||||
switch kind {
|
||||
case reflect.Int8, reflect.Uint8:
|
||||
return 8
|
||||
case reflect.Int16, reflect.Uint16:
|
||||
return 16
|
||||
case reflect.Int32, reflect.Uint32:
|
||||
return 32
|
||||
case reflect.Int64, reflect.Uint64:
|
||||
return 64
|
||||
case reflect.Int, reflect.Uint:
|
||||
return strconv.IntSize
|
||||
default:
|
||||
Debugf("The type is not valid: %v. Returing int size for arch\n", kind.String())
|
||||
return strconv.IntSize
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func analyzeValue(stringValue string, kind reflect.Kind, field reflect.StructField) (val reflect.Value, valPointer reflect.Value, err error) {
|
||||
switch kind {
|
||||
case timeType.Kind():
|
||||
var t time.Time
|
||||
t, err = tryParsingTimeWithValidFormatsForHeaders([]byte(stringValue), field.Name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
sdkTime := sdkTimeFromTime(t)
|
||||
val = reflect.ValueOf(sdkTime)
|
||||
valPointer = reflect.ValueOf(&sdkTime)
|
||||
return
|
||||
case sdkDateType.Kind():
|
||||
var t time.Time
|
||||
t, err = tryParsingTimeWithValidFormatsForHeaders([]byte(stringValue), field.Name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
sdkDate := sdkDateFromTime(t)
|
||||
val = reflect.ValueOf(sdkDate)
|
||||
valPointer = reflect.ValueOf(&sdkDate)
|
||||
return
|
||||
case reflect.Bool:
|
||||
var bVal bool
|
||||
if bVal, err = strconv.ParseBool(stringValue); err != nil {
|
||||
return
|
||||
}
|
||||
val = reflect.ValueOf(bVal)
|
||||
valPointer = reflect.ValueOf(&bVal)
|
||||
return
|
||||
case reflect.Int:
|
||||
size := intSizeFromKind(kind)
|
||||
var iVal int64
|
||||
if iVal, err = strconv.ParseInt(stringValue, 10, size); err != nil {
|
||||
return
|
||||
}
|
||||
var iiVal int
|
||||
iiVal = int(iVal)
|
||||
val = reflect.ValueOf(iiVal)
|
||||
valPointer = reflect.ValueOf(&iiVal)
|
||||
return
|
||||
case reflect.Int64:
|
||||
size := intSizeFromKind(kind)
|
||||
var iVal int64
|
||||
if iVal, err = strconv.ParseInt(stringValue, 10, size); err != nil {
|
||||
return
|
||||
}
|
||||
val = reflect.ValueOf(iVal)
|
||||
valPointer = reflect.ValueOf(&iVal)
|
||||
return
|
||||
case reflect.Uint:
|
||||
size := intSizeFromKind(kind)
|
||||
var iVal uint64
|
||||
if iVal, err = strconv.ParseUint(stringValue, 10, size); err != nil {
|
||||
return
|
||||
}
|
||||
var uiVal uint
|
||||
uiVal = uint(iVal)
|
||||
val = reflect.ValueOf(uiVal)
|
||||
valPointer = reflect.ValueOf(&uiVal)
|
||||
return
|
||||
case reflect.String:
|
||||
val = reflect.ValueOf(stringValue)
|
||||
valPointer = reflect.ValueOf(&stringValue)
|
||||
case reflect.Float32:
|
||||
var fVal float64
|
||||
if fVal, err = strconv.ParseFloat(stringValue, 32); err != nil {
|
||||
return
|
||||
}
|
||||
var ffVal float32
|
||||
ffVal = float32(fVal)
|
||||
val = reflect.ValueOf(ffVal)
|
||||
valPointer = reflect.ValueOf(&ffVal)
|
||||
return
|
||||
case reflect.Float64:
|
||||
var fVal float64
|
||||
if fVal, err = strconv.ParseFloat(stringValue, 64); err != nil {
|
||||
return
|
||||
}
|
||||
val = reflect.ValueOf(fVal)
|
||||
valPointer = reflect.ValueOf(&fVal)
|
||||
return
|
||||
default:
|
||||
err = fmt.Errorf("value for kind: %s not supported", kind)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Sets the field of a struct, with the appropiate value of the string
|
||||
// Only sets basic types
|
||||
func fromStringValue(newValue string, val *reflect.Value, field reflect.StructField) (err error) {
|
||||
|
||||
if !val.CanSet() {
|
||||
err = fmt.Errorf("can not set field name: %s of type: %v", field.Name, val.Type().String())
|
||||
return
|
||||
}
|
||||
|
||||
kind := val.Kind()
|
||||
isPointer := false
|
||||
if val.Kind() == reflect.Ptr {
|
||||
isPointer = true
|
||||
kind = field.Type.Elem().Kind()
|
||||
}
|
||||
|
||||
value, valPtr, err := analyzeValue(newValue, kind, field)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !isPointer {
|
||||
val.Set(value)
|
||||
} else {
|
||||
val.Set(valPtr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// PolymorphicJSONUnmarshaler is the interface to unmarshal polymorphic json payloads
|
||||
type PolymorphicJSONUnmarshaler interface {
|
||||
UnmarshalPolymorphicJSON(data []byte) (interface{}, error)
|
||||
}
|
||||
|
||||
func valueFromPolymorphicJSON(content []byte, unmarshaler PolymorphicJSONUnmarshaler) (val interface{}, err error) {
|
||||
err = json.Unmarshal(content, unmarshaler)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
val, err = unmarshaler.UnmarshalPolymorphicJSON(content)
|
||||
return
|
||||
}
|
||||
|
||||
func valueFromJSONBody(response *http.Response, value *reflect.Value, unmarshaler PolymorphicJSONUnmarshaler) (val interface{}, err error) {
|
||||
//Consumes the body, consider implementing it
|
||||
//without body consumption
|
||||
var content []byte
|
||||
content, err = ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if unmarshaler != nil {
|
||||
val, err = valueFromPolymorphicJSON(content, unmarshaler)
|
||||
return
|
||||
}
|
||||
|
||||
val = reflect.New(value.Type()).Interface()
|
||||
err = json.Unmarshal(content, &val)
|
||||
return
|
||||
}
|
||||
|
||||
func addFromBody(response *http.Response, value *reflect.Value, field reflect.StructField, unmarshaler PolymorphicJSONUnmarshaler) (err error) {
|
||||
Debugln("Unmarshaling from body to field: ", field.Name)
|
||||
if response.Body == nil {
|
||||
Debugln("Unmarshaling body skipped due to nil body content for field: ", field.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
tag := field.Tag
|
||||
encoding := tag.Get("encoding")
|
||||
var iVal interface{}
|
||||
switch encoding {
|
||||
case "binary":
|
||||
value.Set(reflect.ValueOf(response.Body))
|
||||
return
|
||||
case "plain-text":
|
||||
//Expects UTF-8
|
||||
byteArr, e := ioutil.ReadAll(response.Body)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
str := string(byteArr)
|
||||
value.Set(reflect.ValueOf(&str))
|
||||
return
|
||||
default: //If the encoding is not set. we'll decode with json
|
||||
iVal, err = valueFromJSONBody(response, value, unmarshaler)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
newVal := reflect.ValueOf(iVal)
|
||||
if newVal.Kind() == reflect.Ptr {
|
||||
newVal = newVal.Elem()
|
||||
}
|
||||
value.Set(newVal)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func addFromHeader(response *http.Response, value *reflect.Value, field reflect.StructField) (err error) {
|
||||
Debugln("Unmarshaling from header to field: ", field.Name)
|
||||
var headerName string
|
||||
if headerName = field.Tag.Get("name"); headerName == "" {
|
||||
return fmt.Errorf("unmarshaling response to a header requires the 'name' tag for field: %s", field.Name)
|
||||
}
|
||||
|
||||
headerValue := response.Header.Get(headerName)
|
||||
if headerValue == "" {
|
||||
Debugf("Unmarshalling did not find header with name:%s", headerName)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err = fromStringValue(headerValue, value, field); err != nil {
|
||||
return fmt.Errorf("unmarshaling response to a header failed for field %s, due to %s", field.Name,
|
||||
err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func addFromHeaderCollection(response *http.Response, value *reflect.Value, field reflect.StructField) error {
|
||||
Debugln("Unmarshaling from header-collection to field:", field.Name)
|
||||
var headerPrefix string
|
||||
if headerPrefix = field.Tag.Get("prefix"); headerPrefix == "" {
|
||||
return fmt.Errorf("Unmarshaling response to a header-collection requires the 'prefix' tag for field: %s", field.Name)
|
||||
}
|
||||
|
||||
mapCollection := make(map[string]string)
|
||||
for name, value := range response.Header {
|
||||
nameLowerCase := strings.ToLower(name)
|
||||
if strings.HasPrefix(nameLowerCase, headerPrefix) {
|
||||
headerNoPrefix := strings.TrimPrefix(nameLowerCase, headerPrefix)
|
||||
mapCollection[headerNoPrefix] = value[0]
|
||||
}
|
||||
}
|
||||
|
||||
Debugln("Marshalled header collection is:", mapCollection)
|
||||
value.Set(reflect.ValueOf(mapCollection))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Populates a struct from parts of a request by reading tags of the struct
|
||||
func responseToStruct(response *http.Response, val *reflect.Value, unmarshaler PolymorphicJSONUnmarshaler) (err error) {
|
||||
typ := val.Type()
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
sf := typ.Field(i)
|
||||
|
||||
//unexported
|
||||
if sf.PkgPath != "" {
|
||||
continue
|
||||
}
|
||||
|
||||
sv := val.Field(i)
|
||||
tag := sf.Tag.Get("presentIn")
|
||||
switch tag {
|
||||
case "header":
|
||||
err = addFromHeader(response, &sv, sf)
|
||||
case "header-collection":
|
||||
err = addFromHeaderCollection(response, &sv, sf)
|
||||
case "body":
|
||||
err = addFromBody(response, &sv, sf, unmarshaler)
|
||||
case "":
|
||||
Debugln(sf.Name, " does not contain presentIn tag. Skipping")
|
||||
default:
|
||||
err = fmt.Errorf("can not unmarshal field: %s. It needs to contain valid presentIn tag", sf.Name)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalResponse hydrates the fields of a struct with the values of a http response, guided
|
||||
// by the field tags. The directive tag is "presentIn" and it can be either
|
||||
// - "header": Will look for the header tagged as "name" in the headers of the struct and set it value to that
|
||||
// - "body": It will try to marshal the body from a json string to a struct tagged with 'presentIn: "body"'.
|
||||
// Further this method will consume the body it should be safe to close it after this function
|
||||
// Notice the current implementation only supports native types:int, strings, floats, bool as the field types
|
||||
func UnmarshalResponse(httpResponse *http.Response, responseStruct interface{}) (err error) {
|
||||
|
||||
var val *reflect.Value
|
||||
if val, err = checkForValidResponseStruct(responseStruct); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = responseToStruct(httpResponse, val, nil); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalResponseWithPolymorphicBody similar to UnmarshalResponse but assumes the body of the response
|
||||
// contains polymorphic json. This function will use the unmarshaler argument to unmarshal json content
|
||||
func UnmarshalResponseWithPolymorphicBody(httpResponse *http.Response, responseStruct interface{}, unmarshaler PolymorphicJSONUnmarshaler) (err error) {
|
||||
|
||||
var val *reflect.Value
|
||||
if val, err = checkForValidResponseStruct(responseStruct); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = responseToStruct(httpResponse, val, unmarshaler); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// generate request id if user not provided and for each retry operation re-gen a new request id
|
||||
func generateOpcRequestID(headerName string, value reflect.Value) (newValue reflect.Value) {
|
||||
newValue = value
|
||||
isNilValue := isNil(newValue)
|
||||
isOpcRequestIDHeader := headerName == requestHeaderOpcRequestID || headerName == requestHeaderOpcClientRequestID
|
||||
|
||||
if isNilValue && isOpcRequestIDHeader {
|
||||
requestID, err := generateRandUUID()
|
||||
|
||||
if err != nil {
|
||||
// this will not fail the request, just skip add opc-request-id
|
||||
Debugf("unable to generate opc-request-id. %s", err.Error())
|
||||
} else {
|
||||
newValue = reflect.ValueOf(String(requestID))
|
||||
Debugf("add request id for header: %s, with value: %s", headerName, requestID)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
269
vendor/github.com/oracle/oci-go-sdk/common/http_signer.go
generated
vendored
Normal file
269
vendor/github.com/oracle/oci-go-sdk/common/http_signer.go
generated
vendored
Normal file
|
@ -0,0 +1,269 @@
|
|||
// Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// HTTPRequestSigner the interface to sign a request
|
||||
type HTTPRequestSigner interface {
|
||||
Sign(r *http.Request) error
|
||||
}
|
||||
|
||||
// KeyProvider interface that wraps information about the key's account owner
|
||||
type KeyProvider interface {
|
||||
PrivateRSAKey() (*rsa.PrivateKey, error)
|
||||
KeyID() (string, error)
|
||||
}
|
||||
|
||||
const signerVersion = "1"
|
||||
|
||||
// SignerBodyHashPredicate a function that allows to disable/enable body hashing
|
||||
// of requests and headers associated with body content
|
||||
type SignerBodyHashPredicate func(r *http.Request) bool
|
||||
|
||||
// ociRequestSigner implements the http-signatures-draft spec
|
||||
// as described in https://tools.ietf.org/html/draft-cavage-http-signatures-08
|
||||
type ociRequestSigner struct {
|
||||
KeyProvider KeyProvider
|
||||
GenericHeaders []string
|
||||
BodyHeaders []string
|
||||
ShouldHashBody SignerBodyHashPredicate
|
||||
}
|
||||
|
||||
var (
|
||||
defaultGenericHeaders = []string{"date", "(request-target)", "host"}
|
||||
defaultBodyHeaders = []string{"content-length", "content-type", "x-content-sha256"}
|
||||
defaultBodyHashPredicate = func(r *http.Request) bool {
|
||||
return r.Method == http.MethodPost || r.Method == http.MethodPut || r.Method == http.MethodPatch
|
||||
}
|
||||
)
|
||||
|
||||
// DefaultGenericHeaders list of default generic headers that is used in signing
|
||||
func DefaultGenericHeaders() []string {
|
||||
return makeACopy(defaultGenericHeaders)
|
||||
}
|
||||
|
||||
// DefaultBodyHeaders list of default body headers that is used in signing
|
||||
func DefaultBodyHeaders() []string {
|
||||
return makeACopy(defaultBodyHeaders)
|
||||
}
|
||||
|
||||
// DefaultRequestSigner creates a signer with default parameters.
|
||||
func DefaultRequestSigner(provider KeyProvider) HTTPRequestSigner {
|
||||
return RequestSigner(provider, defaultGenericHeaders, defaultBodyHeaders)
|
||||
}
|
||||
|
||||
// RequestSignerExcludeBody creates a signer without hash the body.
|
||||
func RequestSignerExcludeBody(provider KeyProvider) HTTPRequestSigner {
|
||||
bodyHashPredicate := func(r *http.Request) bool {
|
||||
// week request signer will not hash the body
|
||||
return false
|
||||
}
|
||||
return RequestSignerWithBodyHashingPredicate(provider, defaultGenericHeaders, defaultBodyHeaders, bodyHashPredicate)
|
||||
}
|
||||
|
||||
// NewSignerFromOCIRequestSigner creates a copy of the request signer and attaches the new SignerBodyHashPredicate
|
||||
// returns an error if the passed signer is not of type ociRequestSigner
|
||||
func NewSignerFromOCIRequestSigner(oldSigner HTTPRequestSigner, predicate SignerBodyHashPredicate) (HTTPRequestSigner, error) {
|
||||
if oldS, ok := oldSigner.(ociRequestSigner); ok {
|
||||
s := ociRequestSigner{
|
||||
KeyProvider: oldS.KeyProvider,
|
||||
GenericHeaders: oldS.GenericHeaders,
|
||||
BodyHeaders: oldS.BodyHeaders,
|
||||
ShouldHashBody: predicate,
|
||||
}
|
||||
return s, nil
|
||||
|
||||
}
|
||||
return nil, fmt.Errorf("can not create a signer, input signer needs to be of type ociRequestSigner")
|
||||
}
|
||||
|
||||
// RequestSigner creates a signer that utilizes the specified headers for signing
|
||||
// and the default predicate for using the body of the request as part of the signature
|
||||
func RequestSigner(provider KeyProvider, genericHeaders, bodyHeaders []string) HTTPRequestSigner {
|
||||
return ociRequestSigner{
|
||||
KeyProvider: provider,
|
||||
GenericHeaders: genericHeaders,
|
||||
BodyHeaders: bodyHeaders,
|
||||
ShouldHashBody: defaultBodyHashPredicate}
|
||||
}
|
||||
|
||||
// RequestSignerWithBodyHashingPredicate creates a signer that utilizes the specified headers for signing, as well as a predicate for using
|
||||
// the body of the request and bodyHeaders parameter as part of the signature
|
||||
func RequestSignerWithBodyHashingPredicate(provider KeyProvider, genericHeaders, bodyHeaders []string, shouldHashBody SignerBodyHashPredicate) HTTPRequestSigner {
|
||||
return ociRequestSigner{
|
||||
KeyProvider: provider,
|
||||
GenericHeaders: genericHeaders,
|
||||
BodyHeaders: bodyHeaders,
|
||||
ShouldHashBody: shouldHashBody}
|
||||
}
|
||||
|
||||
func (signer ociRequestSigner) getSigningHeaders(r *http.Request) []string {
|
||||
var result []string
|
||||
result = append(result, signer.GenericHeaders...)
|
||||
|
||||
if signer.ShouldHashBody(r) {
|
||||
result = append(result, signer.BodyHeaders...)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (signer ociRequestSigner) getSigningString(request *http.Request) string {
|
||||
signingHeaders := signer.getSigningHeaders(request)
|
||||
signingParts := make([]string, len(signingHeaders))
|
||||
for i, part := range signingHeaders {
|
||||
var value string
|
||||
part = strings.ToLower(part)
|
||||
switch part {
|
||||
case "(request-target)":
|
||||
value = getRequestTarget(request)
|
||||
case "host":
|
||||
value = request.URL.Host
|
||||
if len(value) == 0 {
|
||||
value = request.Host
|
||||
}
|
||||
default:
|
||||
value = request.Header.Get(part)
|
||||
}
|
||||
signingParts[i] = fmt.Sprintf("%s: %s", part, value)
|
||||
}
|
||||
|
||||
signingString := strings.Join(signingParts, "\n")
|
||||
return signingString
|
||||
|
||||
}
|
||||
|
||||
func getRequestTarget(request *http.Request) string {
|
||||
lowercaseMethod := strings.ToLower(request.Method)
|
||||
return fmt.Sprintf("%s %s", lowercaseMethod, request.URL.RequestURI())
|
||||
}
|
||||
|
||||
func calculateHashOfBody(request *http.Request) (err error) {
|
||||
var hash string
|
||||
hash, err = GetBodyHash(request)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
request.Header.Set(requestHeaderXContentSHA256, hash)
|
||||
return
|
||||
}
|
||||
|
||||
// drainBody reads all of b to memory and then returns two equivalent
|
||||
// ReadClosers yielding the same bytes.
|
||||
//
|
||||
// It returns an error if the initial slurp of all bytes fails. It does not attempt
|
||||
// to make the returned ReadClosers have identical error-matching behavior.
|
||||
func drainBody(b io.ReadCloser) (r1, r2 io.ReadCloser, err error) {
|
||||
if b == http.NoBody {
|
||||
// No copying needed. Preserve the magic sentinel meaning of NoBody.
|
||||
return http.NoBody, http.NoBody, nil
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
if _, err = buf.ReadFrom(b); err != nil {
|
||||
return nil, b, err
|
||||
}
|
||||
if err = b.Close(); err != nil {
|
||||
return nil, b, err
|
||||
}
|
||||
return ioutil.NopCloser(&buf), ioutil.NopCloser(bytes.NewReader(buf.Bytes())), nil
|
||||
}
|
||||
|
||||
func hashAndEncode(data []byte) string {
|
||||
hashedContent := sha256.Sum256(data)
|
||||
hash := base64.StdEncoding.EncodeToString(hashedContent[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// GetBodyHash creates a base64 string from the hash of body the request
|
||||
func GetBodyHash(request *http.Request) (hashString string, err error) {
|
||||
if request.Body == nil {
|
||||
request.ContentLength = 0
|
||||
request.Header.Set(requestHeaderContentLength, fmt.Sprintf("%v", request.ContentLength))
|
||||
return hashAndEncode([]byte("")), nil
|
||||
}
|
||||
|
||||
var data []byte
|
||||
bReader := request.Body
|
||||
bReader, request.Body, err = drainBody(request.Body)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("can not read body of request while calculating body hash: %s", err.Error())
|
||||
}
|
||||
|
||||
data, err = ioutil.ReadAll(bReader)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("can not read body of request while calculating body hash: %s", err.Error())
|
||||
}
|
||||
|
||||
// Since the request can be coming from a binary body. Make an attempt to set the body length
|
||||
request.ContentLength = int64(len(data))
|
||||
request.Header.Set(requestHeaderContentLength, fmt.Sprintf("%v", request.ContentLength))
|
||||
|
||||
hashString = hashAndEncode(data)
|
||||
return
|
||||
}
|
||||
|
||||
func (signer ociRequestSigner) computeSignature(request *http.Request) (signature string, err error) {
|
||||
signingString := signer.getSigningString(request)
|
||||
hasher := sha256.New()
|
||||
hasher.Write([]byte(signingString))
|
||||
hashed := hasher.Sum(nil)
|
||||
|
||||
privateKey, err := signer.KeyProvider.PrivateRSAKey()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var unencodedSig []byte
|
||||
unencodedSig, e := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hashed)
|
||||
if e != nil {
|
||||
err = fmt.Errorf("can not compute signature while signing the request %s: ", e.Error())
|
||||
return
|
||||
}
|
||||
|
||||
signature = base64.StdEncoding.EncodeToString(unencodedSig)
|
||||
return
|
||||
}
|
||||
|
||||
// Sign signs the http request, by inspecting the necessary headers. Once signed
|
||||
// the request will have the proper 'Authorization' header set, otherwise
|
||||
// and error is returned
|
||||
func (signer ociRequestSigner) Sign(request *http.Request) (err error) {
|
||||
if signer.ShouldHashBody(request) {
|
||||
err = calculateHashOfBody(request)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var signature string
|
||||
if signature, err = signer.computeSignature(request); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
signingHeaders := strings.Join(signer.getSigningHeaders(request), " ")
|
||||
|
||||
var keyID string
|
||||
if keyID, err = signer.KeyProvider.KeyID(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
authValue := fmt.Sprintf("Signature version=\"%s\",headers=\"%s\",keyId=\"%s\",algorithm=\"rsa-sha256\",signature=\"%s\"",
|
||||
signerVersion, signingHeaders, keyID, signature)
|
||||
|
||||
request.Header.Set(requestHeaderAuthorization, authValue)
|
||||
|
||||
return
|
||||
}
|
170
vendor/github.com/oracle/oci-go-sdk/common/log.go
generated
vendored
Normal file
170
vendor/github.com/oracle/oci-go-sdk/common/log.go
generated
vendored
Normal file
|
@ -0,0 +1,170 @@
|
|||
// Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
//sdkLogger an interface for logging in the SDK
|
||||
type sdkLogger interface {
|
||||
//LogLevel returns the log level of sdkLogger
|
||||
LogLevel() int
|
||||
|
||||
//Log logs v with the provided format if the current log level is loglevel
|
||||
Log(logLevel int, format string, v ...interface{}) error
|
||||
}
|
||||
|
||||
//noLogging no logging messages
|
||||
const noLogging = 0
|
||||
|
||||
//infoLogging minimal logging messages
|
||||
const infoLogging = 1
|
||||
|
||||
//debugLogging some logging messages
|
||||
const debugLogging = 2
|
||||
|
||||
//verboseLogging all logging messages
|
||||
const verboseLogging = 3
|
||||
|
||||
//defaultSDKLogger the default implementation of the sdkLogger
|
||||
type defaultSDKLogger struct {
|
||||
currentLoggingLevel int
|
||||
verboseLogger *log.Logger
|
||||
debugLogger *log.Logger
|
||||
infoLogger *log.Logger
|
||||
nullLogger *log.Logger
|
||||
}
|
||||
|
||||
//defaultLogger is the defaultLogger in the SDK
|
||||
var defaultLogger sdkLogger
|
||||
var loggerLock sync.Mutex
|
||||
|
||||
//initializes the SDK defaultLogger as a defaultLogger
|
||||
func init() {
|
||||
l, _ := newSDKLogger()
|
||||
setSDKLogger(l)
|
||||
}
|
||||
|
||||
//setSDKLogger sets the logger used by the sdk
|
||||
func setSDKLogger(logger sdkLogger) {
|
||||
loggerLock.Lock()
|
||||
defaultLogger = logger
|
||||
loggerLock.Unlock()
|
||||
}
|
||||
|
||||
// newSDKLogger creates a defaultSDKLogger
|
||||
// Debug logging is turned on/off by the presence of the environment variable "OCI_GO_SDK_DEBUG"
|
||||
// The value of the "OCI_GO_SDK_DEBUG" environment variable controls the logging level.
|
||||
// "null" outputs no log messages
|
||||
// "i" or "info" outputs minimal log messages
|
||||
// "d" or "debug" outputs some logs messages
|
||||
// "v" or "verbose" outputs all logs messages, including body of requests
|
||||
func newSDKLogger() (defaultSDKLogger, error) {
|
||||
logger := defaultSDKLogger{}
|
||||
|
||||
logger.currentLoggingLevel = noLogging
|
||||
logger.verboseLogger = log.New(os.Stderr, "VERBOSE ", log.Ldate|log.Lmicroseconds|log.Lshortfile)
|
||||
logger.debugLogger = log.New(os.Stderr, "DEBUG ", log.Ldate|log.Lmicroseconds|log.Lshortfile)
|
||||
logger.infoLogger = log.New(os.Stderr, "INFO ", log.Ldate|log.Lmicroseconds|log.Lshortfile)
|
||||
logger.nullLogger = log.New(ioutil.Discard, "", log.Ldate|log.Lmicroseconds|log.Lshortfile)
|
||||
|
||||
configured, isLogEnabled := os.LookupEnv("OCI_GO_SDK_DEBUG")
|
||||
|
||||
// If env variable not present turn logging of
|
||||
if !isLogEnabled {
|
||||
logger.currentLoggingLevel = noLogging
|
||||
} else {
|
||||
|
||||
switch strings.ToLower(configured) {
|
||||
case "null":
|
||||
logger.currentLoggingLevel = noLogging
|
||||
break
|
||||
case "i", "info":
|
||||
logger.currentLoggingLevel = infoLogging
|
||||
break
|
||||
case "d", "debug":
|
||||
logger.currentLoggingLevel = debugLogging
|
||||
break
|
||||
//1 here for backwards compatibility
|
||||
case "v", "verbose", "1":
|
||||
logger.currentLoggingLevel = verboseLogging
|
||||
break
|
||||
default:
|
||||
logger.currentLoggingLevel = infoLogging
|
||||
}
|
||||
logger.infoLogger.Println("logger level set to: ", logger.currentLoggingLevel)
|
||||
}
|
||||
|
||||
return logger, nil
|
||||
}
|
||||
|
||||
func (l defaultSDKLogger) getLoggerForLevel(logLevel int) *log.Logger {
|
||||
if logLevel > l.currentLoggingLevel {
|
||||
return l.nullLogger
|
||||
}
|
||||
|
||||
switch logLevel {
|
||||
case noLogging:
|
||||
return l.nullLogger
|
||||
case infoLogging:
|
||||
return l.infoLogger
|
||||
case debugLogging:
|
||||
return l.debugLogger
|
||||
case verboseLogging:
|
||||
return l.verboseLogger
|
||||
default:
|
||||
return l.nullLogger
|
||||
}
|
||||
}
|
||||
|
||||
//LogLevel returns the current debug level
|
||||
func (l defaultSDKLogger) LogLevel() int {
|
||||
return l.currentLoggingLevel
|
||||
}
|
||||
|
||||
func (l defaultSDKLogger) Log(logLevel int, format string, v ...interface{}) error {
|
||||
logger := l.getLoggerForLevel(logLevel)
|
||||
logger.Output(4, fmt.Sprintf(format, v...))
|
||||
return nil
|
||||
}
|
||||
|
||||
//Logln logs v appending a new line at the end
|
||||
//Deprecated
|
||||
func Logln(v ...interface{}) {
|
||||
defaultLogger.Log(infoLogging, "%v\n", v...)
|
||||
}
|
||||
|
||||
// Logf logs v with the provided format
|
||||
func Logf(format string, v ...interface{}) {
|
||||
defaultLogger.Log(infoLogging, format, v...)
|
||||
}
|
||||
|
||||
// Debugf logs v with the provided format if debug mode is set
|
||||
func Debugf(format string, v ...interface{}) {
|
||||
defaultLogger.Log(debugLogging, format, v...)
|
||||
}
|
||||
|
||||
// Debug logs v if debug mode is set
|
||||
func Debug(v ...interface{}) {
|
||||
m := fmt.Sprint(v...)
|
||||
defaultLogger.Log(debugLogging, "%s", m)
|
||||
}
|
||||
|
||||
// Debugln logs v appending a new line if debug mode is set
|
||||
func Debugln(v ...interface{}) {
|
||||
m := fmt.Sprint(v...)
|
||||
defaultLogger.Log(debugLogging, "%s\n", m)
|
||||
}
|
||||
|
||||
// IfDebug executes closure if debug is enabled
|
||||
func IfDebug(fn func()) {
|
||||
if defaultLogger.LogLevel() >= debugLogging {
|
||||
fn()
|
||||
}
|
||||
}
|
159
vendor/github.com/oracle/oci-go-sdk/common/retry.go
generated
vendored
Normal file
159
vendor/github.com/oracle/oci-go-sdk/common/retry.go
generated
vendored
Normal file
|
@ -0,0 +1,159 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// UnlimitedNumAttemptsValue is the value for indicating unlimited attempts for reaching success
|
||||
UnlimitedNumAttemptsValue = uint(0)
|
||||
|
||||
// number of characters contained in the generated retry token
|
||||
generatedRetryTokenLength = 32
|
||||
)
|
||||
|
||||
// OCIRetryableRequest represents a request that can be reissued according to the specified policy.
|
||||
type OCIRetryableRequest interface {
|
||||
// Any retryable request must implement the OCIRequest interface
|
||||
OCIRequest
|
||||
|
||||
// Each operation specifies default retry behavior. By passing no arguments to this method, the default retry
|
||||
// behavior, as determined on a per-operation-basis, will be honored. Variadic retry policy option arguments
|
||||
// passed to this method will override the default behavior.
|
||||
RetryPolicy() *RetryPolicy
|
||||
}
|
||||
|
||||
// OCIOperationResponse represents the output of an OCIOperation, with additional context of error message
|
||||
// and operation attempt number.
|
||||
type OCIOperationResponse struct {
|
||||
// Response from OCI Operation
|
||||
Response OCIResponse
|
||||
|
||||
// Error from OCI Operation
|
||||
Error error
|
||||
|
||||
// Operation Attempt Number (one-based)
|
||||
AttemptNumber uint
|
||||
}
|
||||
|
||||
// NewOCIOperationResponse assembles an OCI Operation Response object.
|
||||
func NewOCIOperationResponse(response OCIResponse, err error, attempt uint) OCIOperationResponse {
|
||||
return OCIOperationResponse{
|
||||
Response: response,
|
||||
Error: err,
|
||||
AttemptNumber: attempt,
|
||||
}
|
||||
}
|
||||
|
||||
// RetryPolicy is the class that holds all relevant information for retrying operations.
|
||||
type RetryPolicy struct {
|
||||
// MaximumNumberAttempts is the maximum number of times to retry a request. Zero indicates an unlimited
|
||||
// number of attempts.
|
||||
MaximumNumberAttempts uint
|
||||
|
||||
// ShouldRetryOperation inspects the http response, error, and operation attempt number, and
|
||||
// - returns true if we should retry the operation
|
||||
// - returns false otherwise
|
||||
ShouldRetryOperation func(OCIOperationResponse) bool
|
||||
|
||||
// GetNextDuration computes the duration to pause between operation retries.
|
||||
NextDuration func(OCIOperationResponse) time.Duration
|
||||
}
|
||||
|
||||
// NoRetryPolicy is a helper method that assembles and returns a return policy that indicates an operation should
|
||||
// never be retried (the operation is performed exactly once).
|
||||
func NoRetryPolicy() RetryPolicy {
|
||||
dontRetryOperation := func(OCIOperationResponse) bool { return false }
|
||||
zeroNextDuration := func(OCIOperationResponse) time.Duration { return 0 * time.Second }
|
||||
return NewRetryPolicy(uint(1), dontRetryOperation, zeroNextDuration)
|
||||
}
|
||||
|
||||
// NewRetryPolicy is a helper method for assembling a Retry Policy object.
|
||||
func NewRetryPolicy(attempts uint, retryOperation func(OCIOperationResponse) bool, nextDuration func(OCIOperationResponse) time.Duration) RetryPolicy {
|
||||
return RetryPolicy{
|
||||
MaximumNumberAttempts: attempts,
|
||||
ShouldRetryOperation: retryOperation,
|
||||
NextDuration: nextDuration,
|
||||
}
|
||||
}
|
||||
|
||||
// shouldContinueIssuingRequests returns true if we should continue retrying a request, based on the current attempt
|
||||
// number and the maximum number of attempts specified, or false otherwise.
|
||||
func shouldContinueIssuingRequests(current, maximum uint) bool {
|
||||
return maximum == UnlimitedNumAttemptsValue || current <= maximum
|
||||
}
|
||||
|
||||
// RetryToken generates a retry token that must be included on any request passed to the Retry method.
|
||||
func RetryToken() string {
|
||||
alphanumericChars := []rune("abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
retryToken := make([]rune, generatedRetryTokenLength)
|
||||
for i := range retryToken {
|
||||
retryToken[i] = alphanumericChars[rand.Intn(len(alphanumericChars))]
|
||||
}
|
||||
return string(retryToken)
|
||||
}
|
||||
|
||||
// Retry is a package-level operation that executes the retryable request using the specified operation and retry policy.
|
||||
func Retry(ctx context.Context, request OCIRetryableRequest, operation OCIOperation, policy RetryPolicy) (OCIResponse, error) {
|
||||
|
||||
type retrierResult struct {
|
||||
response OCIResponse
|
||||
err error
|
||||
}
|
||||
|
||||
var response OCIResponse
|
||||
var err error
|
||||
retrierChannel := make(chan retrierResult)
|
||||
|
||||
go func() {
|
||||
|
||||
// Deal with panics more graciously
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
stackBuffer := make([]byte, 1024)
|
||||
bytesWritten := runtime.Stack(stackBuffer, false)
|
||||
stack := string(stackBuffer[:bytesWritten])
|
||||
retrierChannel <- retrierResult{nil, fmt.Errorf("panicked while retrying operation. Panic was: %s\nStack: %s", r, stack)}
|
||||
}
|
||||
}()
|
||||
|
||||
// use a one-based counter because it's easier to think about operation retry in terms of attempt numbering
|
||||
for currentOperationAttempt := uint(1); shouldContinueIssuingRequests(currentOperationAttempt, policy.MaximumNumberAttempts); currentOperationAttempt++ {
|
||||
Debugln(fmt.Sprintf("operation attempt #%v", currentOperationAttempt))
|
||||
response, err = operation(ctx, request)
|
||||
operationResponse := NewOCIOperationResponse(response, err, currentOperationAttempt)
|
||||
|
||||
if !policy.ShouldRetryOperation(operationResponse) {
|
||||
// we should NOT retry operation based on response and/or error => return
|
||||
retrierChannel <- retrierResult{response, err}
|
||||
return
|
||||
}
|
||||
|
||||
duration := policy.NextDuration(operationResponse)
|
||||
//The following condition is kept for backwards compatibility reasons
|
||||
if deadline, ok := ctx.Deadline(); ok && time.Now().Add(duration).After(deadline) {
|
||||
// we want to retry the operation, but the policy is telling us to wait for a duration that exceeds
|
||||
// the specified overall deadline for the operation => instead of waiting for however long that
|
||||
// time period is and then aborting, abort now and save the cycles
|
||||
retrierChannel <- retrierResult{response, DeadlineExceededByBackoff}
|
||||
return
|
||||
}
|
||||
Debugln(fmt.Sprintf("waiting %v before retrying operation", duration))
|
||||
// sleep before retrying the operation
|
||||
<-time.After(duration)
|
||||
}
|
||||
|
||||
retrierChannel <- retrierResult{nil, fmt.Errorf("maximum number of attempts exceeded (%v)", policy.MaximumNumberAttempts)}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return response, ctx.Err()
|
||||
case result := <-retrierChannel:
|
||||
return result.response, result.err
|
||||
}
|
||||
}
|
36
vendor/github.com/oracle/oci-go-sdk/common/version.go
generated
vendored
Normal file
36
vendor/github.com/oracle/oci-go-sdk/common/version.go
generated
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
// Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
// Code generated by go generate; DO NOT EDIT
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
major = "4"
|
||||
minor = "2"
|
||||
patch = "0"
|
||||
tag = ""
|
||||
)
|
||||
|
||||
var once sync.Once
|
||||
var version string
|
||||
|
||||
// Version returns semantic version of the sdk
|
||||
func Version() string {
|
||||
once.Do(func() {
|
||||
ver := fmt.Sprintf("%s.%s.%s", major, minor, patch)
|
||||
verBuilder := bytes.NewBufferString(ver)
|
||||
if tag != "" && tag != "-" {
|
||||
_, err := verBuilder.WriteString(tag)
|
||||
if err == nil {
|
||||
verBuilder = bytes.NewBufferString(ver)
|
||||
}
|
||||
}
|
||||
version = verBuilder.String()
|
||||
})
|
||||
return version
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue