diff --git a/CHANGELOG.md b/CHANGELOG.md index 19d562c3ee2..f7f78459300 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ - Fix memory leak by checking triggers uniqueness properly ([#1640](https://github.com/kedacore/keda/pull/1640)) - Print correct ScaleTarget Kind in Events ([#1641](https://github.com/kedacore/keda/pull/1641)) - Fixed keda clusterroles to give permissions for clustertriggerauthentications ([#1645](https://github.com/kedacore/keda/pull/1645)) +- Make `swiftURL` parameter optional for the OpenStack Swift scaler ### Breaking Changes diff --git a/pkg/scalers/openstack/keystone_authentication.go b/pkg/scalers/openstack/keystone_authentication.go index dd798916c13..76523890f8b 100644 --- a/pkg/scalers/openstack/keystone_authentication.go +++ b/pkg/scalers/openstack/keystone_authentication.go @@ -10,17 +10,41 @@ import ( "path" "time" + openstackutil "github.com/kedacore/keda/v2/pkg/scalers/openstack/utils" + kedautil "github.com/kedacore/keda/v2/pkg/util" ) -const tokensEndpoint = "/auth/tokens" +const tokensEndpoint = "/v3/auth/tokens" +const catalogEndpoint = "/v3/auth/catalog" + +// Client is a struct containing an authentication token and an HTTP client for HTTP requests. +// It can also have a public URL for an specific OpenStack project or service. +// "authMetadata" is an unexported attribute used to validate the current token or to renew it against Keystone when it is expired. +type Client struct { + // Token is the authentication token for querying an OpenStack API. + Token string + + // URL is the public URL for an OpenStack project. + URL string -// KeystoneAuthMetadata contains all the necessary metadata for Keystone authentication -type KeystoneAuthMetadata struct { - AuthURL string `json:"-"` - AuthToken string `json:"-"` - HTTPClient *http.Client `json:"-"` - Properties *authProps `json:"auth"` + // HTTPClient is the client used for launching HTTP requests. + HTTPClient *http.Client + + // authMetadata contains the properties needed for retrieving an authentication token, renew it, and dinamically discover services public URLs from Keystone. + authMetadata *KeystoneAuthRequest +} + +// KeystoneAuthRequest contains all the necessary metadata for building an authentication request for Keystone, the official OpenStack Identity Provider. +type KeystoneAuthRequest struct { + // authURL is the Keystone URL. + authURL string `json:"-"` + + // httpClientTimeout is the HTTP client for querying the OpenStack service API. + httpClientTimeout time.Duration `json:"-"` + + // Properties contains the authentication metadata to build the body of a token request. + Properties *authProps `json:"auth"` } type authProps struct { @@ -56,59 +80,34 @@ type projectProps struct { ID string `json:"id"` } -// GetToken retrieves a token from Keystone -func (authProps *KeystoneAuthMetadata) GetToken() (string, error) { - jsonBody, jsonError := json.Marshal(authProps) - - if jsonError != nil { - return "", jsonError - } - - body := bytes.NewReader(jsonBody) - - tokenURL, err := url.Parse(authProps.AuthURL) - - if err != nil { - return "", fmt.Errorf("the authURL is invalid: %s", err.Error()) - } - - tokenURL.Path = path.Join(tokenURL.Path, tokensEndpoint) - - getTokenRequest, getTokenRequestError := http.NewRequest("POST", tokenURL.String(), body) - - getTokenRequest.Header.Set("Content-Type", "application/json") - - if getTokenRequestError != nil { - return "", getTokenRequestError - } - - resp, requestError := authProps.HTTPClient.Do(getTokenRequest) - - if requestError != nil { - return "", requestError - } - - defer resp.Body.Close() - - if resp.StatusCode >= 200 && resp.StatusCode < 300 { - authProps.AuthToken = resp.Header["X-Subject-Token"][0] - return resp.Header["X-Subject-Token"][0], nil - } - - errBody, readBodyErr := ioutil.ReadAll(resp.Body) +type keystoneCatalog struct { + Catalog []service `json:"catalog"` +} - if readBodyErr != nil { - return "", readBodyErr - } +type service struct { + Endpoints []endpoint `json:"endpoints"` + Type string `json:"type"` + ID string `json:"id"` + Name string `json:"name"` +} - return "", fmt.Errorf(string(errBody)) +type endpoint struct { + URL string `json:"url"` + Interface string `json:"interface"` + Region string `json:"region"` + RegionID string `json:"region_id"` + ID string `json:"id"` } // IsTokenValid checks if a authentication token is valid -func IsTokenValid(authProps KeystoneAuthMetadata) (bool, error) { - token := authProps.AuthToken +func (client *Client) IsTokenValid() (bool, error) { + var token = client.Token - tokenURL, err := url.Parse(authProps.AuthURL) + if token == "" { + return false, fmt.Errorf("no authentication token provided") + } + + tokenURL, err := url.Parse(client.authMetadata.authURL) if err != nil { return false, fmt.Errorf("the authURL is invalid: %s", err.Error()) @@ -116,34 +115,45 @@ func IsTokenValid(authProps KeystoneAuthMetadata) (bool, error) { tokenURL.Path = path.Join(tokenURL.Path, tokensEndpoint) - checkTokenRequest, checkRequestError := http.NewRequest("HEAD", tokenURL.String(), nil) + checkTokenRequest, err := http.NewRequest("HEAD", tokenURL.String(), nil) checkTokenRequest.Header.Set("X-Subject-Token", token) checkTokenRequest.Header.Set("X-Auth-Token", token) - if checkRequestError != nil { - return false, checkRequestError + if err != nil { + return false, err } - checkResp, requestError := authProps.HTTPClient.Do(checkTokenRequest) + response, err := client.HTTPClient.Do(checkTokenRequest) - if requestError != nil { - return false, requestError + if err != nil { + return false, err } - defer checkResp.Body.Close() + defer response.Body.Close() - if checkResp.StatusCode >= 400 { + if response.StatusCode >= 400 { return false, nil } return true, nil } -// NewPasswordAuth creates a struct containing metadata for authentication using password method -func NewPasswordAuth(authURL string, userID string, userPassword string, projectID string, httpTimeout int) (*KeystoneAuthMetadata, error) { - var tokenError error +// RenewToken retrives another token from Keystone +func (client *Client) RenewToken() error { + token, err := client.authMetadata.getToken() + + if err != nil { + return err + } + + client.Token = token + + return nil +} - passAuth := new(KeystoneAuthMetadata) +// NewPasswordAuth creates a struct containing metadata for authentication using the password method +func NewPasswordAuth(authURL string, userID string, userPassword string, projectID string, httpTimeout int) (*KeystoneAuthRequest, error) { + passAuth := new(KeystoneAuthRequest) passAuth.Properties = new(authProps) @@ -162,26 +172,23 @@ func NewPasswordAuth(authURL string, userID string, userPassword string, project url.Path = path.Join(url.Path, "") - passAuth.AuthURL = url.String() + passAuth.authURL = url.String() - passAuth.HTTPClient = kedautil.CreateHTTPClient(time.Duration(httpTimeout) * time.Second) + passAuth.httpClientTimeout = time.Duration(httpTimeout) * time.Second passAuth.Properties.Identity.Methods = []string{"password"} + passAuth.Properties.Identity.Password.User.ID = userID passAuth.Properties.Identity.Password.User.Password = userPassword passAuth.Properties.Scope.Project.ID = projectID - passAuth.AuthToken, tokenError = passAuth.GetToken() - - return passAuth, tokenError + return passAuth, nil } -// NewAppCredentialsAuth creates a struct containing metadata for authentication using application credentials method -func NewAppCredentialsAuth(authURL string, id string, secret string, httpTimeout int) (*KeystoneAuthMetadata, error) { - var tokenError error - - appAuth := new(KeystoneAuthMetadata) +// NewAppCredentialsAuth creates a struct containing metadata for authentication using the application credentials method +func NewAppCredentialsAuth(authURL string, id string, secret string, httpTimeout int) (*KeystoneAuthRequest, error) { + appAuth := new(KeystoneAuthRequest) appAuth.Properties = new(authProps) @@ -195,16 +202,205 @@ func NewAppCredentialsAuth(authURL string, id string, secret string, httpTimeout url.Path = path.Join(url.Path, "") - appAuth.AuthURL = url.String() - - appAuth.HTTPClient = kedautil.CreateHTTPClient(time.Duration(httpTimeout) * time.Second) + appAuth.authURL = url.String() appAuth.Properties.Identity.AppCredential = new(appCredentialProps) appAuth.Properties.Identity.Methods = []string{"application_credential"} appAuth.Properties.Identity.AppCredential.ID = id appAuth.Properties.Identity.AppCredential.Secret = secret - appAuth.AuthToken, tokenError = appAuth.GetToken() + appAuth.httpClientTimeout = time.Duration(httpTimeout) * time.Second + + return appAuth, nil +} + +// RequestClient returns a Client containing an HTTP client and a token. +// If an OpenStack project name is provided as first parameter, it will try to retrieve its API URL using the current credentials. +// If an OpenStack region or availability zone is provided as second parameter, it will retrieve the service API URL for that region. +// Otherwise, if the service API URL was found, it retrieves the first public URL for that service. +func (keystone *KeystoneAuthRequest) RequestClient(projectProps ...string) (Client, error) { + var client = Client{ + HTTPClient: kedautil.CreateHTTPClient(keystone.httpClientTimeout), + authMetadata: keystone, + } + + token, err := keystone.getToken() + + if err != nil { + return client, err + } + + client.Token = token + + var serviceURL string + + switch len(projectProps) { + case 2: + serviceURL, err = keystone.getServiceURL(token, projectProps[0], projectProps[1]) + case 1: + serviceURL, err = keystone.getServiceURL(token, projectProps[0], "") + default: + serviceURL = "" + } + + if err != nil { + return client, fmt.Errorf("scaler could not find the service URL dinamically. Either provide it in the scaler parameters or check your OpenStack configuration: %s", err.Error()) + } + + client.URL = serviceURL + + return client, nil +} + +func (keystone *KeystoneAuthRequest) getToken() (string, error) { + var httpClient = kedautil.CreateHTTPClient(keystone.httpClientTimeout) + + jsonBody, err := json.Marshal(keystone) + + if err != nil { + return "", err + } + + jsonBodyReader := bytes.NewReader(jsonBody) + + tokenURL, err := url.Parse(keystone.authURL) + + if err != nil { + return "", fmt.Errorf("the authURL is invalid: %s", err.Error()) + } + + tokenURL.Path = path.Join(tokenURL.Path, tokensEndpoint) + + tokenRequest, err := http.NewRequest("POST", tokenURL.String(), jsonBodyReader) + + if err != nil { + return "", err + } + + resp, err := httpClient.Do(tokenRequest) + + if err != nil { + return "", err + } + + defer resp.Body.Close() + + if resp.StatusCode >= http.StatusOK && resp.StatusCode < http.StatusMultipleChoices { + return resp.Header["X-Subject-Token"][0], nil + } + + errBody, err := ioutil.ReadAll(resp.Body) + + if err != nil { + return "", err + } + + return "", fmt.Errorf(string(errBody)) +} + +// getCatalog retrives the OpenStack catalog according to the current authorization +func (keystone *KeystoneAuthRequest) getCatalog(token string) ([]service, error) { + var httpClient = kedautil.CreateHTTPClient(keystone.httpClientTimeout) + + catalogURL, err := url.Parse(keystone.authURL) + + if err != nil { + return nil, fmt.Errorf("the authURL is invalid: %s", err.Error()) + } + + catalogURL.Path = path.Join(catalogURL.Path, catalogEndpoint) + + getCatalog, err := http.NewRequest("GET", catalogURL.String(), nil) + + if err != nil { + return nil, err + } + + getCatalog.Header.Set("X-Auth-Token", token) + + resp, err := httpClient.Do(getCatalog) + + if err != nil { + return nil, err + } + + defer resp.Body.Close() + + if resp.StatusCode >= 200 && resp.StatusCode < 300 { + var keystoneCatalog = keystoneCatalog{} + + err := json.NewDecoder(resp.Body).Decode(&keystoneCatalog) + + if err != nil { + return nil, fmt.Errorf("error parsing the catalog resquest response body: %s", err.Error()) + } + + return keystoneCatalog.Catalog, nil + } + + errBody, err := ioutil.ReadAll(resp.Body) + + if err != nil { + return nil, err + } + + return nil, fmt.Errorf(string(errBody)) +} + +// getServiceURL retrieves a public URL for an OpenStack project from the OpenStack catalog +func (keystone *KeystoneAuthRequest) getServiceURL(token string, projectName string, region string) (string, error) { + serviceTypes, err := openstackutil.GetServiceTypes(projectName) + + if err != nil { + return "", err + } + + serviceCatalog, err := keystone.getCatalog(token) + + if err != nil { + return "", err + } + + if len(serviceCatalog) == 0 { + return "", fmt.Errorf("no catalog provided based upon the current authorization. Service URL cannot be dinamically retrieved") + } + + for _, serviceType := range serviceTypes { + for _, service := range serviceCatalog { + if serviceType == service.Type { + for _, endpoint := range service.Endpoints { + if endpoint.Interface == "public" { + if region != "" { + if endpoint.Region == region { + return endpoint.URL, nil + } + continue + } + return endpoint.URL, nil + } + } + return "", fmt.Errorf("service '%s' does not have a public URL or the public URL for a specific region is not registered in the catalog", projectName) + } + } + } + + // If getServiceTypes() timed-out or failed or if serviceType is not in the catalog, try to find by project name + for _, service := range serviceCatalog { + if projectName == service.Name { + for _, endpoint := range service.Endpoints { + if endpoint.Interface == "public" { + if region != "" { + if endpoint.Region == region { + return endpoint.URL, nil + } + continue + } + return endpoint.URL, nil + } + } + return "", fmt.Errorf("service '%s' does not have a public URL or the public URL for a specific region is not registered in the catalog", projectName) + } + } - return appAuth, tokenError + return "", fmt.Errorf("service '%s' not found in catalog. Please, provide different credentials or reach to your OpenStack cluster manager", projectName) } diff --git a/pkg/scalers/openstack/utils/serviceTypes.go b/pkg/scalers/openstack/utils/serviceTypes.go new file mode 100644 index 00000000000..ba63f074791 --- /dev/null +++ b/pkg/scalers/openstack/utils/serviceTypes.go @@ -0,0 +1,76 @@ +package utils + +import ( + "encoding/json" + "fmt" + "net/http" + "time" + + kedautil "github.com/kedacore/keda/v2/pkg/util" +) + +const ( + serviceTypesAuthorityEndpoint = "https://service-types.openstack.org/service-types.json" + defaultHTTPClientTimeout = 30 +) + +type serviceTypesRequest struct { + AllTypesByServiceType map[string][]string `json:"all_types_by_service_type"` + Forward map[string][]string `json:"forward"` + PrimaryServiceByProject map[string]serviceMapping `json:"primary_service_by_project"` + Reverse map[string]string `json:"reverse"` + ServiceTypesByProject map[string][]string `json:"service_types_by_project"` + Services []serviceMapping `json:"services"` + SHA string `json:"sha"` + Version string `json:"version"` +} + +type serviceMapping struct { + Aliases []string `json:"aliases,omitempty"` + APIReference string `json:"api_reference"` + Project string `json:"project"` + ServiceType string `json:"service_type"` +} + +// GetServiceTypes retrieves all historical OpenStack Service Types for a given OpenStack project +func GetServiceTypes(projectName string) ([]string, error) { + var serviceTypesRequest serviceTypesRequest + + var httpClient = kedautil.CreateHTTPClient(defaultHTTPClientTimeout * time.Second) + + var url = serviceTypesAuthorityEndpoint + + getServiceTypes, err := http.NewRequest("GET", url, nil) + + if err != nil { + return []string{}, err + } + + resp, err := httpClient.Do(getServiceTypes) + + if err != nil || resp.Status >= "300" { + return []string{}, nil + } + + defer resp.Body.Close() + + jsonErr := json.NewDecoder(resp.Body).Decode(&serviceTypesRequest) + + if jsonErr != nil { + return []string{}, jsonErr + } + + var serviceTypes = serviceTypesRequest.PrimaryServiceByProject[projectName].Aliases + + if len(serviceTypes) == 0 { + var serviceType = serviceTypesRequest.PrimaryServiceByProject[projectName].ServiceType + + if serviceType != "" { + return []string{serviceType}, nil + } + + return []string{}, fmt.Errorf("project is not an official OpenStack project") + } + + return serviceTypes, nil +} diff --git a/pkg/scalers/openstack_swift_scaler.go b/pkg/scalers/openstack_swift_scaler.go index 256838b0f33..7ea457acea7 100644 --- a/pkg/scalers/openstack_swift_scaler.go +++ b/pkg/scalers/openstack_swift_scaler.go @@ -48,38 +48,37 @@ type openstackSwiftAuthenticationMetadata struct { authURL string appCredentialID string appCredentialSecret string + regionName string } type openstackSwiftScaler struct { - metadata *openstackSwiftMetadata - authMetadata *openstack.KeystoneAuthMetadata + metadata *openstackSwiftMetadata + swiftClient openstack.Client } var openstackSwiftLog = logf.Log.WithName("openstack_swift_scaler") func (s *openstackSwiftScaler) getOpenstackSwiftContainerObjectCount() (int, error) { - var token string - var swiftURL string = s.metadata.swiftURL - var containerName string = s.metadata.containerName + var containerName = s.metadata.containerName + var swiftURL = s.metadata.swiftURL - isValid, validationError := openstack.IsTokenValid(*s.authMetadata) + isValid, err := s.swiftClient.IsTokenValid() - if validationError != nil { - openstackSwiftLog.Error(validationError, "scaler could not validate the token for authentication") - return 0, validationError + if err != nil { + openstackSwiftLog.Error(err, "scaler could not validate the token for authentication") + return 0, err } if !isValid { - var tokenRequestError error - token, tokenRequestError = s.authMetadata.GetToken() - s.authMetadata.AuthToken = token - if tokenRequestError != nil { - openstackSwiftLog.Error(tokenRequestError, "error requesting token for authentication") - return 0, tokenRequestError + err := s.swiftClient.RenewToken() + + if err != nil { + openstackSwiftLog.Error(err, "error requesting token for authentication") + return 0, err } } - token = s.authMetadata.AuthToken + token := s.swiftClient.Token swiftContainerURL, err := url.Parse(swiftURL) @@ -98,14 +97,14 @@ func (s *openstackSwiftScaler) getOpenstackSwiftContainerObjectCount() (int, err query.Add("prefix", s.metadata.objectPrefix) query.Add("delimiter", s.metadata.objectDelimiter) - // If scaler wants to scale based on only files, we first need to query all objects, then filter files and finally limit the result to the specified query limit + // If scaler wants to scale based on only files, we first need to query all objects, then filter files and finally limit the result to the specified query limit if !s.metadata.onlyFiles { query.Add("limit", s.metadata.objectLimit) } swiftRequest.URL.RawQuery = query.Encode() - resp, requestError := s.authMetadata.HTTPClient.Do(swiftRequest) + resp, requestError := s.swiftClient.HTTPClient.Do(swiftRequest) if requestError != nil { openstackSwiftLog.Error(requestError, fmt.Sprintf("error getting metrics for container '%s'. You probably specified the wrong swift URL or the URL is not reachable", containerName)) @@ -120,7 +119,7 @@ func (s *openstackSwiftScaler) getOpenstackSwiftContainerObjectCount() (int, err openstackSwiftLog.Error(readError, "could not read response body from Swift API") return 0, readError } - if resp.StatusCode >= 200 && resp.StatusCode < 300 { + if resp.StatusCode >= http.StatusOK && resp.StatusCode < http.StatusMultipleChoices { var objectsList = strings.Split(strings.TrimSpace(string(body)), "\n") // If onlyFiles is set to "true", return the total amount of files (excluding empty objects/folders) @@ -158,17 +157,17 @@ func (s *openstackSwiftScaler) getOpenstackSwiftContainerObjectCount() (int, err return objectCount, conversionError } - if resp.StatusCode == 401 { + if resp.StatusCode == http.StatusUnauthorized { openstackSwiftLog.Error(nil, "the retrieved token is not a valid token. Provide the correct auth credentials so the scaler can retrieve a valid access token (Unauthorized)") return 0, fmt.Errorf("the retrieved token is not a valid token. Provide the correct auth credentials so the scaler can retrieve a valid access token (Unauthorized)") } - if resp.StatusCode == 403 { + if resp.StatusCode == http.StatusForbidden { openstackSwiftLog.Error(nil, "the retrieved token is a valid token, but it does not have sufficient permission to retrieve Swift and/or container metadata (Forbidden)") return 0, fmt.Errorf("the retrieved token is a valid token, but it does not have sufficient permission to retrieve Swift and/or container metadata (Forbidden)") } - if resp.StatusCode == 404 { + if resp.StatusCode == http.StatusNotFound { openstackSwiftLog.Error(nil, fmt.Sprintf("the container '%s' does not exist (Not Found)", containerName)) return 0, fmt.Errorf("the container '%s' does not exist (Not Found)", containerName) } @@ -176,9 +175,11 @@ func (s *openstackSwiftScaler) getOpenstackSwiftContainerObjectCount() (int, err return 0, fmt.Errorf(string(body)) } -// NewOpenstackSwiftScaler creates a new swift scaler +// NewOpenstackSwiftScaler creates a new OpenStack Swift scaler func NewOpenstackSwiftScaler(config *ScalerConfig) (Scaler, error) { - var keystoneAuth *openstack.KeystoneAuthMetadata + var authRequest *openstack.KeystoneAuthRequest + + var swiftClient openstack.Client openstackSwiftMetadata, err := parseOpenstackSwiftMetadata(config) @@ -194,14 +195,14 @@ func NewOpenstackSwiftScaler(config *ScalerConfig) (Scaler, error) { // User chose the "application_credentials" authentication method if authMetadata.appCredentialID != "" { - keystoneAuth, err = openstack.NewAppCredentialsAuth(authMetadata.authURL, authMetadata.appCredentialID, authMetadata.appCredentialSecret, openstackSwiftMetadata.httpClientTimeout) + authRequest, err = openstack.NewAppCredentialsAuth(authMetadata.authURL, authMetadata.appCredentialID, authMetadata.appCredentialSecret, openstackSwiftMetadata.httpClientTimeout) if err != nil { return nil, fmt.Errorf("error getting openstack credentials for application credentials method: %s", err) } } else { // User chose the "password" authentication method if authMetadata.userID != "" { - keystoneAuth, err = openstack.NewPasswordAuth(authMetadata.authURL, authMetadata.userID, authMetadata.password, authMetadata.projectID, openstackSwiftMetadata.httpClientTimeout) + authRequest, err = openstack.NewPasswordAuth(authMetadata.authURL, authMetadata.userID, authMetadata.password, authMetadata.projectID, openstackSwiftMetadata.httpClientTimeout) if err != nil { return nil, fmt.Errorf("error getting openstack credentials for password method: %s", err) } @@ -210,9 +211,29 @@ func NewOpenstackSwiftScaler(config *ScalerConfig) (Scaler, error) { } } + if openstackSwiftMetadata.swiftURL == "" { + // Request a Client with a token and the Swift API endpoint + swiftClient, err = authRequest.RequestClient("swift", authMetadata.regionName) + + if err != nil { + return nil, fmt.Errorf("swiftURL was not provided and the scaler could not retrieve it dinamically using the OpenStack catalog: %s", err.Error()) + } + + openstackSwiftMetadata.swiftURL = swiftClient.URL + } else { + // Request a Client with a token, but not the Swift API endpoint + swiftClient, err = authRequest.RequestClient() + + if err != nil { + return nil, err + } + + swiftClient.URL = openstackSwiftMetadata.swiftURL + } + return &openstackSwiftScaler{ - metadata: openstackSwiftMetadata, - authMetadata: keystoneAuth, + metadata: openstackSwiftMetadata, + swiftClient: swiftClient, }, nil } @@ -222,7 +243,7 @@ func parseOpenstackSwiftMetadata(config *ScalerConfig) (*openstackSwiftMetadata, if val, ok := config.TriggerMetadata["swiftURL"]; ok { meta.swiftURL = val } else { - return nil, fmt.Errorf("no swiftURL given") + meta.swiftURL = "" } if val, ok := config.TriggerMetadata["containerName"]; ok { @@ -291,6 +312,12 @@ func parseOpenstackSwiftAuthenticationMetadata(config *ScalerConfig) (*openstack return nil, fmt.Errorf("authURL doesn't exist in the authParams") } + if config.AuthParams["regionName"] != "" { + authMeta.regionName = config.AuthParams["regionName"] + } else { + authMeta.regionName = "" + } + if config.AuthParams["userID"] != "" { authMeta.userID = config.AuthParams["userID"] diff --git a/pkg/scalers/openstack_swift_scaler_test.go b/pkg/scalers/openstack_swift_scaler_test.go index 2f5b3919bc3..5bb98df9109 100644 --- a/pkg/scalers/openstack_swift_scaler_test.go +++ b/pkg/scalers/openstack_swift_scaler_test.go @@ -3,6 +3,7 @@ package scalers import ( "testing" + "github.com/kedacore/keda/v2/pkg/scalers/openstack" "github.com/stretchr/testify/assert" ) @@ -23,7 +24,7 @@ type openstackSwiftMetricIdentifier struct { var openstackSwiftMetadataTestData = []parseOpenstackSwiftMetadataTestData{ // Only required parameters - {metadata: map[string]string{"swiftURL": "http://localhost:8080/v1/my-account-id", "containerName": "my-container"}}, + {metadata: map[string]string{"containerName": "my-container"}}, // Adding objectCount {metadata: map[string]string{"swiftURL": "http://localhost:8080/v1/my-account-id", "containerName": "my-container", "objectCount": "5"}}, // Adding objectPrefix @@ -44,8 +45,6 @@ var openstackSwiftAuthMetadataTestData = []parseOpenstackSwiftAuthMetadataTestDa } var invalidOpenstackSwiftMetadataTestData = []parseOpenstackSwiftMetadataTestData{ - // Missing swiftURL - {metadata: map[string]string{"containerName": "my-container", "objectCount": "5"}}, // Missing containerName {metadata: map[string]string{"swiftURL": "http://localhost:8080/v1/my-account-id", "objectCount": "5"}}, // objectCount is not an integer value @@ -108,7 +107,7 @@ func TestOpenstackSwiftGetMetricSpecForScaling(t *testing.T) { t.Fatal("Could not parse auth metadata:", err) } - mockSwiftScaler := openstackSwiftScaler{meta, nil} + mockSwiftScaler := openstackSwiftScaler{meta, openstack.Client{}} metricSpec := mockSwiftScaler.GetMetricSpecForScaling() @@ -122,11 +121,10 @@ func TestOpenstackSwiftGetMetricSpecForScaling(t *testing.T) { func TestParseOpenstackSwiftMetadataForInvalidCases(t *testing.T) { testCases := []openstackSwiftMetricIdentifier{ - {nil, &invalidOpenstackSwiftMetadataTestData[0], &parseOpenstackSwiftAuthMetadataTestData{}, "missing swiftURL"}, - {nil, &invalidOpenstackSwiftMetadataTestData[1], &parseOpenstackSwiftAuthMetadataTestData{}, "missing containerName"}, - {nil, &invalidOpenstackSwiftMetadataTestData[2], &parseOpenstackSwiftAuthMetadataTestData{}, "objectCount is not an integer value"}, - {nil, &invalidOpenstackSwiftMetadataTestData[3], &parseOpenstackSwiftAuthMetadataTestData{}, "onlyFiles is not a boolean value"}, - {nil, &invalidOpenstackSwiftMetadataTestData[4], &parseOpenstackSwiftAuthMetadataTestData{}, "timeout is not an integer value"}, + {nil, &invalidOpenstackSwiftMetadataTestData[0], &parseOpenstackSwiftAuthMetadataTestData{}, "missing containerName"}, + {nil, &invalidOpenstackSwiftMetadataTestData[1], &parseOpenstackSwiftAuthMetadataTestData{}, "objectCount is not an integer value"}, + {nil, &invalidOpenstackSwiftMetadataTestData[2], &parseOpenstackSwiftAuthMetadataTestData{}, "onlyFiles is not a boolean value"}, + {nil, &invalidOpenstackSwiftMetadataTestData[3], &parseOpenstackSwiftAuthMetadataTestData{}, "timeout is not an integer value"}, } for _, testData := range testCases {