Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add OpenStack Swift scaler #1462

Merged
merged 1 commit into from
Jan 8, 2021
Merged

Conversation

pdominguite
Copy link
Contributor

@pdominguite pdominguite commented Dec 29, 2020

Signed-off-by: Pedro Dominguite [email protected]

This PR adds a new scaler for the Swift OpenStack object-store.

Checklist

We are committed to delivering end to end test for this scaler soon.

Related to #1342.

@zroubalik zroubalik changed the title Add swift scaler Add OpenStack Swift scaler Jan 4, 2021
Copy link
Member

@zroubalik zroubalik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution!

Looking good, just some minor remarks

@@ -464,6 +464,8 @@ func buildScaler(triggerType string, config *scalers.ScalerConfig) (scalers.Scal
return scalers.NewRedisStreamsScaler(config)
case "stan":
return scalers.NewStanScaler(config)
case "swift":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
case "swift":
case "openstack-swift":

Copy link
Contributor Author

@pdominguite pdominguite Jan 4, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Constructor and other helper functions were named NewSwiftScaler or parseSwiftMetadata. Would it be the case to rename them to NewOpenstackSwiftScaler and parseOpenstackSwiftMetadata, for example?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both 0313be5 and e12e140 applied the suggested changes 😃


externalMetric := &v2beta2.ExternalMetricSource{
Metric: v2beta2.MetricIdentifier{
Name: kedautil.NormalizeString(fmt.Sprintf("%s-%s", "swift", s.metadata.containerName)),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The generated metricName should be unique enough, that if there are multiple scalers of the same type in one ScaledObject, each metric name is still unique.
Just double checking, because I don't have enough expertise in Openstack Swift, I am not sure whether this is the case?

Do you think that there's a sane scenario where we would like to have multiple swift triggers pointing to the same container? Or is that something, that doesn't make a sense?

Copy link
Contributor Author

@pdominguite pdominguite Jan 4, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OpenStack Swift should work similarly to Azure Blob Storage or any other object-store service.

We have identified one scenario where it could be possible to have multiple swift triggers pointing to the same container, though: when one wants to scale based on the number of objects inside different folders within the same container. In this case, delimiter and prefixes can be used. In order to set more than one prefix, new triggers for the same container in the same ScaledObject must be created.

Thus, we have modified metricName to contain the containerName + objectPrefix, when the latter is provided. That should be enough to cover this scenario.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, thanks!

objectPrefix string
objectDelimiter string
objectLimit string
httpClientTimeout int
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For handling the http, could you please take a look on this PR: #1251

there's kedautil.CreateHTTPClient(timeout time.Duration) function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done by 07fc5e4.

@pdominguite pdominguite requested a review from zroubalik January 4, 2021 21:04
Copy link
Member

@zroubalik zroubalik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks @pdominguite!

What is your plan for e2e tests? We would like to see one for this scaler.

@pdominguite
Copy link
Contributor Author

pdominguite commented Jan 5, 2021

Thanks for all feedbacks @zroubalik! We are very happy to contribute to Keda 😄

Regarding e2e tests: end-to-end tests are a must-have to add this contribution or the swift scaler can be merged and tests provided later on?

If the merge can happen without tests, we plan to submit them in a separate PR by the end of this month/beginning of February max. Is that ok?

Comment on lines +142 to +155
passAuth := new(KeystoneAuthMetadata)

passAuth.Properties = new(authProps)

passAuth.Properties.Scope = new(scopeProps)
passAuth.Properties.Scope.Project = new(projectProps)

passAuth.Properties.Identity = new(identityProps)
passAuth.Properties.Identity.Password = new(passwordProps)
passAuth.Properties.Identity.Password.User = new(userProps)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems to me that you don't need to make passAuth, passAuth.Properties, passAuth.Properties.Identity, passAuth.Properties.Identity.Password, passAuth.Properties.Identity.Password.User, or passAuth.Properties.Scope references, and then you'll get a default object for all of them just by doing

passAuth := KeystoneAuthMetadata{}

right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I know (and tried), since KeystoneAuthMetadata is a struct that has other structs inside that are pointers, they need to be initialized somehow first. Thus,

passAuth := KeystoneAuthMetadata{}

is not sufficient to get a proper KeystoneAuthMetadata default object. Assigning values to it without manually making those references will result in:

runtime error: invalid memory address or nil pointer dereference

Maybe there is a more elegant way to do or another way to initialize these "inner structs", but my limited experience in Go does not allow me to find it out 😅

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess what I meant is maybe non of these pointers need to be a pointer since you never expect them to actually be nil, right? So KeystoreAuthMetadata would be

type KeystoneAuthMetadata struct {
	AuthURL    string       `json:"-"`
	AuthToken  string       `json:"-"`
	HTTPClient *http.Client `json:"-"`
-	Properties *authProps   `json:"auth"`
+	Properties authProps   `json:"auth"`
}

and in turn authProps would be

type authProps struct {
-	Identity *identityProps `json:"identity"`
+	Identity identityProps `json:"identity"`
-	Scope    *scopeProps    `json:"scope,omitempty"`
+	Scope    scopeProps    `json:"scope,omitempty"`
}

and so on.

I think the only difference is in the json.Marshal() behavior where a nil is an empty object but any non-nil object is not empty, but I don't think it makes a difference really. Just thought it would make the code a bit easier to read since you're already initializing all the pointers with new() anyway. No worries if it gives you trouble.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly! They do not need to be pointers. The only reason we did that way was because of json.Marshal() as you noticed. Since we use these structs to build a JSON payload for our token request, some of them need to be omitted (and thus be pointers, and then be nil) in order to make it work properly.

Also, we plan to submit new scalers for OpenStack projects soon. As they come, we can study better ways to do it and, eventually, updated the way we originally structured this.

That is nice feedback, anyway @ahmelsayed. Thanks! We'll keep an eye on it 😄

return resp.Header["X-Subject-Token"][0], nil
}

errBody, _ := ioutil.ReadAll(resp.Body)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: since this is an error case, maybe handle the err in case the stream is terminated from the server early.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I missed this error handling. c6d9f14 added the basic handler.

@ahmelsayed
Copy link
Contributor

I think an e2e test can come in a separate PR to not block this one on it.

Signed-off-by: Pedro Felipe Dominguite <[email protected]>

* Add Swift scaler unit tests

Signed-off-by: Pedro Felipe Dominguite <[email protected]>

* Change scaler name to openstack-swift

Signed-off-by: Pedro Felipe Dominguite <[email protected]>

* Change scaler name references to openstackSwift

* Add objectPrefix to metricName

Signed-off-by: Pedro Felipe Dominguite <[email protected]>

* Use kedautil for handling http

Signed-off-by: Pedro Felipe Dominguite <[email protected]>

* Add read stream error handler

Signed-off-by: Pedro Felipe Dominguite <[email protected]>

* Update CHANGELOG.md

Signed-off-by: Pedro Felipe Dominguite <[email protected]>
@zroubalik zroubalik merged commit 3ff035a into kedacore:main Jan 8, 2021
ycabrer pushed a commit to ycabrer/keda that referenced this pull request Mar 1, 2021
Signed-off-by: Pedro Felipe Dominguite <[email protected]>

* Add Swift scaler unit tests

Signed-off-by: Pedro Felipe Dominguite <[email protected]>

* Change scaler name to openstack-swift

Signed-off-by: Pedro Felipe Dominguite <[email protected]>

* Change scaler name references to openstackSwift

* Add objectPrefix to metricName

Signed-off-by: Pedro Felipe Dominguite <[email protected]>

* Use kedautil for handling http

Signed-off-by: Pedro Felipe Dominguite <[email protected]>

* Add read stream error handler

Signed-off-by: Pedro Felipe Dominguite <[email protected]>

* Update CHANGELOG.md

Signed-off-by: Pedro Felipe Dominguite <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants