packer-cn/vendor/github.com/oracle/oci-go-sdk/example/helpers/helper.go

154 lines
4.4 KiB
Go

// Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
//
// Helper methods for OCI GOSDK Samples
//
package helpers
import (
"fmt"
"log"
"math"
"math/rand"
"reflect"
"strings"
"time"
"github.com/oracle/oci-go-sdk/common"
)
// FatalIfError is equivalent to Println() followed by a call to os.Exit(1) if error is not nil
func FatalIfError(err error) {
if err != nil {
log.Fatalln(err.Error())
}
}
// RetryUntilTrueOrError retries a function until the predicate is true or it reaches a timeout.
// The operation is retried at the give frequency
func RetryUntilTrueOrError(operation func() (interface{}, error), predicate func(interface{}) (bool, error), frequency, timeout <-chan time.Time) error {
for {
select {
case <-timeout:
return fmt.Errorf("timeout reached")
case <-frequency:
result, err := operation()
if err != nil {
return err
}
isTrue, err := predicate(result)
if err != nil {
return err
}
if isTrue {
return nil
}
}
}
}
// FindLifecycleFieldValue finds lifecycle value inside the struct based on reflection
func FindLifecycleFieldValue(request interface{}) (string, error) {
val := reflect.ValueOf(request)
if val.Kind() == reflect.Ptr {
if val.IsNil() {
return "", fmt.Errorf("can not unmarshal to response a pointer to nil structure")
}
val = val.Elem()
}
var err error
typ := val.Type()
for i := 0; i < typ.NumField(); i++ {
if err != nil {
return "", err
}
sf := typ.Field(i)
//unexported
if sf.PkgPath != "" {
continue
}
sv := val.Field(i)
if sv.Kind() == reflect.Struct {
lif, err := FindLifecycleFieldValue(sv.Interface())
if err == nil {
return lif, nil
}
}
if !strings.Contains(strings.ToLower(sf.Name), "lifecyclestate") {
continue
}
return sv.String(), nil
}
return "", fmt.Errorf("request does not have a lifecycle field")
}
// CheckLifecycleState returns a function that checks for that a struct has the given lifecycle
func CheckLifecycleState(lifecycleState string) func(interface{}) (bool, error) {
return func(request interface{}) (bool, error) {
fieldLifecycle, err := FindLifecycleFieldValue(request)
if err != nil {
return false, err
}
isEqual := fieldLifecycle == lifecycleState
log.Printf("Current lifecycle state is: %s, waiting for it becomes to: %s", fieldLifecycle, lifecycleState)
return isEqual, nil
}
}
// GetRequestMetadataWithDefaultRetryPolicy returns a requestMetadata with default retry policy
// which will do retry for non-200 status code return back from service
// Notes: not all non-200 status code should do retry, this should be based on specific operation
// such as delete operation followed with get operation will retrun 404 if resource already been
// deleted
func GetRequestMetadataWithDefaultRetryPolicy() common.RequestMetadata {
return common.RequestMetadata{
RetryPolicy: getDefaultRetryPolicy(),
}
}
// GetRequestMetadataWithCustomizedRetryPolicy returns a requestMetadata which will do the retry based on
// input function (retry until the function return false)
func GetRequestMetadataWithCustomizedRetryPolicy(fn func(r common.OCIOperationResponse) bool) common.RequestMetadata {
return common.RequestMetadata{
RetryPolicy: getExponentialBackoffRetryPolicy(uint(20), fn),
}
}
func getDefaultRetryPolicy() *common.RetryPolicy {
// how many times to do the retry
attempts := uint(10)
// retry for all non-200 status code
retryOnAllNon200ResponseCodes := func(r common.OCIOperationResponse) bool {
return !(r.Error == nil && 199 < r.Response.HTTPResponse().StatusCode && r.Response.HTTPResponse().StatusCode < 300)
}
return getExponentialBackoffRetryPolicy(attempts, retryOnAllNon200ResponseCodes)
}
func getExponentialBackoffRetryPolicy(n uint, fn func(r common.OCIOperationResponse) bool) *common.RetryPolicy {
// the duration between each retry operation, you might want to waite longer each time the retry fails
exponentialBackoff := func(r common.OCIOperationResponse) time.Duration {
return time.Duration(math.Pow(float64(2), float64(r.AttemptNumber-1))) * time.Second
}
policy := common.NewRetryPolicy(n, fn, exponentialBackoff)
return &policy
}
// GetRandomString returns a random string with length equals to n
func GetRandomString(n int) string {
letters := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
b := make([]rune, n)
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
return string(b)
}