Skip to content

feat(keda): add ArtemisQueue scaler mapping - #6893

Open
dhvani7014 wants to merge 4 commits into
apache:mainfrom
dhvani7014:feat-6890-artemis-scaler
Open

dhvani7014 wants to merge 4 commits into
apache:mainfrom
dhvani7014:feat-6890-artemis-scaler

Conversation

@dhvani7014

Copy link
Copy Markdown

Fixes #6890

Adds a KEDA scaler mapping for ActiveMQ Artemis queues, following the keda-scaler contributing guide.

  • pkg/trait/keda/scalers/artemis.go: registers a mapper for the amqp component (the URI ArkMQ bindings produce, amqp:queue:<name>). It maps the queue name to queueName and brokerAddress for the KEDA artemis-queue scaler. Topics are not mapped. managementEndpoint and brokerName can't be derived from the URI, so they are provided via keda.autoMetadata.
  • artemis_test.go: unit tests for registration, mapping, and MapToKedaTrigger, modelled on kafka_test.go.
  • e2e: added a KEDA auto-discovery test to the ArkMQ suite (Pipe from my-queue with autoMetadata), and installed KEDA in the ArkMQ setup script.

Tested locally with go test ./pkg/trait/keda/... ./pkg/trait, gofmt, and go vet -tags integration ./e2e/arkmq/. I couldn't run the e2e test locally.

The e2e test checks the generated ScaledObject (auto-discovery + metadata merge), like the Kafka auto-discovery test. Happy to extend it to a full scale-up/scale-down test if you'd like.

@squakez squakez left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think the main goal is to be able to discover the endpoint automatically. I think you can use a Pipe and see how it converts into an Integration DSL. From the Integration you can verify the camel parameters and map them into the scaler accordingly.

@dhvani7014

Copy link
Copy Markdown
Author

Thanks @squakez! I checked the Pipe → Integration conversion: the ArkMQ binding produces amqp:queue:my-queue plus the camel.component.amqp.broker-url property. I'm planning to let the KEDA mapper read camel.component.<scheme>.* properties so the artemis scaler can derive managementEndpoint from the broker URL host + port 8161.

A few questions before I push:

  1. Should the endpoint be the headless service host or the pod FQDN (<cr>-ss-0...), given the Jolokia origin check?
  2. Is it ok to default brokerName to amq-broker, or keep it in autoMetadata?
  3. Since auto-discovered triggers can't carry the console credentials, is a ScaledObject-only e2e check enough for this PR?

@squakez

squakez commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Thanks @squakez! I checked the Pipe → Integration conversion: the ArkMQ binding produces amqp:queue:my-queue plus the camel.component.amqp.broker-url property. I'm planning to let the KEDA mapper read camel.component.<scheme>.* properties so the artemis scaler can derive managementEndpoint from the broker URL host + port 8161.

A few questions before I push:

1. Should the endpoint be the headless service host or the pod FQDN (`<cr>-ss-0...`), given the Jolokia origin check?

2. Is it ok to default `brokerName` to `amq-broker`, or keep it in `autoMetadata`?

3. Since auto-discovered triggers can't carry the console credentials, is a ScaledObject-only e2e check enough for this PR?

It could be a good idea to also use application properties to extract mapping configuration. However it may require some change in the original architecture to pass a new map to the scaler. Feel free to provide some proposal. The host should be the one coming from the configuration (mind that it could even be an Integration, not only a Pipe). Any other parameter has to be recovered automatically from the Integration as well as credential if they are present. However, given this is a first implementation you may let the credentials setting out of the implementation.

A good sample to get some inspiration is in the e2e Kafka scaler test. We should try to replicate the same logic, but using the new scaler instead.

@dhvani7014

Copy link
Copy Markdown
Author

