/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package config
import (
"github.com/BurntSushi/toml"
"github.com/kelseyhightower/envconfig"
"go.uber.org/zap"
"os"
"path/filepath"
"strings"
)
func getEnv(key string) string {
return os.Getenv(strings.ToUpper(key))
}
// GetGoPath inspects the environment for the GOPATH variable
func GetGoPath() string {
if goPath := getEnv("GOPATH"); goPath != "" {
return goPath
}
return ""
}
// Config is the parent struct for all the configuration information for -cluster
type Config struct {
Server *ServerConfig `required:"true"`
Bluemix *BluemixConfig //`required:"true"`
Softlayer *SoftlayerConfig
Gen2 *Gen2Config
VPC *VPCProviderConfig
IKS *IKSConfig
}
//ReadConfig loads the config from file
func ReadConfig(confPath string, logger *zap.Logger) (*Config, error) {
// load the default config, if confPath not provided
if confPath == "" {
confPath = GetDefaultConfPath()
}
// Parse config file
conf := Config{
IKS: &IKSConfig{}, // IKS block may not be populated in secrete toml. Make sure its not nil
}
logger.Info("parsing conf file", zap.String("confpath", confPath))
err := ParseConfig(confPath, &conf, logger)
return &conf, err
}
// GetConfPath get configuration file path
func GetConfPath() string {
if confPath := getEnv("SECRET_CONFIG_PATH"); confPath != "" {
return filepath.Join(confPath, "libconfig.toml")
}
//Get default conf path
return GetDefaultConfPath()
}
// GetConfPathDir get configuration dir path
func GetConfPathDir() string {
if confPath := getEnv("SECRET_CONFIG_PATH"); confPath != "" {
return confPath
}
//Get default conf path
return GetEtcPath()
}
// GetDefaultConfPath get default config file path
func GetDefaultConfPath() string {
return filepath.Join(GetEtcPath(), "libconfig.toml")
}
// ParseConfig ...
func ParseConfig(filePath string, conf interface{}, logger *zap.Logger) error {
_, err := toml.DecodeFile(filePath, conf)
if err != nil {
logger.Error("Failed to parse config file", zap.Error(err))
}
// Read environment variables
err = envconfig.Process("", conf)
if err != nil {
logger.Error("Failed to gather environment config variable", zap.Error(err))
}
return err
}
// ServerConfig configuration options for the provider server itself
type ServerConfig struct {
// DebugTrace is a flag to enable the debug level trace within the provider code.
DebugTrace bool `toml:"debug_trace" envconfig:"DEBUG_TRACE"`
}
// BluemixConfig ...
type BluemixConfig struct {
IamURL string `toml:"iam_url"`
IamClientID string `toml:"iam_client_id"`
IamClientSecret string `toml:"iam_client_secret" json:"-"`
IamAPIKey string `toml:"iam_api_key" json:"-"`
RefreshToken string `toml:"refresh_token" json:"-"`
APIEndpointURL string `toml:"containers_api_route"`
Encryption bool `toml:"encryption"`
}
// SoftlayerConfig ...
type SoftlayerConfig struct {
SoftlayerBlockEnabled bool `toml:"softlayer_block_enabled" envconfig:"SOFTLAYER_BLOCK_ENABLED"`
SoftlayerBlockProviderName string `toml:"softlayer_block_provider_name" envconfig:"SOFTLAYER_BLOCK_PROVIDER_NAME"`
SoftlayerFileEnabled bool `toml:"softlayer_file_enabled" envconfig:"SOFTLAYER_FILE_ENABLED"`
SoftlayerFileProviderName string `toml:"softlayer_file_provider_name" envconfig:"SOFTLAYER_FILE_PROVIDER_NAME"`
SoftlayerUsername string `toml:"softlayer_username" json:"-"`
SoftlayerAPIKey string `toml:"softlayer_api_key" json:"-"`
SoftlayerEndpointURL string `toml:"softlayer_endpoint_url"`
SoftlayerDataCenter string `toml:"softlayer_datacenter"`
SoftlayerTimeout string `toml:"softlayer_api_timeout" envconfig:"SOFTLAYER_API_TIMEOUT"`
SoftlayerVolProvisionTimeout string `toml:"softlayer_vol_provision_timeout" envconfig:"SOFTLAYER_VOL_PROVISION_TIMEOUT"`
SoftlayerRetryInterval string `toml:"softlayer_api_retry_interval" envconfig:"SOFTLAYER_API_RETRY_INTERVAL"`
//Configuration values for JWT tokens
SoftlayerJWTKID string `toml:"softlayer_jwt_kid"`
SoftlayerJWTTTL int `toml:"softlayer_jwt_ttl"`
SoftlayerJWTValidFrom int `toml:"softlayer_jwt_valid"`
SoftlayerIMSEndpointURL string `toml:"softlayer_iam_endpoint_url"`
SoftlayerAPIDebug bool
}
// Gen2Config ...
type Gen2Config struct {
Gen2ProviderEnabled bool `toml:"genesis_provider_enabled"`
Gen2Username string `toml:"genesis_user_name"`
Gen2APIKey string `toml:"genesis_api_key"`
Gen2URL string `toml:"genesis_url"`
}
// VPCProviderConfig configures a specific instance of a VPC provider (e.g. GT/GC/Z)
type VPCProviderConfig struct {
Enabled bool `toml:"vpc_enabled" envconfig:"VPC_ENABLED"`
VPCBlockProviderName string `toml:"vpc_block_provider_name" envconfig:"VPC_BLOCK_PROVIDER_NAME"`
EndpointURL string `toml:"gc_riaas_endpoint_url"`
TokenExchangeURL string `toml:"gc_token_exchange_endpoint_url"`
APIKey string `toml:"gc_api_key" json:"-"`
Encryption bool `toml:"encryption"`
ResourceGroupID string `toml:"gc_resource_group_id"`
VPCTimeout string `toml:"vpc_api_timeout,omitempty" envconfig:"VPC_API_TIMEOUT"`
MaxRetryAttempt int `toml:"max_retry_attempt,omitempty" envconfig:"VPC_RETRY_ATTEMPT"`
MaxRetryGap int `toml:"max_retry_gap,omitempty" envconfig:"VPC_RETRY_INTERVAL"`
// This is in seconds
VPCAPIRetryAttempt int `toml:"vpc_api_retry_attempt,omitempty" envconfig:"VPC_API_RETRY_ATTEMPT"`
VPCAPIRetryInterval int `toml:"vpc_api_retry_interval,omitempty" envconfig:"VPC_API_RETRY_INTERVAL"`
IsVPCAPIExpoRetry bool `toml:"is_vpc_api_expo_retry,omitempty" envconfig:"IS_VPC_API_EXPO_RETRY"`
APIVersion string `toml:"api_version,omitempty" envconfig:"VPC_API_VERSION"`
IsIKS bool `toml:"is_iks,omitempty"`
}
//IKSConfig config
type IKSConfig struct {
Enabled bool `toml:"iks_enabled" envconfig:"IKS_ENABLED"`
IKSBlockProviderName string `toml:"iks_block_provider_name" envconfig:"IKS_BLOCK_PROVIDER_NAME"`
}
// GetEtcPath returns the path to the etc directory
func GetEtcPath() string {
goPath := GetGoPath()
srcPath := filepath.Join("src", "github.com", "IBM",
"ibmcloud-storage-volume-lib")
return filepath.Join(goPath, srcPath, "etc")
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package config
import (
"crypto/tls"
"net/http"
"time"
)
// GeneralCAHttpClient returns an http.Client configured for general use
func GeneralCAHttpClient() (*http.Client, error) {
httpClient := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
MinVersion: tls.VersionTLS12, // Require TLS 1.2 or higher
},
},
// softlayer.go has been overriding http.DefaultClient and forcing 120s
// timeout on us, so we'll continue to force it on ourselves in case
// we've accidentally become acustomed to it.
Timeout: time.Second * 120,
}
return httpClient, nil
}
// GeneralCAHttpClientWithTimeout returns an http.Client configured for general use
func GeneralCAHttpClientWithTimeout(timeout time.Duration) (*http.Client, error) {
httpClient := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
MinVersion: tls.VersionTLS12, // Require TLS 1.2 or higher
},
},
Timeout: timeout,
}
return httpClient, nil
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package util
// SafeStringValue returns the referenced string value, treating nil as equivalent to "".
// It is intended as a type-safe and nil-safe test for empty values in data fields of
func SafeStringValue(s *string) string {
if s == nil {
return ""
}
return *s
}
// StringHasValue returns true if the argument is neither nil nor a pointer to the
// zero/empty string.
func StringHasValue(s *string) bool {
return s != nil && *s != ""
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package util
// These are the error types which all provider should categorize their errors
const (
// ProvisioningFailed volume or snapshot provisioning failed
ProvisioningFailed = "ProvisioningFailed"
// DeletionFailed ...
DeletionFailed = "DeletionFailed"
// RetrivalFailed ...
RetrivalFailed = "RetrivalFailed"
// InvalidRequest ...
InvalidRequest = "InvalidRequest"
// EntityNotFound ...
EntityNotFound = "EntityNotFound"
// PermissionDenied ...
PermissionDenied = "PermissionDenied"
// Unauthenticated ...
Unauthenticated = "Unauthenticated"
// ErrorTypeFailed ...
ErrorTypeFailed = "ErrorTypeConversionFailed"
)
// GetErrorType return the user error type provided by volume provider
func GetErrorType(err error) string {
providerError, ok := err.(Message)
if ok {
return providerError.Type
}
return ErrorTypeFailed
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package util
import (
"errors"
"github.com/IBM/ibmcloud-storage-volume-lib/lib/provider"
"github.com/IBM/ibmcloud-storage-volume-lib/lib/utils/reasoncode"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"reflect"
"time"
)
// NewError returns an error that is implemented by provider.Error.
// If optional wrapped errors are a provider.Error, this preserves all child wrapped
// errors in depth-first order.
func NewError(code reasoncode.ReasonCode, msg string, wrapped ...error) error {
return NewErrorWithProperties(code, msg, nil, wrapped...)
}
// NewErrorWithProperties returns an error that is implemented provider.Error and
// which is decorated with diagnostic properties.
// If optional wrapped errors are a provider.Error, this preserves all child wrapped
// errors in depth-first order.
func NewErrorWithProperties(code reasoncode.ReasonCode, msg string, properties map[string]string, wrapped ...error) error {
if code == "" {
code = "" // TODO: ErrorUnclassified
}
var werrs []string
for _, w := range wrapped {
if w != nil {
werrs = append(werrs, w.Error())
if p, isPerr := w.(provider.Error); isPerr {
for _, u := range p.Wrapped() {
werrs = append(werrs, u)
}
}
}
}
return provider.Error{
Fault: provider.Fault{
ReasonCode: code,
Message: msg,
Properties: properties,
Wrapped: werrs,
},
}
}
// ErrorDeepUnwrapString returns the full list of unwrapped error strings
// Returns empty slice if not a provider.Error
func ErrorDeepUnwrapString(err error) []string {
if perr, isPerr := err.(provider.Error); isPerr && perr.Wrapped() != nil {
return perr.Wrapped()
}
return []string{}
}
// ErrorReasonCode returns reason code if a provider.Error, else ErrorUnclassified
func ErrorReasonCode(err error) reasoncode.ReasonCode {
if pErr, isPerr := err.(provider.Error); isPerr {
if code := pErr.Code(); code != "" {
return code
}
}
return reasoncode.ErrorUnclassified
}
// ErrorToFault returns or builds a Fault pointer for an error (e.g. for a response object)
// Returns nil if no error,
func ErrorToFault(err error) *provider.Fault {
if err == nil {
return nil
}
if pErr, isPerr := err.(provider.Error); isPerr {
return &pErr.Fault
}
return &provider.Fault{
ReasonCode: "", // TODO: ErrorUnclassified,
Message: err.Error(),
}
}
// FaultToError builds a Error from a Fault pointer (e.g. from a response object)
// Returns nil error if no Fault.
func FaultToError(fault *provider.Fault) error {
if fault == nil {
return nil
}
return provider.Error{Fault: *fault}
}
// SetFaultResponse sets the Fault field of any response struct
func SetFaultResponse(fault error, response interface{}) error {
value := reflect.ValueOf(response)
if value.Kind() != reflect.Ptr || value.Elem().Kind() != reflect.Struct {
return errors.New("Value must be a pointer to a struct")
}
field := value.Elem().FieldByName("Fault")
if field.Kind() != reflect.Ptr {
return errors.New("Value struct must have Fault provider.Fault field")
}
field.Set(reflect.ValueOf(ErrorToFault(fault)))
return nil
}
// ZapError returns a zapcore.Field for an error that includes the metadata
// associated with a provider.Error. If the error is not a provider.Error then
// the standard zap.Error is used.
func ZapError(err error) zapcore.Field {
if perr, isPerr := err.(provider.Error); isPerr {
// Use zap.Relfect() to format all fields of struct
// zap.Any() would select standard error formatting
return zap.Reflect("error", perr)
}
return zap.Error(err)
}
//ErrorRetrier retry the fucntion
type ErrorRetrier struct {
MaxAttempts int
RetryInterval time.Duration
Logger *zap.Logger
}
//NewErrorRetrier return new ErrorRetrier
func NewErrorRetrier(maxAttempt int, retryInterval time.Duration, logger *zap.Logger) *ErrorRetrier {
return &ErrorRetrier{
MaxAttempts: maxAttempt,
RetryInterval: retryInterval,
Logger: logger,
}
}
//ErrorRetry path for retry logic with logger passed in
func (er *ErrorRetrier) ErrorRetry(funcToRetry func() (error, bool)) error {
var err error
var shouldStop bool
for i := 0; ; i++ {
err, shouldStop = funcToRetry()
er.Logger.Debug("Retry Function Result", zap.Error(err), zap.Bool("shouldStop", shouldStop))
if shouldStop {
break
}
if err == nil {
return err
}
//Stop if out of retries
if i >= (er.MaxAttempts - 1) {
break
}
time.Sleep(er.RetryInterval)
er.Logger.Warn("retrying after Error:", zap.Error(err))
}
//error set by name above so no need to explicitly return it
return err
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Bluemix Container Registry, 5737-D42
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, * irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package util
import (
"fmt"
)
// Message Wrapper Message/Error Class
type Message struct {
Code string
Type string
RequestID string
Description string
BackendError string
RC int
Action string
}
// Error Implement the Error() interface method
func (msg Message) Error() string {
return msg.Info()
}
// Info ...
func (msg Message) Info() string {
return fmt.Sprintf("{Code:%s, Type:%s, Description:%s, BackendError:%s, RC:%d}", msg.Code, msg.Type, msg.Description, msg.BackendError, msg.RC)
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package util
import (
"fmt"
"log"
"time"
)
// TimeTracker Get execution time of a function
func TimeTracker(functionName string, start time.Time) {
elapsed := time.Since(start)
log.Println(fmt.Sprintf("TIME TAKEN BY FUNCTION %s IS %s", functionName, elapsed))
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package auth
import (
"go.uber.org/zap"
"github.com/IBM/ibmcloud-storage-volume-lib/lib/provider"
)
// ForIaaSAPIKey ...
func (ccf *ContextCredentialsFactory) ForIaaSAPIKey(iamAccountID, userid, apikey string, logger *zap.Logger) (provider.ContextCredentials, error) {
return provider.ContextCredentials{
AuthType: provider.IaaSAPIKey,
IAMAccountID: iamAccountID,
UserID: userid,
Credential: apikey,
}, nil
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package auth
import (
"github.com/IBM/ibmcloud-storage-volume-lib/config"
"github.com/IBM/ibmcloud-storage-volume-lib/provider/local"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/iam"
)
// ContextCredentialsFactory ...
type ContextCredentialsFactory struct {
softlayerConfig *config.SoftlayerConfig
vpcConfig *config.VPCProviderConfig
tokenExchangeService iam.TokenExchangeService
}
var _ local.ContextCredentialsFactory = &ContextCredentialsFactory{}
// NewContextCredentialsFactory ...
func NewContextCredentialsFactory(bluemixConfig *config.BluemixConfig, softlayerConfig *config.SoftlayerConfig, vpcConfig *config.VPCProviderConfig) (*ContextCredentialsFactory, error) {
tokenExchangeService, err := iam.NewTokenExchangeService(bluemixConfig)
if err != nil {
return nil, err
}
return &ContextCredentialsFactory{
softlayerConfig: softlayerConfig,
vpcConfig: vpcConfig,
tokenExchangeService: tokenExchangeService,
}, nil
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package auth
import (
"strconv"
"go.uber.org/zap"
"github.com/IBM/ibmcloud-storage-volume-lib/provider/local"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/iam"
"github.com/IBM/ibmcloud-storage-volume-lib/lib/provider"
)
const (
// IMSToken is an IMS user ID and token
IMSToken = provider.AuthType("IMS_TOKEN")
// IAMAccessToken ...
IAMAccessToken = provider.AuthType("IAM_ACCESS_TOKEN")
)
// ForRefreshToken ...
func (ccf *ContextCredentialsFactory) ForRefreshToken(refreshToken string, logger *zap.Logger) (provider.ContextCredentials, error) {
accessToken, err := ccf.tokenExchangeService.ExchangeRefreshTokenForAccessToken(refreshToken, logger)
if err != nil {
// Must preserve provider error code in the ErrorProviderAccountTemporarilyLocked case
logger.Error("Unable to retrieve access token from refresh token", local.ZapError(err))
return provider.ContextCredentials{}, err
}
imsToken, err := ccf.tokenExchangeService.ExchangeAccessTokenForIMSToken(*accessToken, logger)
if err != nil {
// Must preserve provider error code in the ErrorProviderAccountTemporarilyLocked case
logger.Error("Unable to retrieve IAM token from access token", local.ZapError(err))
return provider.ContextCredentials{}, err
}
return forIMSToken("", imsToken), nil
}
// ForIAMAPIKey ...
func (ccf *ContextCredentialsFactory) ForIAMAPIKey(iamAccountID, apiKey string, logger *zap.Logger) (provider.ContextCredentials, error) {
imsToken, err := ccf.tokenExchangeService.ExchangeIAMAPIKeyForIMSToken(apiKey, logger)
if err != nil {
// Must preserve provider error code in the ErrorProviderAccountTemporarilyLocked case
logger.Error("Unable to retrieve IMS credentials from IAM API key", local.ZapError(err))
return provider.ContextCredentials{}, err
}
return forIMSToken(iamAccountID, imsToken), nil
}
// ForIAMAccessToken ...
func (ccf *ContextCredentialsFactory) ForIAMAccessToken(apiKey string, logger *zap.Logger) (provider.ContextCredentials, error) {
iamAccessToken, err := ccf.tokenExchangeService.ExchangeIAMAPIKeyForAccessToken(apiKey, logger)
if err != nil {
logger.Error("Unable to retrieve IAM access token from IAM API key", local.ZapError(err))
return provider.ContextCredentials{}, err
}
iamAccountID, err := ccf.tokenExchangeService.GetIAMAccountIDFromAccessToken(iam.AccessToken{Token: iamAccessToken.Token}, logger)
if err != nil {
logger.Error("Unable to retrieve IAM access token from IAM API key", local.ZapError(err))
return provider.ContextCredentials{}, err
}
return forIAMAccessToken(iamAccountID, iamAccessToken), nil
}
// forIMSToken ...
func forIMSToken(iamAccountID string, imsToken *iam.IMSToken) provider.ContextCredentials {
return provider.ContextCredentials{
AuthType: IMSToken,
IAMAccountID: iamAccountID,
UserID: strconv.Itoa(imsToken.UserID),
Credential: imsToken.Token,
}
}
// forIAMAccessToken ...
func forIAMAccessToken(iamAccountID string, iamAccessToken *iam.AccessToken) provider.ContextCredentials {
return provider.ContextCredentials{
AuthType: IAMAccessToken,
IAMAccountID: iamAccountID,
Credential: iamAccessToken.Token,
}
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package iam
import (
"encoding/base64"
"errors"
"fmt"
"net/http"
"strings"
"time"
"go.uber.org/zap"
"github.com/IBM-Cloud/ibm-cloud-cli-sdk/common/rest"
"github.com/IBM/ibmcloud-storage-volume-lib/config"
"github.com/IBM/ibmcloud-storage-volume-lib/lib/utils"
)
// tokenExchangeService ...
type tokenExchangeService struct {
bluemixConf *config.BluemixConfig
httpClient *http.Client
}
// TokenExchangeService ...
var _ TokenExchangeService = &tokenExchangeService{}
// NewTokenExchangeServiceWithClient ...
func NewTokenExchangeServiceWithClient(bluemixConf *config.BluemixConfig, httpClient *http.Client) (TokenExchangeService, error) {
return &tokenExchangeService{
bluemixConf: bluemixConf,
httpClient: httpClient,
}, nil
}
// NewTokenExchangeService ...
func NewTokenExchangeService(bluemixConf *config.BluemixConfig) (TokenExchangeService, error) {
httpClient, err := config.GeneralCAHttpClient()
if err != nil {
return nil, err
}
return &tokenExchangeService{
bluemixConf: bluemixConf,
httpClient: httpClient,
}, nil
}
// tokenExchangeRequest ...
type tokenExchangeRequest struct {
tes *tokenExchangeService
request *rest.Request
client *rest.Client
logger *zap.Logger
errorRetrier *util.ErrorRetrier
}
// tokenExchangeResponse ...
type tokenExchangeResponse struct {
AccessToken string `json:"access_token"`
ImsToken string `json:"ims_token"`
ImsUserID int `json:"ims_user_id"`
}
// ExchangeRefreshTokenForAccessToken ...
func (tes *tokenExchangeService) ExchangeRefreshTokenForAccessToken(refreshToken string, logger *zap.Logger) (*AccessToken, error) {
r := tes.newTokenExchangeRequest(logger)
r.request.Field("grant_type", "refresh_token")
r.request.Field("refresh_token", refreshToken)
return r.exchangeForAccessToken()
}
// ExchangeAccessTokenForIMSToken ...
func (tes *tokenExchangeService) ExchangeAccessTokenForIMSToken(accessToken AccessToken, logger *zap.Logger) (*IMSToken, error) {
r := tes.newTokenExchangeRequest(logger)
r.request.Field("grant_type", "urn:ibm:params:oauth:grant-type:derive")
r.request.Field("response_type", "ims_portal")
r.request.Field("access_token", accessToken.Token)
return r.exchangeForIMSToken()
}
// ExchangeIAMAPIKeyForIMSToken ...
func (tes *tokenExchangeService) ExchangeIAMAPIKeyForIMSToken(iamAPIKey string, logger *zap.Logger) (*IMSToken, error) {
r := tes.newTokenExchangeRequest(logger)
r.request.Field("grant_type", "urn:ibm:params:oauth:grant-type:apikey")
r.request.Field("response_type", "ims_portal")
r.request.Field("apikey", iamAPIKey)
return r.exchangeForIMSToken()
}
// ExchangeIAMAPIKeyForAccessToken ...
func (tes *tokenExchangeService) ExchangeIAMAPIKeyForAccessToken(iamAPIKey string, logger *zap.Logger) (*AccessToken, error) {
r := tes.newTokenExchangeRequest(logger)
r.request.Field("grant_type", "urn:ibm:params:oauth:grant-type:apikey")
r.request.Field("apikey", iamAPIKey)
return r.exchangeForAccessToken()
}
// exchangeForAccessToken ...
func (r *tokenExchangeRequest) exchangeForAccessToken() (*AccessToken, error) {
var iamResp *tokenExchangeResponse
var err error
err = r.errorRetrier.ErrorRetry(func() (error, bool) {
iamResp, err = r.sendTokenExchangeRequest()
return err, !isConnectionError(err) // Skip rettry if its not connection error
})
if err != nil {
return nil, err
}
return &AccessToken{Token: iamResp.AccessToken}, nil
}
// exchangeForIMSToken ...
func (r *tokenExchangeRequest) exchangeForIMSToken() (*IMSToken, error) {
var iamResp *tokenExchangeResponse
var err error
err = r.errorRetrier.ErrorRetry(func() (error, bool) {
iamResp, err = r.sendTokenExchangeRequest()
return err, !isConnectionError(err)
})
if err != nil {
return nil, err
}
return &IMSToken{
UserID: iamResp.ImsUserID,
Token: iamResp.ImsToken,
}, nil
}
// newTokenExchangeRequest ...
func (tes *tokenExchangeService) newTokenExchangeRequest(logger *zap.Logger) *tokenExchangeRequest {
client := rest.NewClient()
client.HTTPClient = tes.httpClient
retyrInterval, _ := time.ParseDuration("3s")
return &tokenExchangeRequest{
tes: tes,
request: rest.PostRequest(fmt.Sprintf("%s/oidc/token", tes.bluemixConf.IamURL)),
client: client,
logger: logger,
errorRetrier: util.NewErrorRetrier(40, retyrInterval, logger),
}
}
// sendTokenExchangeRequest ...
func (r *tokenExchangeRequest) sendTokenExchangeRequest() (*tokenExchangeResponse, error) {
// Set headers
basicAuth := fmt.Sprintf("%s:%s", r.tes.bluemixConf.IamClientID, r.tes.bluemixConf.IamClientSecret)
r.request.Set("Authorization", fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(basicAuth))))
r.request.Set("Accept", "application/json")
// Make the request
var successV tokenExchangeResponse
var errorV = struct {
ErrorMessage string `json:"errorMessage"`
ErrorType string `json:"errorCode"`
ErrorDetails string `json:"errorDetails"`
Requirements struct {
Error string `json:"error"`
Code string `json:"code"`
} `json:"requirements"`
}{}
r.logger.Info("Sending IAM token exchange request")
resp, err := r.client.Do(r.request, &successV, &errorV)
if err != nil {
r.logger.Error("IAM token exchange request failed", zap.Reflect("Response", resp), zap.Error(err))
// TODO Handle timeout here?
return nil,
util.NewError("ErrorUnclassified",
"IAM token exchange request failed", err)
}
if resp != nil && resp.StatusCode == 200 {
r.logger.Debug("IAM token exchange request successful")
return &successV, nil
}
// TODO Check other status code values? (but be careful not to mask the reason codes, below)
if errorV.ErrorMessage != "" {
r.logger.Error("IAM token exchange request failed with message",
zap.Int("StatusCode", resp.StatusCode),
zap.String("ErrorMessage:", errorV.ErrorMessage),
zap.String("ErrorType:", errorV.ErrorType),
zap.Reflect("Error", errorV))
err := util.NewError("ErrorFailedTokenExchange",
"IAM token exchange request failed: "+errorV.ErrorMessage,
errors.New(errorV.ErrorDetails+" "+errorV.Requirements.Code+": "+errorV.Requirements.Error))
if errorV.Requirements.Code == "SoftLayer_Exception_User_Customer_AccountLocked" {
err = util.NewError("ErrorProviderAccountTemporarilyLocked",
"Infrastructure account is temporarily locked", err)
}
return nil, err
}
r.logger.Error("Unexpected IAM token exchange response",
zap.Int("StatusCode", resp.StatusCode), zap.Reflect("Response", resp))
return nil,
util.NewError("ErrorUnclassified",
"Unexpected IAM token exchange response")
}
func isConnectionError(err error) bool {
if err != nil {
wrappedErrors := util.ErrorDeepUnwrapString(err)
// wrapped error contains actual backend error
for _, werr := range wrappedErrors {
if strings.Contains(werr, "tcp") {
// if error contains "tcp" string, its connection error
return true
}
}
}
return false
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package iam
import (
"errors"
"github.com/dgrijalva/jwt-go"
"go.uber.org/zap"
)
type accessTokenClaims struct {
jwt.StandardClaims
Account struct {
Bss string `json:"bss"`
} `json:"account"`
}
func (r *tokenExchangeService) GetIAMAccountIDFromAccessToken(accessToken AccessToken, logger *zap.Logger) (accountID string, err error) {
// TODO - TEMPORARY CODE - VERIFY SIGNATURE HERE
token, _, err := new(jwt.Parser).ParseUnverified(accessToken.Token, &accessTokenClaims{})
if err != nil {
return
}
token.Valid = true
// TODO - TEMPORARY CODE - DONT OVERRIDE VERIFICATION
claims, haveClaims := token.Claims.(*accessTokenClaims)
logger.Debug("Access token parsed", zap.Bool("haveClaims", haveClaims), zap.Bool("valid", token.Valid))
if !token.Valid || !haveClaims {
err = errors.New("Access token invalid")
return
}
accountID = claims.Account.Bss
logger.Debug("GetIAMAccountIDFromAccessToken", zap.Reflect("claims.Account.Bss", claims.Account.Bss))
return
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package provider
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/provider"
"github.com/IBM/ibmcloud-storage-volume-lib/lib/utils/reasoncode"
userError "github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/messages"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/models"
"go.uber.org/zap"
)
//VpcVolumeAttachment ...
const (
VpcVolumeAttachment = "vpcVolumeAttachment"
StatusAttached = "attached"
StatusAttaching = "attaching"
StatusDetaching = "detaching"
)
// AttachVolume attach volume based on given volume attachment request
func (vpcs *VPCSession) AttachVolume(volumeAttachmentRequest provider.VolumeAttachmentRequest) (*provider.VolumeAttachmentResponse, error) {
vpcs.Logger.Debug("Entry of AttachVolume method...")
defer vpcs.Logger.Debug("Exit from AttachVolume method...")
var err error
vpcs.Logger.Info("Validating basic inputs for Attach method...", zap.Reflect("volumeAttachRequest", volumeAttachmentRequest))
err = vpcs.validateAttachVolumeRequest(volumeAttachmentRequest)
if err != nil {
return nil, err
}
var volumeAttachResult *models.VolumeAttachment
// First , check if volume is already attached or attaching to given instance
vpcs.Logger.Info("Checking if volume is already attached ")
currentVolAttachment, err := vpcs.GetVolumeAttachment(volumeAttachmentRequest)
if err == nil && currentVolAttachment != nil && currentVolAttachment.Status != StatusDetaching {
vpcs.Logger.Info("volume is already attached", zap.Reflect("currentVolAttachment", currentVolAttachment))
return currentVolAttachment, nil
}
//Try attaching volume if it's not already attached or there is error in getting current volume attachment
vpcs.Logger.Info("Attaching volume from VPC provider...", zap.Bool("IKSEnabled?", vpcs.Config.IsIKS))
volumeAttachment := models.NewVolumeAttachment(volumeAttachmentRequest)
err = vpcs.APIRetry.FlexyRetry(vpcs.Logger, func() (error, bool) {
volumeAttachResult, err = vpcs.APIClientVolAttachMgr.AttachVolume(&volumeAttachment, vpcs.Logger)
// Keep retry, until we get the proper volumeAttachResult object
if err != nil {
return err, skipRetryForAttach(err, vpcs.Config.IsIKS)
}
return err, true // stop retry as no error
})
if err != nil {
userErr := userError.GetUserError(string(userError.VolumeAttachFailed), err, volumeAttachmentRequest.VolumeID, volumeAttachmentRequest.InstanceID)
return nil, userErr
}
varp := volumeAttachResult.ToVolumeAttachmentResponse()
vpcs.Logger.Info("Successfully attached volume from VPC provider", zap.Reflect("volumeResponse", varp))
return varp, nil
}
// validateVolume validating volume ID
func (vpcs *VPCSession) validateAttachVolumeRequest(volumeAttachRequest provider.VolumeAttachmentRequest) error {
var err error
// Check for InstanceID - required validation
if len(volumeAttachRequest.InstanceID) == 0 {
err = userError.GetUserError(string(reasoncode.ErrorRequiredFieldMissing), nil, "InstanceID")
vpcs.Logger.Error("volumeAttachRequest.InstanceID is required", zap.Error(err))
return err
}
// Check for VolumeID - required validation
if len(volumeAttachRequest.VolumeID) == 0 {
err = userError.GetUserError(string(reasoncode.ErrorRequiredFieldMissing), nil, "VolumeID")
vpcs.Logger.Error("volumeAttachRequest.VolumeID is required", zap.Error(err))
return err
}
return nil
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package provider
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/provider"
"go.uber.org/zap"
)
//AuthorizeVolume allows aceess to volume based on given authorization
func (vpcs *VPCSession) AuthorizeVolume(volumeAuthorization provider.VolumeAuthorization) error {
vpcs.Logger.Info("Entry AuthorizeVolume", zap.Reflect("volumeAuthorization", volumeAuthorization))
defer vpcs.Logger.Info("Exit AuthorizeVolume", zap.Reflect("volumeAuthorization", volumeAuthorization))
return nil
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package provider
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/provider"
userError "github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/messages"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/models"
"go.uber.org/zap"
)
// CreateSnapshot Create snapshot from given volume
func (vpcs *VPCSession) CreateSnapshot(volumeRequest *provider.Volume, tags map[string]string) (*provider.Snapshot, error) {
vpcs.Logger.Info("Entry CreateSnapshot", zap.Reflect("volumeRequest", volumeRequest))
defer vpcs.Logger.Info("Exit CreateSnapshot", zap.Reflect("volumeRequest", volumeRequest))
if volumeRequest == nil {
return nil, userError.GetUserError("StorageFindFailedWithVolumeId", nil, "Not a valid volume ID")
}
var snapshot *models.Snapshot
var err error
// Step 1- validate input which are required
vpcs.Logger.Info("Requested volume is:", zap.Reflect("Volume", volumeRequest))
var volume *models.Volume
err = retry(vpcs.Logger, func() error {
volume, err = vpcs.Apiclient.VolumeService().GetVolume(volumeRequest.VolumeID, vpcs.Logger)
return err
})
if err != nil {
return nil, userError.GetUserError("StorageFindFailedWithVolumeId", err, "Not a valid volume ID")
}
if volume == nil {
return nil, userError.GetUserError("StorageFindFailedWithVolumeId", err, volumeRequest.VolumeID, "Not a valid volume ID")
}
err = retry(vpcs.Logger, func() error {
snapshot, err = vpcs.Apiclient.SnapshotService().CreateSnapshot(volumeRequest.VolumeID, snapshot, vpcs.Logger)
return err
})
if err != nil {
return nil, userError.GetUserError("SnapshotSpaceOrderFailed", err)
}
vpcs.Logger.Info("Successfully created snapshot with backend (vpcclient) call")
vpcs.Logger.Info("Backend created snapshot details", zap.Reflect("Snapshot", snapshot))
// Converting volume to lib volume type
volumeResponse := FromProviderToLibVolume(volume, vpcs.Logger)
if volumeResponse != nil {
respSnapshot := &provider.Snapshot{
Volume: *volumeResponse,
SnapshotID: snapshot.ID,
SnapshotCreationTime: *snapshot.CreatedAt,
}
return respSnapshot, nil
}
return nil, userError.GetUserError("CoversionNotSuccessful", err, "Not able to prepare provider volume")
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package provider
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/provider"
userError "github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/messages"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/models"
"go.uber.org/zap"
)
const (
customProfile = "custom"
minSize = 10
)
// CreateVolume Get the volume by using ID
func (vpcs *VPCSession) CreateVolume(volumeRequest provider.Volume) (volumeResponse *provider.Volume, err error) {
vpcs.Logger.Debug("Entry of CreateVolume method...")
defer vpcs.Logger.Debug("Exit from CreateVolume method...")
vpcs.Logger.Info("Basic validation for CreateVolume request... ", zap.Reflect("RequestedVolumeDetails", volumeRequest))
resourceGroup, iops, err := validateVolumeRequest(volumeRequest)
if err != nil {
return nil, err
}
vpcs.Logger.Info("Successfully validated inputs for CreateVolume request... ")
// Build the template to send to backend
volumeTemplate := &models.Volume{
Name: *volumeRequest.Name,
Capacity: int64(*volumeRequest.Capacity),
Iops: iops,
Tags: volumeRequest.VPCVolume.Tags,
ResourceGroup: &resourceGroup,
Generation: models.GenerationType(volumeRequest.Generation),
Profile: &models.Profile{
Name: volumeRequest.VPCVolume.Profile.Name,
},
Zone: &models.Zone{
Name: volumeRequest.Az,
},
}
var encryptionKeyCRN string
if volumeRequest.VPCVolume.VolumeEncryptionKey != nil && len(volumeRequest.VPCVolume.VolumeEncryptionKey.CRN) > 0 {
encryptionKeyCRN = volumeRequest.VPCVolume.VolumeEncryptionKey.CRN
volumeTemplate.VolumeEncryptionKey = &models.VolumeEncryptionKey{CRN: encryptionKeyCRN}
}
vpcs.Logger.Info("Calling VPC provider for volume creation...")
var volume *models.Volume
err = retry(vpcs.Logger, func() error {
volume, err = vpcs.Apiclient.VolumeService().CreateVolume(volumeTemplate, vpcs.Logger)
return err
})
if err != nil {
vpcs.Logger.Debug("Failed to create volume from VPC provider", zap.Reflect("BackendError", err))
return nil, userError.GetUserError("FailedToPlaceOrder", err)
}
vpcs.Logger.Info("Successfully created volume from VPC provider...", zap.Reflect("VolumeDetails", volume))
vpcs.Logger.Info("Waiting for volume to be in valid (available) state", zap.Reflect("VolumeDetails", volume))
err = WaitForValidVolumeState(vpcs, volume.ID)
if err != nil {
return nil, userError.GetUserError("VolumeNotInValidState", err, volume.ID)
}
vpcs.Logger.Info("Volume got valid (available) state", zap.Reflect("VolumeDetails", volume))
// Converting volume to lib volume type
volumeResponse = FromProviderToLibVolume(volume, vpcs.Logger)
return volumeResponse, err
}
// validateVolumeRequest validating volume request
func validateVolumeRequest(volumeRequest provider.Volume) (models.ResourceGroup, int64, error) {
resourceGroup := models.ResourceGroup{}
var iops int64
iops = 0
// Volume name should not be empty
if volumeRequest.Name == nil {
return resourceGroup, iops, userError.GetUserError("InvalidVolumeName", nil, nil)
} else if len(*volumeRequest.Name) == 0 {
return resourceGroup, iops, userError.GetUserError("InvalidVolumeName", nil, *volumeRequest.Name)
}
// Capacity should not be empty
if volumeRequest.Capacity == nil {
return resourceGroup, iops, userError.GetUserError("VolumeCapacityInvalid", nil, nil)
} else if *volumeRequest.Capacity < minSize {
return resourceGroup, iops, userError.GetUserError("VolumeCapacityInvalid", nil, *volumeRequest.Capacity)
}
// Read user provided error, no harm to pass the 0 values to RIaaS in case of tiered profiles
if volumeRequest.Iops != nil {
iops = ToInt64(*volumeRequest.Iops)
}
if volumeRequest.VPCVolume.Profile.Name != customProfile && iops > 0 {
return resourceGroup, iops, userError.GetUserError("VolumeProfileIopsInvalid", nil)
}
// validate and add resource group ID or Name whichever is provided by user
if volumeRequest.VPCVolume.ResourceGroup == nil {
return resourceGroup, iops, userError.GetUserError("EmptyResourceGroup", nil)
}
// validate and add resource group ID or Name whichever is provided by user
if len(volumeRequest.VPCVolume.ResourceGroup.ID) == 0 && len(volumeRequest.VPCVolume.ResourceGroup.Name) == 0 {
return resourceGroup, iops, userError.GetUserError("EmptyResourceGroupIDandName", nil)
}
if len(volumeRequest.VPCVolume.ResourceGroup.ID) > 0 {
resourceGroup.ID = volumeRequest.VPCVolume.ResourceGroup.ID
}
if len(volumeRequest.VPCVolume.ResourceGroup.Name) > 0 {
// get the resource group ID from resource group name as Name is not supported by RIaaS
resourceGroup.Name = volumeRequest.VPCVolume.ResourceGroup.Name
}
return resourceGroup, iops, nil
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package provider
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/provider"
"go.uber.org/zap"
)
// CreateVolumeFromSnapshot creates the volume by using ID
func (vpcs *VPCSession) CreateVolumeFromSnapshot(snapshot provider.Snapshot, tags map[string]string) (*provider.Volume, error) {
vpcs.Logger.Info("Entry CreateVolumeFromSnapshot", zap.Reflect("Snapshot", snapshot))
defer vpcs.Logger.Info("Exit CreateVolumeFromSnapshot", zap.Reflect("Snapshot", snapshot))
return nil, nil
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package provider
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/provider"
userError "github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/messages"
"go.uber.org/zap"
)
// DeleteSnapshot delete snapshot
func (vpcs *VPCSession) DeleteSnapshot(snapshot *provider.Snapshot) error {
vpcs.Logger.Info("Entry DeleteSnapshot", zap.Reflect("snapshot", snapshot))
defer vpcs.Logger.Info("Exit DeleteSnapshot", zap.Reflect("snapshot", snapshot))
var err error
_, err = vpcs.GetSnapshot(snapshot.SnapshotID)
if err != nil {
return userError.GetUserError("StorageFindFailedWithSnapshotId", err, snapshot.SnapshotID, "Not a valid snapshot ID")
}
err = retry(vpcs.Logger, func() error {
err = vpcs.Apiclient.SnapshotService().DeleteSnapshot(snapshot.Volume.VolumeID, snapshot.SnapshotID, vpcs.Logger)
return err
})
if err != nil {
return userError.GetUserError("FailedToDeleteSnapshot", err)
}
vpcs.Logger.Info("Successfully deleted the snapshot with backend (vpcclient) call)")
return nil
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package provider
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/provider"
userError "github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/messages"
"go.uber.org/zap"
)
// DeleteVolume deletes the volume
func (vpcs *VPCSession) DeleteVolume(volume *provider.Volume) (err error) {
vpcs.Logger.Debug("Entry of DeleteVolume method...")
defer vpcs.Logger.Debug("Exit from DeleteVolume method...")
vpcs.Logger.Info("Validating basic inputs for DeleteVolume method...", zap.Reflect("VolumeDetails", volume))
err = validateVolume(volume)
if err != nil {
return err
}
vpcs.Logger.Info("Deleting volume from VPC provider...")
err = retry(vpcs.Logger, func() error {
err = vpcs.Apiclient.VolumeService().DeleteVolume(volume.VolumeID, vpcs.Logger)
return err
})
if err != nil {
return userError.GetUserError("FailedToDeleteVolume", err, volume.VolumeID)
}
vpcs.Logger.Info("Successfully deleted volume from VPC provider")
return err
}
// validateVolume validating volume ID
func validateVolume(volume *provider.Volume) (err error) {
if volume == nil {
err = userError.GetUserError("InvalidVolumeID", nil, nil)
return
}
if IsValidVolumeIDFormat(volume.VolumeID) {
return nil
}
err = userError.GetUserError("InvalidVolumeID", nil, volume.VolumeID)
return
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package provider
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/provider"
userError "github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/messages"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/models"
"go.uber.org/zap"
"net/http"
)
// DetachVolume detach volume based on given volume attachment request
func (vpcs *VPCSession) DetachVolume(volumeAttachmentTemplate provider.VolumeAttachmentRequest) (*http.Response, error) {
vpcs.Logger.Debug("Entry of DetachVolume method...")
defer vpcs.Logger.Debug("Exit from DetachVolume method...")
var err error
vpcs.Logger.Info("Validating basic inputs for detach method...", zap.Reflect("volumeAttachmentTemplate", volumeAttachmentTemplate))
err = vpcs.validateAttachVolumeRequest(volumeAttachmentTemplate)
if err != nil {
return nil, err
}
var response *http.Response
// First , check if volume is already attached to given instance
vpcs.Logger.Info("Checking if volume is already attached ")
currentVolAttachment, err := vpcs.GetVolumeAttachment(volumeAttachmentTemplate)
if err == nil && currentVolAttachment.Status != StatusDetaching {
// If no error and current volume is not already in detaching state ( i.e in attached or attaching state) attemp to detach
vpcs.Logger.Info("Found volume attachment", zap.Reflect("currentVolAttachment", currentVolAttachment))
volumeAttachment := models.NewVolumeAttachment(volumeAttachmentTemplate)
volumeAttachment.ID = currentVolAttachment.VPCVolumeAttachment.ID
vpcs.Logger.Info("Detaching volume from VPC provider...")
err = vpcs.APIRetry.FlexyRetry(vpcs.Logger, func() (error, bool) {
response, err = vpcs.APIClientVolAttachMgr.DetachVolume(&volumeAttachment, vpcs.Logger)
return err, err == nil // Retry in case of all errors
})
if err != nil {
userErr := userError.GetUserError(string(userError.VolumeDetachFailed), err, volumeAttachmentTemplate.VolumeID, volumeAttachmentTemplate.InstanceID, volumeAttachment.ID)
vpcs.Logger.Error("Volume detach failed with error", zap.Error(err))
return response, userErr
}
vpcs.Logger.Info("Successfully detached volume from VPC provider", zap.Reflect("resp", response))
return response, nil
}
vpcs.Logger.Info("No volume attachment found for", zap.Reflect("currentVolAttachment", currentVolAttachment), zap.Error(err))
// consider volume detach success if its already in Detaching or VolumeAttachment is not found
response = &http.Response{
StatusCode: http.StatusOK,
}
return response, nil
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package provider
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/utils/reasoncode"
)
// Error implements the error interface for a Fault.
// Most easily constructed using util.NewError() or util.NewErrorWithProperties()
type Error struct {
// Fault ...
Fault Fault
}
// Fault encodes a fault condition.
// Does not implement the error interface so that cannot be accidentally
// misassigned to error variables when returned in a function response.
type Fault struct {
// Message is the fault message (required)
Message string `json:"msg"`
// ReasonCode is fault reason code (required) //TODO: will have better reasoncode mechanism
ReasonCode reasoncode.ReasonCode `json:"code"`
// WrappedErrors contains wrapped error messages (if applicable)
Wrapped []string `json:"wrapped,omitempty"`
// Properties contains diagnostic properties (if applicable)
Properties map[string]string `json:"properties,omitempty"`
}
// FaultResponse is an optional Fault
type FaultResponse struct {
Fault *Fault `json:"fault,omitempty"`
}
var _ error = Error{}
// Error satisfies the error contract
func (err Error) Error() string {
return err.Fault.Message
}
// Code satisfies the legacy provider.Error interface
func (err Error) Code() reasoncode.ReasonCode {
if err.Fault.ReasonCode == "" {
return reasoncode.ErrorUnclassified
}
return err.Fault.ReasonCode
}
// Wrapped mirrors the legacy provider.Error interface
func (err Error) Wrapped() []string {
return err.Fault.Wrapped
}
// Properties satisfies the legacy provider.Error interface
func (err Error) Properties() map[string]string {
return err.Fault.Properties
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package provider
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/provider"
userError "github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/messages"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/models"
"go.uber.org/zap"
)
// GetSnapshot get snapshot
func (vpcs *VPCSession) GetSnapshot(snapshotID string) (*provider.Snapshot, error) {
vpcs.Logger.Info("Entry GetSnapshot", zap.Reflect("SnapshotID", snapshotID))
defer vpcs.Logger.Info("Exit GetSnapshot", zap.Reflect("SnapshotID", snapshotID))
return nil, nil
}
// GetSnapshotWithVolumeID get snapshot
func (vpcs *VPCSession) GetSnapshotWithVolumeID(volumeID string, snapshotID string) (*provider.Snapshot, error) {
vpcs.Logger.Info("Entry GetSnapshot", zap.Reflect("SnapshotID", snapshotID))
defer vpcs.Logger.Info("Exit GetSnapshot", zap.Reflect("SnapshotID", snapshotID))
var err error
var snapshot *models.Snapshot
err = retry(vpcs.Logger, func() error {
snapshot, err = vpcs.Apiclient.SnapshotService().GetSnapshot(volumeID, snapshotID, vpcs.Logger)
return err
})
if err != nil {
return nil, userError.GetUserError("FailedToDeleteSnapshot", err)
}
vpcs.Logger.Info("Successfully retrieved the snapshot details", zap.Reflect("Snapshot", snapshot))
volume, err := vpcs.GetVolume(volumeID)
if err != nil {
return nil, userError.GetUserError("StorageFindFailedWithVolumeId", err, volume.VolumeID, "Not a valid volume ID")
}
respSnapshot := &provider.Snapshot{
SnapshotID: snapshot.ID,
Volume: *volume,
}
vpcs.Logger.Info("Successfully retrieved the snapshot details", zap.Reflect("Provider snapshot", respSnapshot))
return respSnapshot, nil
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package provider
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/provider"
userError "github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/messages"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/models"
"go.uber.org/zap"
)
// GetVolume gets the volume by using ID
func (vpcs *VPCSession) GetVolume(id string) (respVolume *provider.Volume, err error) {
vpcs.Logger.Debug("Entry of GetVolume method...")
defer vpcs.Logger.Debug("Exit from GetVolume method...")
vpcs.Logger.Info("Basic validation for volume ID...", zap.Reflect("VolumeID", id))
// validating volume ID
err = validateVolumeID(id)
if err != nil {
return nil, err
}
vpcs.Logger.Info("Getting volume details from VPC provider...", zap.Reflect("VolumeID", id))
var volume *models.Volume
err = retry(vpcs.Logger, func() error {
volume, err = vpcs.Apiclient.VolumeService().GetVolume(id, vpcs.Logger)
return err
})
if err != nil {
return nil, userError.GetUserError("StorageFindFailedWithVolumeId", err, id)
}
vpcs.Logger.Info("Successfully retrieved volume details from VPC backend", zap.Reflect("VolumeDetails", volume))
// Converting volume to lib volume type
respVolume = FromProviderToLibVolume(volume, vpcs.Logger)
return respVolume, err
}
// GetVolumeByName ...
func (vpcs *VPCSession) GetVolumeByName(name string) (respVolume *provider.Volume, err error) {
vpcs.Logger.Debug("Entry of GetVolumeByName method...")
defer vpcs.Logger.Debug("Exit from GetVolumeByName method...")
vpcs.Logger.Info("Basic validation for volume Name...", zap.Reflect("VolumeName", name))
if len(name) <= 0 {
err = userError.GetUserError("InvalidVolumeName", nil, name)
return
}
vpcs.Logger.Info("Getting volume details from VPC provider...", zap.Reflect("VolumeName", name))
var volume *models.Volume
err = retry(vpcs.Logger, func() error {
volume, err = vpcs.Apiclient.VolumeService().GetVolumeByName(name, vpcs.Logger)
return err
})
if err != nil {
return nil, userError.GetUserError("StorageFindFailedWithVolumeName", err, name)
}
vpcs.Logger.Info("Successfully retrieved volume details from VPC backend", zap.Reflect("VolumeDetails", volume))
// Converting volume to lib volume type
respVolume = FromProviderToLibVolume(volume, vpcs.Logger)
return respVolume, err
}
// validateVolumeID validating basic volume ID
func validateVolumeID(volumeID string) (err error) {
if IsValidVolumeIDFormat(volumeID) {
return nil
}
err = userError.GetUserError("InvalidVolumeID", nil, volumeID)
return
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package provider
import (
"errors"
"github.com/IBM/ibmcloud-storage-volume-lib/lib/provider"
userError "github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/messages"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/models"
"go.uber.org/zap"
)
// GetVolumeAttachment get the volume attachment based on the request
func (vpcs *VPCSession) GetVolumeAttachment(volumeAttachmentRequest provider.VolumeAttachmentRequest) (*provider.VolumeAttachmentResponse, error) {
vpcs.Logger.Debug("Entry of GetVolumeAttachment method...", zap.Reflect("volumeAttachmentRequest", volumeAttachmentRequest))
defer vpcs.Logger.Debug("Exit from GetVolumeAttachment method...")
var err error
vpcs.Logger.Info("Validating basic inputs for GetVolumeAttachment method...", zap.Reflect("volumeAttachRequest", volumeAttachmentRequest))
err = vpcs.validateAttachVolumeRequest(volumeAttachmentRequest)
if err != nil {
return nil, err
}
var volumeAttachmentResponse *provider.VolumeAttachmentResponse
volumeAttachment := models.NewVolumeAttachment(volumeAttachmentRequest)
if len(volumeAttachment.ID) > 0 {
//Get volume attachmet by ID if its specified
volumeAttachmentResponse, err = vpcs.getVolumeAttachmentByID(volumeAttachment)
} else {
// Get volume attachment by Volume ID. This is inefficient operation which requires iteration over volume attachment list
volumeAttachmentResponse, err = vpcs.getVolumeAttachmentByVolumeID(volumeAttachment)
}
vpcs.Logger.Info("Volume attachment response", zap.Reflect("volumeAttachmentResponse", volumeAttachmentResponse), zap.Error(err))
return volumeAttachmentResponse, err
}
func (vpcs *VPCSession) getVolumeAttachmentByID(volumeAttachmentRequest models.VolumeAttachment) (*provider.VolumeAttachmentResponse, error) {
vpcs.Logger.Debug("Entry of getVolumeAttachmentByID()")
defer vpcs.Logger.Debug("Exit from getVolumeAttachmentByID()")
vpcs.Logger.Info("Getting VolumeAttachment from VPC provider...")
var err error
var volumeAttachmentResult *models.VolumeAttachment
/*err = retry(vpcs.Logger, func() error {
volumeAttachmentResult, err = vpcs.APIClientVolAttachMgr.GetVolumeAttachment(&volumeAttachmentRequest, vpcs.Logger)
return err
})*/
err = vpcs.APIRetry.FlexyRetry(vpcs.Logger, func() (error, bool) {
volumeAttachmentResult, err = vpcs.APIClientVolAttachMgr.GetVolumeAttachment(&volumeAttachmentRequest, vpcs.Logger)
// Keep retry, until we get the proper volumeAttachmentRequest object
if err != nil {
return err, skipRetryForAttach(err, vpcs.Config.IsIKS)
}
return err, true // stop retry as no error
})
if err != nil {
// API call is failed
userErr := userError.GetUserError(string(userError.VolumeAttachFindFailed), err, volumeAttachmentRequest.Volume.ID, *volumeAttachmentRequest.InstanceID)
return nil, userErr
}
volumeAttachmentResponse := volumeAttachmentResult.ToVolumeAttachmentResponse()
vpcs.Logger.Info("Successfuly retrived volume attachment", zap.Reflect("volumeAttachmentResponse", volumeAttachmentResponse))
return volumeAttachmentResponse, err
}
func (vpcs *VPCSession) getVolumeAttachmentByVolumeID(volumeAttachmentRequest models.VolumeAttachment) (*provider.VolumeAttachmentResponse, error) {
vpcs.Logger.Debug("Entry of getVolumeAttachmentByVolumeID()")
defer vpcs.Logger.Debug("Exit from getVolumeAttachmentByVolumeID()")
vpcs.Logger.Info("Getting VolumeAttachmentList from VPC provider...")
var volumeAttachmentList *models.VolumeAttachmentList
var err error
err = vpcs.APIRetry.FlexyRetry(vpcs.Logger, func() (error, bool) {
volumeAttachmentList, err = vpcs.APIClientVolAttachMgr.ListVolumeAttachment(&volumeAttachmentRequest, vpcs.Logger)
// Keep retry, until we get the proper volumeAttachmentRequest object
if err != nil {
return err, skipRetryForAttach(err, vpcs.Config.IsIKS)
}
return err, true // stop retry as no error
})
if err != nil {
// API call is failed
userErr := userError.GetUserError(string(userError.VolumeAttachFindFailed), err, volumeAttachmentRequest.Volume.ID, *volumeAttachmentRequest.InstanceID)
return nil, userErr
}
// Iterate over the volume attachment list for given instance
for _, volumeAttachmentItem := range volumeAttachmentList.VolumeAttachments {
// Check if volume ID is matching with requested volume ID
if volumeAttachmentItem.Volume.ID == volumeAttachmentRequest.Volume.ID {
vpcs.Logger.Info("Successfully found volume attachment", zap.Reflect("volumeAttachment", volumeAttachmentItem))
volumeResponse := volumeAttachmentItem.ToVolumeAttachmentResponse()
vpcs.Logger.Info("Successfully fetched volume attachment from VPC provider", zap.Reflect("volumeResponse", volumeResponse))
return volumeResponse, nil
}
}
// No volume attahment found in the list. So return error
userErr := userError.GetUserError(string(userError.VolumeAttachFindFailed), errors.New("No VolumeAttachment Found"), volumeAttachmentRequest.Volume.ID, *volumeAttachmentRequest.InstanceID)
vpcs.Logger.Error("Volume attachment not found", zap.Error(err))
return nil, userErr
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package provider
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/provider"
"go.uber.org/zap"
)
// GetVolumeByRequestID get volume by volume ID
func (vpcs *VPCSession) GetVolumeByRequestID(requestID string) (*provider.Volume, error) {
vpcs.Logger.Info("Entry GetVolumeByRequestID", zap.Reflect("requestID", requestID))
defer vpcs.Logger.Info("Exit GetVolumeByRequestID", zap.Reflect("requestID", requestID))
return nil, nil
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package provider
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/provider"
"go.uber.org/zap"
)
// ListAllSnapshots list all snapshots
func (vpcs *VPCSession) ListAllSnapshots(volumeID string) ([]*provider.Snapshot, error) {
vpcs.Logger.Info("Entry ListAllSnapshots", zap.Reflect("VolumeID", volumeID))
defer vpcs.Logger.Info("Exit ListAllSnapshots", zap.Reflect("VolumeID", volumeID))
return nil, nil
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package provider
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/provider"
)
// ListSnapshots list all snapshots
func (vpcs *VPCSession) ListSnapshots() ([]*provider.Snapshot, error) {
vpcs.Logger.Info("Entry ListeSnapshots")
defer vpcs.Logger.Info("Exit ListSnapshots")
return nil, nil
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package provider
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/provider"
"go.uber.org/zap"
)
// ListVolumes list all volumes
func (vpcs *VPCSession) ListVolumes(tags map[string]string) ([]*provider.Volume, error) {
vpcs.Logger.Info("Entry ListVolumes", zap.Reflect("Tags", tags))
defer vpcs.Logger.Info("Exit ListVolumes", zap.Reflect("Tags", tags))
//! TODO: we may implement
return nil, nil
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package provider
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/provider"
userError "github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/messages"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/models"
"go.uber.org/zap"
)
// OrderSnapshot order snapshot
func (vpcs *VPCSession) OrderSnapshot(volumeRequest provider.Volume) error {
vpcs.Logger.Info("Entry OrderSnapshot", zap.Reflect("volumeRequest", volumeRequest))
defer vpcs.Logger.Info("Exit OrderSnapshot", zap.Reflect("volumeRequest", volumeRequest))
var snapshot *models.Snapshot
var err error
// Step 1- validate input which are required
vpcs.Logger.Info("Requested volume is:", zap.Reflect("Volume", volumeRequest))
var volume *models.Volume
err = retry(vpcs.Logger, func() error {
volume, err = vpcs.Apiclient.VolumeService().GetVolume(volumeRequest.VolumeID, vpcs.Logger)
return err
})
if err != nil {
return userError.GetUserError("StorageFindFailedWithVolumeId", err, volumeRequest.VolumeID, "Not a valid volume ID")
}
vpcs.Logger.Info("Successfully retrieved given volume details from VPC provider", zap.Reflect("VolumeDetails", volume))
err = retry(vpcs.Logger, func() error {
snapshot, err = vpcs.Apiclient.SnapshotService().CreateSnapshot(volumeRequest.VolumeID, snapshot, vpcs.Logger)
return err
})
if err != nil {
return userError.GetUserError("SnapshotSpaceOrderFailed", err)
}
vpcs.Logger.Info("Successfully created the snapshot with backend (vpcclient) call.", zap.Reflect("Snapshot", snapshot))
return nil
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package provider
import (
"context"
"errors"
"fmt"
"github.com/IBM/ibmcloud-storage-volume-lib/config"
"github.com/IBM/ibmcloud-storage-volume-lib/lib/provider"
util "github.com/IBM/ibmcloud-storage-volume-lib/lib/utils"
"github.com/IBM/ibmcloud-storage-volume-lib/provider/local"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/auth"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/iam"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/messages"
userError "github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/messages"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/riaas"
"go.uber.org/zap"
"net/http"
"os"
"time"
)
const (
// displayName ...
displayName = "IBM Cloud container service"
// vpcProviderDisplayName ...
vpcProviderDisplayName = "IBM Cloud infrastructure"
// vpcExceptionPrefix ...
vpcExceptionPrefix = "IBM Cloud infrastructure exception"
// timeoutDefault ...
timeoutDefault = "120s"
)
// VPCBlockProvider implements provider.Provider
type VPCBlockProvider struct {
timeout time.Duration
serverConfig *config.ServerConfig
config *config.VPCProviderConfig
tokenGenerator *tokenGenerator
contextCF local.ContextCredentialsFactory
ClientProvider riaas.RegionalAPIClientProvider
httpClient *http.Client
APIConfig riaas.Config
}
var _ local.Provider = &VPCBlockProvider{}
// NewProvider initialises an instance of an IaaS provider.
func NewProvider(conf *config.Config, logger *zap.Logger) (local.Provider, error) {
logger.Info("Entering NewProvider")
if conf.Bluemix == nil || conf.VPC == nil {
return nil, errors.New("Incomplete config for VPCBlockProvider")
}
// VPC provider use differnt APIkey and Auth Endpoint
authConfig := &config.BluemixConfig{
IamURL: conf.VPC.TokenExchangeURL,
IamAPIKey: conf.VPC.APIKey,
IamClientID: conf.Bluemix.IamClientID,
IamClientSecret: conf.Bluemix.IamClientSecret,
}
contextCF, err := auth.NewContextCredentialsFactory(authConfig, nil, conf.VPC)
if err != nil {
return nil, err
}
timeoutString := conf.VPC.VPCTimeout
if timeoutString == "" || timeoutString == "0s" {
logger.Info("Using VPC default timeout")
timeoutString = "120s"
}
timeout, err := time.ParseDuration(timeoutString)
if err != nil {
return nil, err
}
httpClient, err := config.GeneralCAHttpClientWithTimeout(timeout)
if err != nil {
logger.Error("Failed to prepare HTTP client", util.ZapError(err))
return nil, err
}
// SetRetryParameters sets the retry logic parameters
SetRetryParameters(conf.VPC.MaxRetryAttempt, conf.VPC.MaxRetryGap)
provider := &VPCBlockProvider{
timeout: timeout,
serverConfig: conf.Server,
config: conf.VPC,
tokenGenerator: &tokenGenerator{config: conf.VPC},
contextCF: contextCF,
httpClient: httpClient,
APIConfig: riaas.Config{
BaseURL: conf.VPC.EndpointURL,
HTTPClient: httpClient,
APIVersion: conf.VPC.APIVersion,
},
}
// Update VPC config for IKS deployment
provider.config.IsIKS = conf.IKS != nil && conf.IKS.Enabled
logger.Info("", zap.Reflect("Provider config", provider.config))
userError.MessagesEn = messages.InitMessages()
return provider, nil
}
// ContextCredentialsFactory ...
func (vpcp *VPCBlockProvider) ContextCredentialsFactory(zone *string) (local.ContextCredentialsFactory, error) {
// Datacenter hint not required by VPC provider implementation
return vpcp.contextCF, nil
}
// OpenSession opens a session on the provider
func (vpcp *VPCBlockProvider) OpenSession(ctx context.Context, contextCredentials provider.ContextCredentials, ctxLogger *zap.Logger) (provider.Session, error) {
ctxLogger.Info("Entering OpenSession")
defer func() {
ctxLogger.Debug("Exiting OpenSession")
}()
// validate that we have what we need - i.e. valid credentials
if contextCredentials.Credential == "" {
return nil, util.NewError("Error Insufficient Authentication", "No authentication credential provided")
}
if vpcp.serverConfig.DebugTrace {
vpcp.APIConfig.DebugWriter = os.Stdout
}
if vpcp.ClientProvider == nil {
vpcp.ClientProvider = riaas.DefaultRegionalAPIClientProvider{}
}
ctxLogger.Debug("", zap.Reflect("apiConfig.BaseURL", vpcp.APIConfig.BaseURL))
if ctx != nil && ctx.Value(provider.RequestID) != nil {
// set ContextID only of speicifed in the context
vpcp.APIConfig.ContextID = fmt.Sprintf("%v", ctx.Value(provider.RequestID))
ctxLogger.Info("", zap.Reflect("apiConfig.ContextID", vpcp.APIConfig.ContextID))
}
client, err := vpcp.ClientProvider.New(vpcp.APIConfig)
if err != nil {
return nil, err
}
// Create a token for all other API calls
token, err := getAccessToken(contextCredentials, ctxLogger)
if err != nil {
return nil, err
}
ctxLogger.Debug("", zap.Reflect("Token", token.Token))
err = client.Login(token.Token)
if err != nil {
return nil, err
}
// Update retry logic default values
if vpcp.config.MaxRetryAttempt > 0 {
ctxLogger.Debug("", zap.Reflect("MaxRetryAttempt", vpcp.config.MaxRetryAttempt))
maxRetryAttempt = vpcp.config.MaxRetryAttempt
}
if vpcp.config.MaxRetryGap > 0 {
ctxLogger.Debug("", zap.Reflect("MaxRetryGap", vpcp.config.MaxRetryGap))
maxRetryGap = vpcp.config.MaxRetryGap
}
vpcSession := &VPCSession{
VPCAccountID: contextCredentials.IAMAccountID,
Config: vpcp.config,
ContextCredentials: contextCredentials,
VolumeType: "vpc-block",
Provider: VPC,
Apiclient: client,
APIClientVolAttachMgr: client.VolumeAttachService(),
Logger: ctxLogger,
APIRetry: NewFlexyRetryDefault(),
}
return vpcSession, nil
}
// getAccessToken ...
func getAccessToken(creds provider.ContextCredentials, logger *zap.Logger) (token *iam.AccessToken, err error) {
switch creds.AuthType {
case provider.IAMAccessToken:
token = &iam.AccessToken{Token: creds.Credential}
default:
err = errors.New("Unknown AuthType")
}
return
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package provider
import (
"github.com/IBM/ibmcloud-storage-volume-lib/config"
"github.com/IBM/ibmcloud-storage-volume-lib/lib/provider"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/instances"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/riaas"
"go.uber.org/zap"
)
// VPCSession implements lib.Session
type VPCSession struct {
VPCAccountID string
Config *config.VPCProviderConfig
ContextCredentials provider.ContextCredentials
VolumeType provider.VolumeType
Provider provider.VolumeProvider
Apiclient riaas.RegionalAPI
APIClientVolAttachMgr instances.VolumeAttachManager
APIVersion string
Logger *zap.Logger
APIRetry FlexyRetry
}
const (
// VPC storage provider
VPC = provider.VolumeProvider("VPC")
// VolumeType ...
VolumeType = provider.VolumeType("vpc-block")
// SnapshotMask ...
SnapshotMask = "id,username,capacityGb,createDate,snapshotCapacityGb,parentVolume[snapshotSizeBytes],parentVolume[snapshotCapacityGb],parentVolume[id],parentVolume[storageTierLevel],parentVolume[notes],storageType[keyName],serviceResource[datacenter[name]],billingItem[location,hourlyFlag],provisionedIops,lunId,originalVolumeName,storageTierLevel,notes"
)
var (
// DeleteVolumeReason ...
DeleteVolumeReason = "deleted by ibm-volume-lib on behalf of user request"
)
// Close at present does nothing
func (*VPCSession) Close() {
// Do nothing for now
}
// GetProviderDisplayName returns the name of the VPC provider
func (vpcs *VPCSession) GetProviderDisplayName() provider.VolumeProvider {
return VPC
}
// ProviderName ...
func (vpcs *VPCSession) ProviderName() provider.VolumeProvider {
return VPC
}
// Type ...
func (vpcs *VPCSession) Type() provider.VolumeType {
return VolumeType
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package provider
import (
"crypto/rsa"
"errors"
"github.com/dgrijalva/jwt-go"
"go.uber.org/zap"
"io/ioutil"
"path/filepath"
"time"
"github.com/IBM/ibmcloud-storage-volume-lib/config"
"github.com/IBM/ibmcloud-storage-volume-lib/lib/provider"
"github.com/IBM/ibmcloud-storage-volume-lib/provider/local"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/auth"
)
// tokenGenerator ...
type tokenGenerator struct {
config *config.VPCProviderConfig
tokenKID string
tokenTTL time.Duration
tokenBeforeTime time.Duration
privateKey *rsa.PrivateKey // Secret. Do not export
}
// readConfig ...
func (tg *tokenGenerator) readConfig(logger zap.Logger) (err error) {
logger.Info("Entering readConfig")
defer func() {
logger.Info("Exiting readConfig", zap.Duration("tokenTTL", tg.tokenTTL), zap.Duration("tokenBeforeTime", tg.tokenBeforeTime), zap.String("tokenKID", tg.tokenKID), local.ZapError(err))
}()
if tg.privateKey != nil {
return
}
path := filepath.Join(config.GetEtcPath(), tg.tokenKID)
pem, err := ioutil.ReadFile(path)
if err != nil {
logger.Error("Error reading PEM", local.ZapError(err))
return
}
privateKey, err := jwt.ParseRSAPrivateKeyFromPEM(pem)
if err != nil {
logger.Error("Error parsing PEM", local.ZapError(err))
return
}
tg.privateKey = privateKey
return
}
// buildToken ...
func (tg *tokenGenerator) buildToken(contextCredentials provider.ContextCredentials, ts time.Time, logger zap.Logger) (token *jwt.Token, err error) {
logger.Info("Entering getJWTToken", zap.Reflect("contextCredentials", contextCredentials))
defer func() {
logger.Info("Exiting getJWTToken", zap.Reflect("token", token), local.ZapError(err))
}()
err = tg.readConfig(logger)
if err != nil {
return
}
claims := jwt.MapClaims{
"iss": "armada",
"exp": ts.Add(tg.tokenTTL).Unix(),
"nbf": ts.Add(tg.tokenBeforeTime).Unix(),
"iat": ts.Unix(),
}
switch {
case contextCredentials.UserID == "":
errStr := "User ID is not configured"
logger.Error(errStr)
err = errors.New(errStr)
return
case contextCredentials.AuthType == auth.IMSToken:
claims["ims_user_id"] = contextCredentials.UserID
default:
claims["ims_username"] = contextCredentials.UserID
}
token = jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
token.Header["kid"] = tg.tokenKID
return
}
// getServiceToken ...
func (tg *tokenGenerator) getServiceToken(contextCredentials provider.ContextCredentials, logger zap.Logger) (signedToken *string, err error) {
token, err := tg.buildToken(contextCredentials, time.Now(), logger)
if err != nil {
return
}
signedString, err := token.SignedString(tg.privateKey)
if err != nil {
return
}
signedToken = &signedString
return
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package provider
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/provider"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/models"
"go.uber.org/zap"
"strconv"
"strings"
"time"
)
// maxRetryAttempt ...
var maxRetryAttempt = 10
// maxRetryGap ...
var maxRetryGap = 60
// retryGap ...
var retryGap = 10
var volumeIDPartsCount = 5
var skipErrorCodes = map[string]bool{
"validation_invalid_name": true,
"volume_capacity_max": true,
"volume_id_invalid": true,
"volume_profile_iops_invalid": true,
"volume_capacity_zero_or_negative": true,
"not_found": true,
"volume_name_not_found": true,
"internal_error": false,
"invalid_route": false,
// IKS ms error code for skip re-try
"ST0008": true, //resources not found
"ST0005": true, //worker node could not be found
}
// retry ...
func retry(logger *zap.Logger, retryfunc func() error) error {
var err error
for i := 0; i < maxRetryAttempt; i++ {
if i > 0 {
time.Sleep(time.Duration(retryGap) * time.Second)
}
err = retryfunc()
if err != nil {
//Skip retry for the below type of Errors
modelError, ok := err.(*models.Error)
if !ok {
continue
}
if skipRetry(modelError) {
break
}
if i >= 1 {
retryGap = 2 * retryGap
if retryGap > maxRetryGap {
retryGap = maxRetryGap
}
}
if (i + 1) < maxRetryAttempt {
logger.Info("Error while executing the function. Re-attempting execution ..", zap.Int("attempt..", i+2), zap.Int("retry-gap", retryGap), zap.Int("max-retry-Attempts", maxRetryGap), zap.Error(err))
}
continue
}
return err
}
return err
}
// skipRetry skip retry as per listed error codes
func skipRetry(err *models.Error) bool {
for _, errorItem := range err.Errors {
skipStatus, ok := skipErrorCodes[string(errorItem.Code)]
if ok {
return skipStatus
}
}
return false
}
// skipRetryForIKS skip retry as per listed error codes
func skipRetryForIKS(err *models.IksError) bool {
skipStatus, ok := skipErrorCodes[string(err.Code)]
if ok {
return skipStatus
}
return false
}
// skipRetryForAttach skip retry as per listed error codes
func skipRetryForAttach(err error, isIKS bool) bool {
// Only for storage-api ms related calls error
if isIKS {
iksError, ok := err.(*models.IksError)
if ok {
return skipRetryForIKS(iksError)
}
return false
}
// Only for RIaaS attachment related calls error
riaasError, ok := err.(*models.Error)
if ok {
return skipRetry(riaasError)
}
return false
}
// FlexyRetry ...
type FlexyRetry struct {
maxRetryAttempt int
maxRetryGap int
}
// NewFlexyRetryDefault ...
func NewFlexyRetryDefault() FlexyRetry {
return FlexyRetry{
// Default values as we configuration
maxRetryAttempt: maxRetryAttempt,
maxRetryGap: maxRetryGap,
}
}
// NewFlexyRetry ...
func NewFlexyRetry(maxRtyAtmpt int, maxrRtyGap int) FlexyRetry {
return FlexyRetry{
maxRetryAttempt: maxRtyAtmpt,
maxRetryGap: maxrRtyGap,
}
}
// FlexyRetry ...
func (fRetry *FlexyRetry) FlexyRetry(logger *zap.Logger, funcToRetry func() (error, bool)) error {
var err error
var stopRetry bool
for i := 0; i < fRetry.maxRetryAttempt; i++ {
if i > 0 {
time.Sleep(time.Duration(retryGap) * time.Second)
}
// Call function which required retry, retry is decided by funtion itself
err, stopRetry = funcToRetry()
if stopRetry {
break
}
// Update retry gap as per exponentioal
if i >= 1 {
retryGap = 2 * retryGap
if retryGap > fRetry.maxRetryGap {
retryGap = fRetry.maxRetryGap
}
}
if (i + 1) < fRetry.maxRetryAttempt {
logger.Info("UNEXPECTED RESULT, Re-attempting execution ..", zap.Int("attempt..", i+2),
zap.Int("retry-gap", retryGap), zap.Int("max-retry-Attempts", fRetry.maxRetryAttempt),
zap.Bool("stopRetry", stopRetry), zap.Error(err))
}
}
return err
}
// FlexyRetryWithConstGap ...
func (fRetry *FlexyRetry) FlexyRetryWithConstGap(logger *zap.Logger, funcToRetry func() (error, bool)) error {
var err error
var stopRetry bool
// lets have more number of try for wait for attach and detach specially
totalAttempt := fRetry.maxRetryAttempt * 4 // 40 time as per default values i.e 400 seconds
for i := 0; i < totalAttempt; i++ {
if i > 0 {
time.Sleep(time.Duration(retryGap) * time.Second)
}
// Call function which required retry, retry is decided by funtion itself
err, stopRetry = funcToRetry()
if stopRetry {
break
}
if (i + 1) < totalAttempt {
logger.Info("UNEXPECTED RESULT from FlexyRetryWithConstGap, Re-attempting execution ..", zap.Int("attempt..", i+2),
zap.Int("retry-gap", retryGap), zap.Int("max-retry-Attempts", totalAttempt),
zap.Bool("stopRetry", stopRetry), zap.Error(err))
}
}
return err
}
// ToInt ...
func ToInt(valueInInt string) int {
value, err := strconv.Atoi(valueInInt)
if err != nil {
return 0
}
return value
}
// ToInt64 ...
func ToInt64(valueInInt string) int64 {
value, err := strconv.ParseInt(valueInInt, 10, 64)
if err != nil {
return 0
}
return value
}
// FromProviderToLibVolume converting vpc provider volume type to generic lib volume type
func FromProviderToLibVolume(vpcVolume *models.Volume, logger *zap.Logger) (libVolume *provider.Volume) {
logger.Debug("Entry of FromProviderToLibVolume method...")
defer logger.Debug("Exit from FromProviderToLibVolume method...")
if vpcVolume == nil {
logger.Info("Volume details are empty")
return
}
if vpcVolume.Zone == nil {
logger.Info("Volume zone is empty")
return
}
logger.Debug("Volume details of VPC client", zap.Reflect("models.Volume", vpcVolume))
volumeCap := int(vpcVolume.Capacity)
iops := strconv.Itoa(int(vpcVolume.Iops))
var createdDate time.Time
if vpcVolume.CreatedAt != nil {
createdDate = *vpcVolume.CreatedAt
}
libVolume = &provider.Volume{
VolumeID: vpcVolume.ID,
Provider: VPC,
Capacity: &volumeCap,
Iops: &iops,
VolumeType: VolumeType,
CreationTime: createdDate,
}
if vpcVolume.Zone != nil {
libVolume.Region = vpcVolume.Zone.Name
}
return
}
// IsValidVolumeIDFormat validating
func IsValidVolumeIDFormat(volID string) bool {
parts := strings.Split(volID, "-")
if len(parts) != volumeIDPartsCount {
return false
}
return true
}
// SetRetryParameters sets the retry logic parameters
func SetRetryParameters(maxAttempts int, maxGap int) {
if maxAttempts > 0 {
maxRetryAttempt = maxAttempts
}
if maxGap > 0 {
maxRetryGap = maxGap
}
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package provider
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/provider"
userError "github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/messages"
"go.uber.org/zap"
)
// WaitForAttachVolume waits for volume to be attached to node. e.g waits till status becomes attached
func (vpcs *VPCSession) WaitForAttachVolume(volumeAttachmentTemplate provider.VolumeAttachmentRequest) (*provider.VolumeAttachmentResponse, error) {
vpcs.Logger.Debug("Entry of WaitForAttachVolume method...")
defer vpcs.Logger.Debug("Exit from WaitForAttachVolume method...")
vpcs.Logger.Info("Validating basic inputs for WaitForAttachVolume method...", zap.Reflect("volumeAttachmentTemplate", volumeAttachmentTemplate))
err := vpcs.validateAttachVolumeRequest(volumeAttachmentTemplate)
if err != nil {
return nil, err
}
var currentVolAttachment *provider.VolumeAttachmentResponse
err = vpcs.APIRetry.FlexyRetryWithConstGap(vpcs.Logger, func() (error, bool) {
currentVolAttachment, err = vpcs.GetVolumeAttachment(volumeAttachmentTemplate)
if err != nil {
// Need to stop retry as there is an error while getting attachment
// considering that vpcs.GetVolumeAttachment already re-tried
return err, true
}
// Stop retry in case of volume is attached
return err, currentVolAttachment != nil && currentVolAttachment.Status == StatusAttached
})
// Success case, checks are required in case of timeout happened and volume is still not attached state
if err == nil && (currentVolAttachment != nil && currentVolAttachment.Status == StatusAttached) {
return currentVolAttachment, nil
}
userErr := userError.GetUserError(string(userError.VolumeAttachTimedOut), nil, volumeAttachmentTemplate.VolumeID, volumeAttachmentTemplate.InstanceID)
vpcs.Logger.Info("Wait for attach timed out", zap.Error(userErr))
return nil, userErr
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package provider
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/provider"
util "github.com/IBM/ibmcloud-storage-volume-lib/lib/utils"
userError "github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/messages"
"go.uber.org/zap"
)
// WaitForDetachVolume waits for volume to be detached from node. e.g waits till no volume attachment is found
func (vpcs *VPCSession) WaitForDetachVolume(volumeAttachmentTemplate provider.VolumeAttachmentRequest) error {
vpcs.Logger.Debug("Entry of WaitForDetachVolume method...")
defer vpcs.Logger.Debug("Exit from WaitForDetachVolume method...")
var err error
vpcs.Logger.Info("Validating basic inputs for WaitForDetachVolume method...", zap.Reflect("volumeAttachmentTemplate", volumeAttachmentTemplate))
err = vpcs.validateAttachVolumeRequest(volumeAttachmentTemplate)
if err != nil {
return err
}
err = vpcs.APIRetry.FlexyRetryWithConstGap(vpcs.Logger, func() (error, bool) {
_, err := vpcs.GetVolumeAttachment(volumeAttachmentTemplate)
// In case of error we should not retry as there are two conditions for error
// 1- some issues at endpoint side --> Which is already covered in vpcs.GetVolumeAttachment
// 2- Attachment not found i.e err != nil --> in this case we should not re-try as it has been deleted
if err != nil {
return err, true
}
return err, false
})
// Could be a success case
if err != nil {
if errMsg, ok := err.(util.Message); ok {
if errMsg.Code == userError.VolumeAttachFindFailed {
vpcs.Logger.Info("Volume detachment is complete")
return nil
}
}
}
userErr := userError.GetUserError(string(userError.VolumeDetachTimedOut), err, volumeAttachmentTemplate.VolumeID, volumeAttachmentTemplate.InstanceID)
vpcs.Logger.Info("Wait for detach timed out", zap.Error(userErr))
return userErr
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package provider
import (
userError "github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/messages"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/models"
"go.uber.org/zap"
)
const (
validVolumeStatus = "available"
)
// WaitForValidVolumeState checks the volume for valid status
func WaitForValidVolumeState(vpcs *VPCSession, volumeID string) (err error) {
vpcs.Logger.Debug("Entry of WaitForValidVolumeState method...")
defer vpcs.Logger.Debug("Exit from WaitForValidVolumeState method...")
vpcs.Logger.Info("Getting volume details from VPC provider...", zap.Reflect("VolumeID", volumeID))
var volume *models.Volume
err = retry(vpcs.Logger, func() error {
volume, err = vpcs.Apiclient.VolumeService().GetVolume(volumeID, vpcs.Logger)
if err != nil {
return err
}
vpcs.Logger.Info("Getting volume details from VPC provider...", zap.Reflect("volume", volume))
if volume != nil && volume.Status == validVolumeStatus {
vpcs.Logger.Info("Volume got valid (available) state", zap.Reflect("VolumeDetails", volume))
return nil
}
return userError.GetUserError("VolumeNotInValidState", err, volumeID)
})
if err != nil {
vpcs.Logger.Info("Volume could not get valid (available) state", zap.Reflect("VolumeDetails", volume))
return userError.GetUserError("VolumeNotInValidState", err, volumeID)
}
return nil
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package client
import (
"errors"
)
// ErrAuthenticationRequired is returned if a request is made before an authentication
// token has been provided to the client
var ErrAuthenticationRequired = errors.New("Authentication token required")
type authenticationHandler struct {
authToken string
resourceGroup string
}
// Before is called before each request
func (a *authenticationHandler) Before(request *Request) error {
request.resourceGroup = a.resourceGroup
if a.authToken == "" {
return ErrAuthenticationRequired
}
request.headers.Set("Authorization", "Bearer "+a.authToken)
return nil
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package client
import (
"context"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/models"
"io"
"net/http"
"net/url"
)
// handler ...
type handler interface {
Before(request *Request) error
}
// SessionClient provides an interface for a REST API client
// go:generate counterfeiter -o fakes/client.go --fake-name SessionClient . SessionClient
type SessionClient interface {
NewRequest(operation *Operation) *Request
WithDebug(writer io.Writer) SessionClient
WithAuthToken(authToken string) SessionClient
WithPathParameter(name, value string) SessionClient
WithQueryValue(name, value string) SessionClient
}
type client struct {
baseURL string
httpClient *http.Client
pathParams Params
queryValues url.Values
authenHandler handler
debugWriter io.Writer
resourceGroup string
contextID string
context context.Context
}
// New creates a new instance of a SessionClient
func New(ctx context.Context, baseURL string, httpClient *http.Client, contextID string, APIVersion string) SessionClient {
// Default API version
backendAPIVersion := models.APIVersion
// Overwrite if the version is passed
if len(APIVersion) > 0 {
backendAPIVersion = APIVersion
}
return &client{
baseURL: baseURL,
httpClient: httpClient,
pathParams: Params{},
queryValues: url.Values{"version": []string{backendAPIVersion}},
authenHandler: &authenticationHandler{},
contextID: contextID,
context: ctx,
}
}
// NewRequest creates a request and configures it with the supplied operation
func (c *client) NewRequest(operation *Operation) *Request {
headers := http.Header{}
headers.Set("Accept", "application/json")
headers.Set("User-Agent", models.UserAgent)
if c.contextID != "" {
headers.Set("X-Request-ID", c.contextID)
headers.Set("X-Transaction-ID", c.contextID) // To avoid IKS cloudflare overriding X-Request-ID
}
// Copy the query values to a new map
qv := url.Values{}
for k, v := range c.queryValues {
qv[k] = v
}
return &Request{
httpClient: c.httpClient,
context: c.context,
baseURL: c.baseURL,
operation: operation,
pathParams: c.pathParams.Copy(),
authenHandler: c.authenHandler,
headers: headers,
debugWriter: c.debugWriter,
resourceGroup: c.resourceGroup,
queryValues: qv,
}
}
// WithDebug enables debug for this SessionClient, outputting to the supplied writer
func (c *client) WithDebug(writer io.Writer) SessionClient {
c.debugWriter = writer
return c
}
// WithAuthToken supplies the authentication token to use for all requests made by this session
func (c *client) WithAuthToken(authToken string) SessionClient {
c.authenHandler = &authenticationHandler{
authToken: authToken,
}
return c
}
// WithPathParameter adds a path parameter to the request
func (c *client) WithPathParameter(name, value string) SessionClient {
c.pathParams[name] = value
return c
}
// WithQueryValue adds a query parameter to the request
func (c *client) WithQueryValue(name, value string) SessionClient {
c.queryValues.Add(name, value)
return c
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package client
// Params ...
type Params map[string]string
// Copy performs a shallow copy of a Params object
func (p Params) Copy() Params {
params := Params{}
for k, v := range p {
params[k] = v
}
return params
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package client
import (
"context"
"fmt"
"io"
"net/http"
"net/http/httputil"
"net/url"
"reflect"
"regexp"
"strings"
"time"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/client/payload"
"github.com/fatih/structs"
)
// Operation defines the API operation to be invoked
type Operation struct {
Name string
Method string
PathPattern string
}
// Request defines the properties of an API request. It can then be invoked to
// call the underlying API specified by the supplied operation
type Request struct {
httpClient *http.Client
baseURL string
authenHandler handler
context context.Context
operation *Operation
pathParams Params
headers http.Header
debugWriter io.Writer
queryValues url.Values
bodyProvider BodyProvider
successConsumer ResponseConsumer
errorConsumer ResponseConsumer
resourceGroup string
}
// BodyProvider declares an interface that describes an HTTP body, for
// both request and response
type BodyProvider interface {
ContentType() string
Body() (io.Reader, error)
}
// ResponseConsumer ...
type ResponseConsumer interface {
Consume(io.Reader) error
Receiver() interface{}
}
func (r *Request) path() string {
path := r.operation.PathPattern
for k, v := range r.pathParams {
path = strings.Replace(path, "{"+k+"}", v, -1)
}
return path
}
// URL constructs the full URL for a request
func (r *Request) URL() string {
baseURL, baseErr := url.Parse(r.baseURL)
if baseErr != nil {
return ""
}
if !strings.HasSuffix(baseURL.Path, "/") {
baseURL.Path += "/"
}
pathURL, pathErr := url.Parse(r.path())
if pathErr != nil {
return ""
}
resolvedURL := baseURL.ResolveReference(pathURL)
resolvedURL.RawQuery = r.queryValues.Encode()
return resolvedURL.String()
}
// PathParameter sets a path parameter to be resolved on invocation of a request
func (r *Request) PathParameter(name, value string) *Request {
r.pathParams[name] = value
return r
}
// AddQueryValue ...
func (r *Request) AddQueryValue(key, value string) *Request {
if r.queryValues == nil {
r.queryValues = url.Values{}
}
r.queryValues.Add(key, value)
return r
}
// JSONBody converts the supplied argument to JSON to use as the body of a request
func (r *Request) JSONBody(p interface{}) *Request {
if r.operation.Method == http.MethodPost && reflect.ValueOf(p).Kind() == reflect.Struct {
structs.DefaultTagName = "json"
m := structs.Map(p)
if r.resourceGroup != "" {
m["resourceGroup"] = r.resourceGroup
}
r.bodyProvider = payload.NewJSONBodyProvider(m)
} else {
r.bodyProvider = payload.NewJSONBodyProvider(p)
}
return r
}
// MultipartFileBody configures the POST payload to be sent in multi-part format. The
// content is read from the supplied Reader.
func (r *Request) MultipartFileBody(name string, contents io.Reader) *Request {
r.bodyProvider = payload.NewMultipartFileBody(name, contents)
return r
}
// JSONSuccess configures the receiver to use to process a JSON response
// for a successful (2xx) response
func (r *Request) JSONSuccess(receiver interface{}) *Request {
r.successConsumer = payload.NewJSONConsumer(receiver)
return r
}
// JSONError configures the error to populate in the event of an unsuccessful
// (non-2xx) response
func (r *Request) JSONError(receiver error) *Request {
r.errorConsumer = payload.NewJSONConsumer(receiver)
return r
}
// Invoke performs the request, and populates the response or error as appropriate
func (r *Request) Invoke() (*http.Response, error) {
err := r.authenHandler.Before(r)
if err != nil {
return nil, err
}
var body io.Reader
if r.bodyProvider != nil {
body, err = r.bodyProvider.Body()
if err != nil {
return nil, err
}
if contentType := r.bodyProvider.ContentType(); contentType != "" {
r.headers.Set("Content-Type", contentType)
}
}
httpRequest, err := http.NewRequest(r.operation.Method, r.URL(), body)
if err != nil {
return nil, err
}
for k, v := range r.headers {
httpRequest.Header[k] = v
}
r.debugRequest(httpRequest)
resp, err := r.httpClient.Do(httpRequest.WithContext(r.context))
if err != nil {
return nil, err
}
defer resp.Body.Close()
r.debugResponse(resp)
switch {
case resp.StatusCode == http.StatusNoContent:
break
case resp.StatusCode >= 200 && resp.StatusCode <= 299:
if r.successConsumer != nil {
err = r.successConsumer.Consume(resp.Body)
}
default:
if r.errorConsumer != nil {
err = r.errorConsumer.Consume(resp.Body)
if err == nil {
err = r.errorConsumer.Receiver().(error)
}
}
}
return resp, err
}
func (r *Request) debugRequest(req *http.Request) {
if r.debugWriter == nil {
return
}
multipart := strings.Contains(req.Header.Get("Content-Type"), "multipart/form-data")
dumpedRequest, err := httputil.DumpRequest(req, !multipart)
if err != nil {
r.debugf("Error dumping request\n%s\n", err)
return
}
r.debugf("\nREQUEST: [%s]\n%s\n", time.Now().Format(time.RFC3339), sanitize(dumpedRequest))
if multipart {
r.debugf("[MULTIPART/FORM-DATA CONTENT HIDDEN]\n")
}
}
func (r *Request) debugResponse(resp *http.Response) {
if r.debugWriter == nil {
return
}
dumpedResponse, err := httputil.DumpResponse(resp, true)
if err != nil {
fmt.Fprintf(r.debugWriter, "Error dumping response\n%s\n", err)
return
}
r.debugf("\nRESPONSE: [%s]\n%s\n", time.Now().Format(time.RFC3339), sanitize(dumpedResponse))
}
func (r *Request) debugf(format string, args ...interface{}) {
fmt.Fprintf(r.debugWriter, format, args...)
}
// RedactedFillin used as a replacement string in debug logs for sensitive data
const RedactedFillin = "[REDACTED]"
func sanitize(input []byte) string {
sanitized := string(input)
re := regexp.MustCompile(`(?mi)^Authorization: .*`)
sanitized = re.ReplaceAllString(sanitized, "Authorization: "+RedactedFillin)
re = regexp.MustCompile(`(?mi)^X-Auth-Token: .*`)
sanitized = re.ReplaceAllString(sanitized, "X-Auth-Token: "+RedactedFillin)
re = regexp.MustCompile(`(?mi)^APIKey: .*`)
sanitized = re.ReplaceAllString(sanitized, "APIKey: "+RedactedFillin)
sanitized = sanitizeJSON("key", sanitized)
sanitized = sanitizeJSON("password", sanitized)
sanitized = sanitizeJSON("passphrase", sanitized)
return sanitized
}
func sanitizeJSON(propertySubstring string, json string) string {
regex := regexp.MustCompile(fmt.Sprintf(`(?i)"([^"]*%s[^"]*)":\s*"[^\,]*"`, propertySubstring))
return regex.ReplaceAllString(json, fmt.Sprintf(`"$1":"%s"`, RedactedFillin))
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package instances
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/utils"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/client"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/models"
"go.uber.org/zap"
"time"
)
// AttachVolume attached volume to instances with givne volume attachment details
func (vs *VolumeAttachService) AttachVolume(volumeAttachmentTemplate *models.VolumeAttachment, ctxLogger *zap.Logger) (*models.VolumeAttachment, error) {
defer util.TimeTracker("AttachVolume", time.Now())
operation := &client.Operation{
Name: "AttachVolume",
Method: "POST",
PathPattern: vs.pathPrefix + instanceIDvolumeAttachmentPath,
}
var volumeAttachment models.VolumeAttachment
apiErr := vs.receiverError
request := vs.client.NewRequest(operation)
ctxLogger.Info("Equivalent curl command and payload details", zap.Reflect("URL", request.URL()), zap.Reflect("Payload", volumeAttachmentTemplate), zap.Reflect("Operation", operation), zap.Reflect("PathParameters", volumeAttachmentTemplate.InstanceID))
_, err := vs.populatePathPrefixParameters(request, volumeAttachmentTemplate).JSONBody(volumeAttachmentTemplate).JSONSuccess(&volumeAttachment).JSONError(apiErr).Invoke()
if err != nil {
return nil, err
}
return &volumeAttachment, nil
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package instances
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/utils"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/client"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/models"
"go.uber.org/zap"
"net/http"
"time"
)
// DetachVolume retrives the volume attach status with givne volume attachment details
func (vs *VolumeAttachService) DetachVolume(volumeAttachmentTemplate *models.VolumeAttachment, ctxLogger *zap.Logger) (*http.Response, error) {
defer util.TimeTracker("DetachVolume", time.Now())
operation := &client.Operation{
Name: "DetachVolume",
Method: "DELETE",
PathPattern: vs.pathPrefix + instanceIDattachmentIDPath,
}
apiErr := vs.receiverError
request := vs.client.NewRequest(operation)
ctxLogger.Info("Equivalent curl command details", zap.Reflect("URL", request.URL()), zap.Reflect("volumeAttachmentTemplate", volumeAttachmentTemplate), zap.Reflect("Operation", operation))
ctxLogger.Info("Pathparameters", zap.Reflect(instanceIDParam, volumeAttachmentTemplate.InstanceID), zap.Reflect(attachmentIDParam, volumeAttachmentTemplate.ID))
req := vs.populatePathPrefixParameters(request, volumeAttachmentTemplate)
req = request.PathParameter(attachmentIDParam, volumeAttachmentTemplate.ID)
resp, err := req.JSONError(apiErr).Invoke()
if err != nil {
ctxLogger.Error("Error occured while deleting volume attachment", zap.Error(err))
if resp != nil && resp.StatusCode == http.StatusNotFound {
// volume Attachment is deleted. So do not want to retry
return resp, apiErr
}
}
ctxLogger.Info("Successfuly deleted the volume attachment")
return resp, err
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package instances
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/utils"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/client"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/models"
"go.uber.org/zap"
"time"
)
// GetVolumeAttachment retrives the volume attach status with given volume attachment details
func (vs *VolumeAttachService) GetVolumeAttachment(volumeAttachmentTemplate *models.VolumeAttachment, ctxLogger *zap.Logger) (*models.VolumeAttachment, error) {
defer util.TimeTracker("DetachVolume", time.Now())
operation := &client.Operation{
Name: "GetVolumeAttachment",
Method: "GET",
PathPattern: vs.pathPrefix + instanceIDattachmentIDPath,
}
apiErr := vs.receiverError
var volumeAttachment models.VolumeAttachment
request := vs.client.NewRequest(operation)
ctxLogger.Info("Equivalent curl command details", zap.Reflect("URL", request.URL()), zap.Reflect("volumeAttachmentTemplate", volumeAttachmentTemplate), zap.Reflect("Operation", operation))
ctxLogger.Info("Pathparameters", zap.Reflect(instanceIDParam, volumeAttachmentTemplate.InstanceID), zap.Reflect(attachmentIDParam, volumeAttachmentTemplate.ID))
req := vs.populatePathPrefixParameters(request, volumeAttachmentTemplate)
req = request.PathParameter(attachmentIDParam, volumeAttachmentTemplate.ID)
_, err := req.JSONSuccess(&volumeAttachment).JSONError(apiErr).Invoke()
if err != nil {
ctxLogger.Error("Error occured while getting volume attachment", zap.Error(err))
return nil, err
}
ctxLogger.Info("Successfuly retrieved the volume attachment", zap.Reflect("volumeAttachment", volumeAttachment))
return &volumeAttachment, err
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package instances
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/utils"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/client"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/models"
"go.uber.org/zap"
"time"
)
// ListVolumeAttachment retrives the list volume attachments with givne volume attachment details
func (vs *VolumeAttachService) ListVolumeAttachment(volumeAttachmentTemplate *models.VolumeAttachment, ctxLogger *zap.Logger) (*models.VolumeAttachmentList, error) {
defer util.TimeTracker("GetAttachStatus", time.Now())
operation := &client.Operation{
Name: "ListVolumeAttachment",
Method: "GET",
PathPattern: vs.pathPrefix + instanceIDvolumeAttachmentPath,
}
var volumeAttachmentList models.VolumeAttachmentList
apiErr := vs.receiverError
request := vs.client.NewRequest(operation)
ctxLogger.Info("Equivalent curl command details", zap.Reflect("URL", request.URL()), zap.Reflect("volumeAttachmentTemplate", volumeAttachmentTemplate), zap.Reflect("Operation", operation))
req := vs.populatePathPrefixParameters(request, volumeAttachmentTemplate)
_, err := req.JSONSuccess(&volumeAttachmentList).JSONError(apiErr).Invoke()
if err != nil {
ctxLogger.Error("Error occured while getting volume attachment", zap.Error(err))
return nil, err
}
return &volumeAttachmentList, nil
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package instances
import (
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/client"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/models"
"go.uber.org/zap"
"net/http"
)
const (
//VpcPathPrefix VPC URL path prefix
VpcPathPrefix = "v1/instances"
//IksPathPrefix IKS URL path prefix
IksPathPrefix = "v2/storage/clusters/{cluster-id}/workers"
)
// VolumeAttachManager operations
//go:generate counterfeiter -o fakes/volume_attach_service.go --fake-name VolumeAttachService . VolumeAttachManager
type VolumeAttachManager interface {
// Create the volume with authorisation by passing required information in the volume object
AttachVolume(*models.VolumeAttachment, *zap.Logger) (*models.VolumeAttachment, error)
// GetVolumeAttachment retrives the single VolumeAttachment based on the instance ID and attachmentID
GetVolumeAttachment(*models.VolumeAttachment, *zap.Logger) (*models.VolumeAttachment, error)
// ListVolumeAttachment retrives the VolumeAttachment list for given server
ListVolumeAttachment(*models.VolumeAttachment, *zap.Logger) (*models.VolumeAttachmentList, error)
// Delete the volume
DetachVolume(*models.VolumeAttachment, *zap.Logger) (*http.Response, error)
}
// VolumeAttachService ...
type VolumeAttachService struct {
client client.SessionClient
pathPrefix string
receiverError error
populatePathPrefixParameters func(request *client.Request, volumeAttachmentTemplate *models.VolumeAttachment) *client.Request
}
var _ VolumeAttachManager = &VolumeAttachService{}
// New ...
func New(clientIn client.SessionClient) VolumeAttachManager {
err := models.Error{}
return &VolumeAttachService{
client: clientIn,
pathPrefix: VpcPathPrefix,
receiverError: &err,
populatePathPrefixParameters: func(request *client.Request, volumeAttachmentTemplate *models.VolumeAttachment) *client.Request {
request.PathParameter(instanceIDParam, *volumeAttachmentTemplate.InstanceID)
return request
},
}
}
// IKSVolumeAttachService ...
type IKSVolumeAttachService struct {
VolumeAttachService
}
var _ VolumeAttachManager = &IKSVolumeAttachService{}
// NewIKSVolumeAttachmentManager ...
func NewIKSVolumeAttachmentManager(clientIn client.SessionClient) VolumeAttachManager {
err := models.IksError{}
return &IKSVolumeAttachService{
VolumeAttachService{
client: clientIn,
pathPrefix: IksPathPrefix,
receiverError: &err,
populatePathPrefixParameters: func(request *client.Request, volumeAttachmentTemplate *models.VolumeAttachment) *client.Request {
request.PathParameter(instanceIDParam, *volumeAttachmentTemplate.InstanceID)
request.PathParameter(clusterIDParam, *volumeAttachmentTemplate.ClusterID)
return request
},
},
}
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package riaas
import (
"context"
"io"
"net/http"
)
// Config for the Session
type Config struct {
BaseURL string
AccountID string
Username string
APIKey string
ResourceGroup string
Password string
ContextID string
DebugWriter io.Writer
HTTPClient *http.Client
Context context.Context
APIVersion string
}
func (c Config) httpClient() *http.Client {
if c.HTTPClient != nil {
return c.HTTPClient
}
return http.DefaultClient
}
func (c Config) baseURL() string {
return c.BaseURL
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package riaas
import (
"context"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/client"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/instances"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/vpcvolume"
)
// RegionalAPI is the main interface for the RIAAS API client. From here, service
// objects for the individual parts of the API can be obtained
//go:generate counterfeiter -o fakes/regional_api.go --fake-name RegionalAPI . RegionalAPI
type RegionalAPI interface {
Login(token string) error
VolumeService() vpcvolume.VolumeManager
VolumeAttachService() instances.VolumeAttachManager
IKSVolumeAttachService() instances.VolumeAttachManager
SnapshotService() vpcvolume.SnapshotManager
}
var _ RegionalAPI = &Session{}
// Session is a base implementation of the RegionalAPI interface
type Session struct {
client client.SessionClient
config Config
}
// New creates a new Session volume, using the supplied config
func New(config Config) (*Session, error) {
ctx := config.Context
if ctx == nil {
ctx = context.Background()
}
riaasClient := client.New(ctx, config.baseURL(), config.httpClient(), config.ContextID, config.APIVersion)
if config.DebugWriter != nil {
riaasClient.WithDebug(config.DebugWriter)
}
return &Session{
client: riaasClient,
config: config,
}, nil
}
// Login configures the session with the supplied Authentication token
// which is used for all requests to the API
func (s *Session) Login(token string) error {
s.client.WithAuthToken(token)
return nil
}
// VolumeService returns the Volume service for managing volumes
func (s *Session) VolumeService() vpcvolume.VolumeManager {
return vpcvolume.New(s.client)
}
// VolumeAttachService returns the VolumeAttachService for managing volumes
func (s *Session) VolumeAttachService() instances.VolumeAttachManager {
return instances.New(s.client)
}
// IKSVolumeAttachService returns the VolumeAttachService for managing volumes through IKS
func (s *Session) IKSVolumeAttachService() instances.VolumeAttachManager {
return instances.NewIKSVolumeAttachmentManager(s.client)
}
// SnapshotService returns the Snapshot service for managing snapshot
func (s *Session) SnapshotService() vpcvolume.SnapshotManager {
return vpcvolume.NewSnapshotManager(s.client)
}
// RegionalAPIClientProvider declares an interface for a provider that can supply a new
// RegionalAPI client session
//go:generate counterfeiter -o fakes/client_provider.go --fake-name RegionalAPIClientProvider . RegionalAPIClientProvider
type RegionalAPIClientProvider interface {
New(config Config) (RegionalAPI, error)
}
// DefaultRegionalAPIClientProvider declares a basic client provider that delegates to
// New(). Can be used for dependency injection.
type DefaultRegionalAPIClientProvider struct {
}
var _ RegionalAPIClientProvider = DefaultRegionalAPIClientProvider{}
// New creates a new Session volume, using the supplied config
func (d DefaultRegionalAPIClientProvider) New(config Config) (RegionalAPI, error) {
return New(config)
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package vpcvolume
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/utils"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/client"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/models"
"go.uber.org/zap"
"time"
)
// CheckSnapshotTag checks if the given tag exists on a snapshot
func (ss *SnapshotService) CheckSnapshotTag(volumeID string, snapshotID string, tagName string, ctxLogger *zap.Logger) error {
ctxLogger.Debug("Entry Backend CreateSnapshotTag")
defer ctxLogger.Debug("Exit Backend CreateSnapshotTag")
defer util.TimeTracker("CheckSnapshotTag", time.Now())
operation := &client.Operation{
Name: "CheckSnapshotTag",
Method: "GET",
PathPattern: snapshotTagNamePath,
}
var apiErr models.Error
request := ss.client.NewRequest(operation)
ctxLogger.Info("Equivalent curl command", zap.Reflect("URL", request.URL()))
req := request.PathParameter(volumeIDParam, volumeID).PathParameter(snapshotIDParam, snapshotID).PathParameter(snapshotTagParam, tagName).JSONError(&apiErr)
_, err := req.Invoke()
if err != nil {
return err
}
return nil
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package vpcvolume
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/utils"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/client"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/models"
"go.uber.org/zap"
"time"
)
// CheckVolumeTag checks if the given tag exists on a volume
func (vs *VolumeService) CheckVolumeTag(volumeID string, tagName string, ctxLogger *zap.Logger) error {
ctxLogger.Debug("Entry Backend CheckVolumeTag")
defer ctxLogger.Debug("Exit Backend CheckVolumeTag")
defer util.TimeTracker("CheckVolumeTag", time.Now())
operation := &client.Operation{
Name: "CheckVolumeTag",
Method: "GET",
PathPattern: volumeTagNamePath,
}
var apiErr models.Error
request := vs.client.NewRequest(operation)
ctxLogger.Info("Equivalent curl command", zap.Reflect("URL", request.URL()), zap.Reflect("Operation", operation))
req := request.PathParameter(volumeIDParam, volumeID).PathParameter(volumeTagParam, tagName).JSONError(&apiErr)
_, err := req.Invoke()
if err != nil {
return err
}
return nil
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package vpcvolume
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/utils"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/client"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/models"
"go.uber.org/zap"
"time"
)
// CreateSnapshot POSTs to /volumes
func (ss *SnapshotService) CreateSnapshot(volumeID string, snapshotTemplate *models.Snapshot, ctxLogger *zap.Logger) (*models.Snapshot, error) {
ctxLogger.Debug("Entry Backend CreateSpanShot")
defer ctxLogger.Debug("Exit Backend CreateSnapshot")
defer util.TimeTracker("CreateSnapshot", time.Now())
operation := &client.Operation{
Name: "CreateSnapshot",
Method: "POST",
PathPattern: snapshotsPath,
}
var snapshot models.Snapshot
var apiErr models.Error
request := ss.client.NewRequest(operation)
ctxLogger.Info("Equivalent curl command and payload details", zap.Reflect("URL", request.URL()), zap.Reflect("Payload", snapshotTemplate), zap.Reflect("Operation", operation))
_, err := request.PathParameter(volumeIDParam, volumeID).JSONBody(snapshotTemplate).JSONSuccess(&snapshot).JSONError(&apiErr).Invoke()
if err != nil {
return nil, err
}
return &snapshot, nil
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package vpcvolume
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/utils"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/client"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/models"
"go.uber.org/zap"
"time"
)
// CreateVolume POSTs to /volumes
func (vs *VolumeService) CreateVolume(volumeTemplate *models.Volume, ctxLogger *zap.Logger) (*models.Volume, error) {
ctxLogger.Debug("Entry Backend CreateVolume")
defer ctxLogger.Debug("Exit Backend CreateVolume")
defer util.TimeTracker("CreateVolume", time.Now())
operation := &client.Operation{
Name: "CreateVolume",
Method: "POST",
PathPattern: volumesPath,
}
var volume models.Volume
var apiErr models.Error
request := vs.client.NewRequest(operation)
ctxLogger.Info("Equivalent curl command and payload details", zap.Reflect("URL", request.URL()), zap.Reflect("Payload", volumeTemplate), zap.Reflect("Operation", operation))
_, err := request.JSONBody(volumeTemplate).JSONSuccess(&volume).JSONError(&apiErr).Invoke()
if err != nil {
return nil, err
}
return &volume, nil
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package vpcvolume
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/utils"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/client"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/models"
"go.uber.org/zap"
"time"
)
// DeleteSnapshot DELETEs to /volumes
func (ss *SnapshotService) DeleteSnapshot(volumeID string, snapshotID string, ctxLogger *zap.Logger) error {
ctxLogger.Debug("Entry Backend DeleteSpanshot")
defer ctxLogger.Debug("Exit Backend DeleteSnapshot")
defer util.TimeTracker("DeleteSnapshot", time.Now())
operation := &client.Operation{
Name: "DeleteSnapshot",
Method: "DELETE",
PathPattern: snapshotIDPath,
}
var apiErr models.Error
request := ss.client.NewRequest(operation)
ctxLogger.Info("Equivalent curl command", zap.Reflect("URL", request.URL()), zap.Reflect("Operation", operation))
_, err := request.PathParameter(volumeIDParam, volumeID).PathParameter(snapshotIDParam, snapshotID).JSONError(&apiErr).Invoke()
if err != nil {
return err
}
return nil
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package vpcvolume
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/utils"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/client"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/models"
"go.uber.org/zap"
"time"
)
// DeleteSnapshotTag deletes tag of a snapshot
func (ss *SnapshotService) DeleteSnapshotTag(volumeID string, snapshotID string, tagName string, ctxLogger *zap.Logger) error {
ctxLogger.Debug("Entry Backend DeleteSnapshotTag")
defer ctxLogger.Debug("Exit Backend DeleteSnapshotTag")
defer util.TimeTracker("DeleteSnapshotTag", time.Now())
operation := &client.Operation{
Name: "DeleteSnapshotTag",
Method: "DELETE",
PathPattern: snapshotTagNamePath,
}
var apiErr models.Error
request := ss.client.NewRequest(operation)
ctxLogger.Info("Equivalent curl command", zap.Reflect("URL", request.URL()), zap.Reflect("Operation", operation))
req := request.PathParameter(volumeIDParam, volumeID).PathParameter(snapshotIDParam, snapshotID).PathParameter(snapshotTagParam, tagName).JSONError(&apiErr)
_, err := req.Invoke()
if err != nil {
return err
}
return nil
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package vpcvolume
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/utils"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/client"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/models"
"go.uber.org/zap"
"time"
)
// DeleteVolume POSTs to /volumes
func (vs *VolumeService) DeleteVolume(volumeID string, ctxLogger *zap.Logger) error {
ctxLogger.Debug("Entry Backend DeleteVolume")
defer ctxLogger.Debug("Exit Backend DeleteVolume")
defer util.TimeTracker("DeleteVolume", time.Now())
operation := &client.Operation{
Name: "DeleteVolume",
Method: "DELETE",
PathPattern: volumeIDPath,
}
var apiErr models.Error
request := vs.client.NewRequest(operation)
ctxLogger.Info("Equivalent curl command", zap.Reflect("URL", request.URL()), zap.Reflect("Operation", operation))
_, err := request.PathParameter(volumeIDParam, volumeID).JSONError(&apiErr).Invoke()
if err != nil {
return err
}
return nil
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package vpcvolume
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/utils"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/client"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/models"
"go.uber.org/zap"
"time"
)
// DeleteVolumeTag deletes tag of a volume
func (vs *VolumeService) DeleteVolumeTag(volumeID string, tagName string, ctxLogger *zap.Logger) error {
ctxLogger.Debug("Entry Backend DeleteVolumeTag")
defer ctxLogger.Debug("Exit Backend DeleteVolumeTag")
defer util.TimeTracker("DeleteVolumeTag", time.Now())
operation := &client.Operation{
Name: "DeleteVolumeTag",
Method: "DELETE",
PathPattern: volumeTagNamePath,
}
var apiErr models.Error
request := vs.client.NewRequest(operation)
ctxLogger.Info("Equivalent curl command", zap.Reflect("URL", request.URL()), zap.Reflect("Operation", operation))
req := request.PathParameter(volumeIDParam, volumeID).PathParameter(volumeTagParam, tagName).JSONError(&apiErr)
_, err := req.Invoke()
if err != nil {
return err
}
return nil
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package vpcvolume
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/utils"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/client"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/models"
"go.uber.org/zap"
"time"
)
// GetSnapshot GETs from /volumes
func (ss *SnapshotService) GetSnapshot(volumeID string, snapshotID string, ctxLogger *zap.Logger) (*models.Snapshot, error) {
ctxLogger.Debug("Entry Backend GetSnapshot")
defer ctxLogger.Debug("Exit Backend GetSnapshot")
defer util.TimeTracker("GetSnapshot", time.Now())
operation := &client.Operation{
Name: "GetSnapshot",
Method: "GET",
PathPattern: snapshotIDPath,
}
var snapshot models.Snapshot
var apiErr models.Error
request := ss.client.NewRequest(operation)
ctxLogger.Info("Equivalent curl command", zap.Reflect("URL", request.URL()), zap.Reflect("Operation", operation))
req := request.PathParameter(volumeIDParam, volumeID).PathParameter(snapshotIDParam, snapshotID)
_, err := req.JSONSuccess(&snapshot).JSONError(&apiErr).Invoke()
if err != nil {
return nil, err
}
return &snapshot, nil
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package vpcvolume
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/utils"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/client"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/models"
"go.uber.org/zap"
"time"
)
// GetVolume POSTs to /volumes
func (vs *VolumeService) GetVolume(volumeID string, ctxLogger *zap.Logger) (*models.Volume, error) {
ctxLogger.Debug("Entry Backend GetVolume")
defer ctxLogger.Debug("Exit Backend GetVolume")
defer util.TimeTracker("GetVolume", time.Now())
operation := &client.Operation{
Name: "GetVolume",
Method: "GET",
PathPattern: volumeIDPath,
}
var volume models.Volume
var apiErr models.Error
request := vs.client.NewRequest(operation)
ctxLogger.Info("Equivalent curl command", zap.Reflect("URL", request.URL()), zap.Reflect("Operation", operation))
req := request.PathParameter(volumeIDParam, volumeID)
_, err := req.JSONSuccess(&volume).JSONError(&apiErr).Invoke()
if err != nil {
return nil, err
}
return &volume, nil
}
// GetVolumeByName GETs /volumes
func (vs *VolumeService) GetVolumeByName(volumeName string, ctxLogger *zap.Logger) (*models.Volume, error) {
ctxLogger.Debug("Entry Backend GetVolumeByName")
defer ctxLogger.Debug("Exit Backend GetVolumeByName")
defer util.TimeTracker("GetVolumeByName", time.Now())
// Get the volume details for a single volume, ListVolumeFilters will return only 1 volume in list
filters := &models.ListVolumeFilters{VolumeName: volumeName}
volumes, err := vs.ListVolumes(1, filters, ctxLogger)
if err != nil {
return nil, err
}
if volumes != nil {
volumeslist := volumes.Volumes
if volumeslist != nil && len(volumeslist) > 0 {
return volumeslist[0], nil
}
}
return nil, err
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package vpcvolume
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/utils"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/client"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/models"
"go.uber.org/zap"
"time"
)
// ListSnapshotTags GETs /volumes/snapshots/tags
func (ss *SnapshotService) ListSnapshotTags(volumeID string, snapshotID string, ctxLogger *zap.Logger) (*[]string, error) {
ctxLogger.Debug("Entry Backend ListSnapshotTags")
defer ctxLogger.Debug("Exit Backend ListSnapshotTags")
defer util.TimeTracker("ListSnapshotTags", time.Now())
operation := &client.Operation{
Name: "ListSnapshotTags",
Method: "GET",
PathPattern: snapshotTagsPath,
}
var tags []string
var apiErr models.Error
request := ss.client.NewRequest(operation)
ctxLogger.Info("Equivalent curl command", zap.Reflect("URL", request.URL()), zap.Reflect("Operation", operation))
req := request.PathParameter(volumeIDParam, volumeID).PathParameter(snapshotIDParam, snapshotID).JSONSuccess(&tags).JSONError(&apiErr)
_, err := req.Invoke()
if err != nil {
return nil, err
}
return &tags, nil
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package vpcvolume
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/utils"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/client"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/models"
"go.uber.org/zap"
"time"
)
// ListSnapshots GETs /volumes/snapshots
func (ss *SnapshotService) ListSnapshots(volumeID string, ctxLogger *zap.Logger) (*models.SnapshotList, error) {
ctxLogger.Debug("Entry Backend ListSnapshots")
defer ctxLogger.Debug("Exit Backend ListSnapshots")
defer util.TimeTracker("ListSnapshots", time.Now())
operation := &client.Operation{
Name: "ListSnapshots",
Method: "GET",
PathPattern: snapshotsPath,
}
var snapshots models.SnapshotList
var apiErr models.Error
request := ss.client.NewRequest(operation)
ctxLogger.Info("Equivalent curl command", zap.Reflect("URL", request.URL()), zap.Reflect("Operation", operation))
_, err := request.PathParameter(volumeIDParam, volumeID).JSONSuccess(&snapshots).JSONError(&apiErr).Invoke()
if err != nil {
return nil, err
}
return &snapshots, nil
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package vpcvolume
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/utils"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/client"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/models"
"go.uber.org/zap"
"time"
)
// ListVolumeTags GETs /volumes/tags
func (vs *VolumeService) ListVolumeTags(volumeID string, ctxLogger *zap.Logger) (*[]string, error) {
ctxLogger.Debug("Entry Backend ListVolumeTags")
defer ctxLogger.Debug("Exit Backend ListVolumeTags")
defer util.TimeTracker("ListVolumeTags", time.Now())
operation := &client.Operation{
Name: "ListVolumeTags",
Method: "GET",
PathPattern: volumeTagsPath,
}
var tags []string
var apiErr models.Error
request := vs.client.NewRequest(operation)
ctxLogger.Info("Equivalent curl command", zap.Reflect("URL", request.URL()), zap.Reflect("Operation", operation))
req := request.PathParameter(volumeIDParam, volumeID).JSONSuccess(&tags).JSONError(&apiErr)
_, err := req.Invoke()
if err != nil {
return nil, err
}
return &tags, nil
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package vpcvolume
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/utils"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/client"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/models"
"go.uber.org/zap"
"strconv"
"time"
)
// ListVolumes GETs /volumes
func (vs *VolumeService) ListVolumes(limit int, filters *models.ListVolumeFilters, ctxLogger *zap.Logger) (*models.VolumeList, error) {
ctxLogger.Debug("Entry Backend ListVolumes")
defer ctxLogger.Debug("Exit Backend ListVolumes")
defer util.TimeTracker("ListVolumes", time.Now())
operation := &client.Operation{
Name: "ListVolumes",
Method: "GET",
PathPattern: volumesPath,
}
var volumes models.VolumeList
var apiErr models.Error
request := vs.client.NewRequest(operation)
ctxLogger.Info("Equivalent curl command", zap.Reflect("URL", request.URL()), zap.Reflect("Operation", operation))
req := request.JSONSuccess(&volumes).JSONError(&apiErr)
if limit > 0 {
req.AddQueryValue("limit", strconv.Itoa(limit))
}
if filters != nil {
if filters.ResourceGroupID != "" {
req.AddQueryValue("resource_group.id", filters.ResourceGroupID)
}
if filters.Tag != "" {
req.AddQueryValue("tag", filters.Tag)
}
if filters.ZoneName != "" {
req.AddQueryValue("zone.name", filters.ZoneName)
}
if filters.VolumeName != "" {
req.AddQueryValue("name", filters.VolumeName)
}
}
_, err := req.Invoke()
if err != nil {
return nil, err
}
return &volumes, nil
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package vpcvolume
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/utils"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/client"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/models"
"go.uber.org/zap"
"time"
)
// SetSnapshotTag sets tag for a snapshot
func (ss *SnapshotService) SetSnapshotTag(volumeID string, snapshotID string, tagName string, ctxLogger *zap.Logger) error {
ctxLogger.Debug("Entry Backend SetVolumeTag")
defer ctxLogger.Debug("Exit Backend SetVolumeTag")
defer util.TimeTracker("SetSnapshotTag", time.Now())
operation := &client.Operation{
Name: "SetSnapshotTag",
Method: "PUT",
PathPattern: snapshotTagNamePath,
}
var apiErr models.Error
request := ss.client.NewRequest(operation)
ctxLogger.Info("Equivalent curl command", zap.Reflect("URL", request.URL()), zap.Reflect("Operation", operation))
req := request.PathParameter(volumeIDParam, volumeID).PathParameter(snapshotIDParam, snapshotID).PathParameter(snapshotTagParam, tagName).JSONError(&apiErr)
_, err := req.Invoke()
if err != nil {
return err
}
return nil
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package vpcvolume
import (
"github.com/IBM/ibmcloud-storage-volume-lib/lib/utils"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/client"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/models"
"go.uber.org/zap"
"time"
)
// SetVolumeTag sets tag for a volume
func (vs *VolumeService) SetVolumeTag(volumeID string, tagName string, ctxLogger *zap.Logger) error {
ctxLogger.Debug("Entry Backend SetVolumeTag")
defer ctxLogger.Debug("Exit Backend SetVolumeTag")
defer util.TimeTracker("SetVolumeTag", time.Now())
operation := &client.Operation{
Name: "SetVolumeTag",
Method: "PUT",
PathPattern: volumeTagNamePath,
}
var apiErr models.Error
request := vs.client.NewRequest(operation)
ctxLogger.Info("Equivalent curl command", zap.Reflect("URL", request.URL()), zap.Reflect("Operation", operation))
req := request.PathParameter(volumeIDParam, volumeID).PathParameter(volumeTagParam, tagName).JSONError(&apiErr)
_, err := req.Invoke()
if err != nil {
return err
}
return nil
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package vpcvolume
import (
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/client"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/models"
"go.uber.org/zap"
)
// SnapshotManager operations
type SnapshotManager interface {
// Create the snapshot on the volume
CreateSnapshot(volumeID string, snapshotTemplate *models.Snapshot, ctxLogger *zap.Logger) (*models.Snapshot, error)
// Delete the snapshot
DeleteSnapshot(volumeID string, snapshotID string, ctxLogger *zap.Logger) error
// Get the snapshot
GetSnapshot(volumeID string, snapshotID string, ctxLogger *zap.Logger) (*models.Snapshot, error)
// List all the snapshots for a given volume
ListSnapshots(volumeID string, ctxLogger *zap.Logger) (*models.SnapshotList, error)
// Set tag for a snapshot
SetSnapshotTag(volumeID string, snapshotID string, tagName string, ctxLogger *zap.Logger) error
// Delete tag of a snapshot
DeleteSnapshotTag(volumeID string, snapshotID string, tagName string, ctxLogger *zap.Logger) error
// List all tags of a snapshot
ListSnapshotTags(volumeID string, snapshotID string, ctxLogger *zap.Logger) (*[]string, error)
// Check if the given tag exists on a snapshot
CheckSnapshotTag(volumeID string, snapshotID string, tagName string, ctxLogger *zap.Logger) error
}
// SnapshotService ...
type SnapshotService struct {
client client.SessionClient
}
var _ SnapshotManager = &SnapshotService{}
// NewSnapshotManager ...
func NewSnapshotManager(client client.SessionClient) SnapshotManager {
return &SnapshotService{
client: client,
}
}
/*******************************************************************************
* IBM Confidential
* OCO Source Materials
* IBM Cloud Container Service, 5737-D43
* (C) Copyright IBM Corp. 2018, 2019 All Rights Reserved.
* The source code for this program is not published or otherwise divested of
* its trade secrets, irrespective of what has been deposited with
* the U.S. Copyright Office.
******************************************************************************/
package vpcvolume
import (
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/client"
"github.com/IBM/ibmcloud-storage-volume-lib/volume-providers/vpc/vpcclient/models"
"go.uber.org/zap"
)
// VolumeManager operations
type VolumeManager interface {
// Create the volume with authorisation by passing required information in the volume object
CreateVolume(volumeTemplate *models.Volume, ctxLogger *zap.Logger) (*models.Volume, error)
// Delete the volume
DeleteVolume(volumeID string, ctxLogger *zap.Logger) error
// Get the volume by using ID
GetVolume(volumeID string, ctxLogger *zap.Logger) (*models.Volume, error)
// Get the volume by using volume name
GetVolumeByName(volumeName string, ctxLogger *zap.Logger) (*models.Volume, error)
// Others
// Get volume lists by using snapshot tags
ListVolumes(limit int, filters *models.ListVolumeFilters, ctxLogger *zap.Logger) (*models.VolumeList, error)
// Set tag for a volume
SetVolumeTag(volumeID string, tagName string, ctxLogger *zap.Logger) error
// Delete tag of a volume
DeleteVolumeTag(volumeID string, tagName string, ctxLogger *zap.Logger) error
// List all tags of a volume
ListVolumeTags(volumeID string, ctxLogger *zap.Logger) (*[]string, error)
// Check if the given tag exists on a volume
CheckVolumeTag(volumeID string, tagName string, ctxLogger *zap.Logger) error
}
// VolumeService ...
type VolumeService struct {
client client.SessionClient
}
var _ VolumeManager = &VolumeService{}
// New ...
func New(client client.SessionClient) VolumeManager {
return &VolumeService{
client: client,
}
}