Thanks a lot for the review @squakez! Here's a proposal before I change the code:

  1. Pass properties to the scaler. In kedaTrait.autoDiscoverTriggers (pkg/trait/keda.go), collect the Integration's property configuration (e.collectConfigurationPairs("property")) plus the camel trait properties. Then change MapToKedaTrigger(rawURI, props) and ScaleMapper.Map(pathValue, params, props) in pkg/trait/keda/mapper.go, so each scaler only gets the camel.component.<scheme>.* entries for its own scheme. The Kafka scaler keeps working as it does today.
  2. Artemis mapping, with no hardcoded defaults:
    • queueName and brokerAddress come from the amqp:queue:<name> URI.
    • The managementEndpoint host comes from camel.component.amqp.broker-url. This works for a plain Integration as well as a Pipe, since the ArkMQ binding already sets that property.
    • If the host can't be resolved, no auto trigger is generated, so users can still set it through autoMetadata.
  3. Open question: the management port (8161) and brokerName aren't in the Camel config. Should the scaler use 8161 as the default management port and require brokerName through autoMetadata for now? Or would you prefer we read them from the ActiveMQArtemis CR?
  4. Credentials are left out of this first version, as you suggested.
  5. e2e test: following e2e/kafka/kafka_autoscale_keda_test.go, deploy a plain Integration from("amqp:queue:my-queue") with the broker-url property set, then assert that the ScaledObject has an artemis-queue trigger with the expected metadata.

Does this direction look okay before I implement it?

@squakez

squakez commented Oct 22, 2026

Copy link
Copy Markdown
Contributor

@dhvani7014 the previous assessment sounds good.

Or would you prefer we read them from the ActiveMQArtemis CR?

No, the scaler should not be aware of that resource.

@dhvani7014

Copy link
Copy Markdown
Author

Thanks @squakez! I'll go with that: default the management port to 8161, take brokerName from autoMetadata, and not touch the CR. I'll push the changes soon.

@dhvani7014

Copy link
Copy Markdown
Author

Hi @squakez, pushed the changes: the scaler now gets the camel.component.amqp.* properties and derives managementEndpoint from broker-url, and brokerName comes from autoMetadata. I also added a plain Integration e2e test and updated the contributor guide. Could you approve the workflow runs when you get a chance?

@squakez squakez left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think this is taking the right direction. There are a few points that require a bit of attention.

maxReplicaCount: 1
autoMetadata:
artemis-queue:
brokerName: amq-broker No newline at end of file

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is there any possible to derive this param instead?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I couldn't find a way to derive it. brokerName isn't part of the Camel/Integration config (broker-url only has the host and the AMQP port), and since the scaler shouldn't read the ActiveMQArtemis resource, I kept it in autoMetadata. Happy to change it if you kno of another source.

g.Expect(scaledObj.Spec.Triggers[0].Metadata["managementEndpoint"]).To(Equal("my-broker-hdls-svc.arkmq.svc:8161"))
// Merged from autoMetadata
g.Expect(scaledObj.Spec.Triggers[0].Metadata["brokerName"]).To(Equal("amq-broker"))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We miss a check to verify the application is running and scaled to zero properly. I think you can get the very same behavior we're testing in Kafka test.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

makes sense. ill add the check that the app runs and scales to zero like the Kafka test

Comment thread e2e/arkmq/arkmq_keda_test.go
}
if brokerURL, ok := props["broker-url"]; ok {
if u, err := url.Parse(brokerURL); err == nil && u.Hostname() != "" {
metadata["managementEndpoint"] = net.JoinHostPort(u.Hostname(), "8161")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think we can derive the port as well from the "broker-url". Can you verify if that is expected to be part of the parameter?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I checked: the port in broker-url (61616) is the AMQP messaging port while KEDA's managementEndpoint needs the web console port (8161 by default). So it cant come from broker-url. If the console runs on a different port, users can override managementEndpoint through autoMetadata.

Comment thread pkg/trait/keda/mapper.go Outdated
return nil, nil
}
kedaType, metadata := mapper.Map(pathValue, params)
prefix := "camel.component." + scheme + "."

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why don't we provide instead the full map? Each scaler can filter the specific fields on its own. If we keep this generic we enable potentially other scalers with more capability.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good point. I'll pass the full map and let each scaler filter what it needs.

Comment thread pkg/trait/keda.go Outdated
return nil
}

func integrationProperties(e *Environment) map[string]string {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nitpick. We should move this func into trait.go or other generic file as it can be reused in theory. It still needs to be private though.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

okay. ill move it to trait and and keep it private

@dhvani7014

Copy link
Copy Markdown
Author

@squakez pushed the changes from your review. let me know if you need anything else to be changed

This branch has not been deployed

No deployments
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.

ArtemisQueue KEDA scaler

2 participants