How do I start rabbitmq-native consumers configured in application.yml? - grails

I have a Grails 3.1.12 app with the rabbitmq-native 3.3.1 plugin.
In build.gradle:
compile "org.grails.plugins:rabbitmq-native:3.3.1"
This app runs in a cluster and I want the instances to act as workers. A message written to the exchange should go to one instance, the next message to another instance, and so on.
I can bind consumers to queues using a static block in each consumer:
static rabbitConfig = [
"queue": "my.queue.that.is.bound.to.some.exchange"
]
Or I can bind them in application.yml:
rabbitmq:
exchanges:
- name: some.exchange
type: fanout
queues:
- name: my.queue.that.is.bound.to.some.exchange
exchange: some.exchange
consumers:
MyConsumer:
queue: my.queue.that.is.bound.to.some.exchange
But when I map consumers to queues in application.yml, the consumer is not consuming messages on the queue. I managed to dump the RabbitMQ status report, which shows the consumer is stopped:
[
{
"consumers":
[
{
"fullName": "MyConsumer",
"load": 0.0,
"name": "MyConsumer",
"numConfigured": 1,
"numConsuming": 1,
"numProcessing": 0,
"queue": "my.queue.that.is.bound.to.some.exchange",
"runningState": {
"enumType": "com.budjb.rabbitmq.RunningState",
"name": "STOPPED"
}
}
],
"host": "localhost",
"name": "35d07d1d-9cdc-460f-a63d-da24eb72b479",
"port": 5672,
"runningState": {
"enumType": "com.budjb.rabbitmq.RunningState",
"name": "RUNNING"
},
"virtualHost": "/"
}
]
I tried calling rabbitContext.startConsumers() or even consumerManager.start() from Bootstrap.init(), but the consumers are not populated yet (consumerManager.consumers == []), so it does nothing.
I'm trying to keep the consumer bindings in an externalized configuration, so I'd can selectively turn consumers on or off depending on context. I might turn off heavy consumers on nodes that serve web traffic, for example. It would be more awkward to do in a static initializer block in the consumer, as opposed to a configuration file.
So, how do I start my consumers when their queue binding is defined in application.yml?

I did some debugging on the plugin, and found out that when it starts the consumers it validates the configuration with this method:
#Override
boolean isValid() {
boolean valid = true
if (!queue && !exchange) {
log.warn("consumer is not valid because it has no queue nor exchange defined")
valid = false
}
if (queue && exchange) {
log.warn("consumer is not valid because is has both a queue and an exchange defined")
valid = false
}
if (binding instanceof Map && !(match in ["any", "all"])) {
log.warn("match must be either 'any' or 'all'")
valid = false
}
return valid
}
In my case, the validation was failing because my configuration didn't have the binding and match properties, which shouldn't be required for queue consumers.
Adding them with bogus values made the configuration validate and the consumers start properly and consume messages.
Not sure if this would help your case as well, but in mine it did :)

Related

Istio EnvoyFilter - Wasm - Classifying Metrics Based on Request or Response

I am trying to insert a custom dimension for an istio metric for URL path.
I am following the steps here -
https://istio.io/latest/docs/tasks/observability/metrics/classify-metrics/
Specifically, this part, where I can parse the URL and decide the value :
configuration:
"#type": type.googleapis.com/google.protobuf.StringValue
value: |
{
"attributes": [
{
"output_attribute": "istio_operationId",
"match": [
{
"value": "ListReviews",
"condition": "request.url_path == '/reviews' && request.method == 'GET'"
},
{
"value": "GetReview",
"condition": "request.url_path.matches('^/reviews/[[:alnum:]]*$') && request.method == 'GET'"
},
{
"value": "CreateReview",
"condition": "request.url_path == '/reviews/' && request.method == 'POST'"
}
]
}
]
}
I want to add a fall-back approach - i.e., if it doesn't match any URL, then make the value of istio_operationId as the original request.url_path
How can I do that?
I tried adding this as the last condition but it doesn't work
{
"value": "request.url_path", //tried without quotes as well
"condition": "request.url_path.matches('.+$')"
}
Also, is this possible in Lua?
To set a fall back value leave the condition blank. From the docs "An empty condition evaluates to true and should be used to provide a default value.".
That said, I don't think there's a way to set the attribute value to anything but a static string. What you can do instead is add the url_path as a dimension to one (or all) metrics generated by the stats filter. To say it another way, I don't think you can combine request classification with custom dimensions the way you're describing.
See this blog post for details as well as an explanation of the difference between attributes and dimensions.
Also, you may want to reconsider trying. When metrics are emitted with unbounded cardinality monitoring systems fall over. Here is a description of an Istio user crashing their Prometheus instance this way. Here is the (then) co-lead of the Istio extensions and telemetry working group discussing why they do not recommend doing this.

How do I safely grant permission for Cloud Scheduler to create a Dataflow job?

I have a Dataflow template that I can use for a Dataflow job running as a service account of my choosing. I've actually used one of Google's provided samples: gs://dataflow-templates/latest/GCS_Text_to_BigQuery.
I now want to schedule this using Cloud Scheduler. I've set up my scheduler job like so:
When the scheduler job runs it errors with PERMISSION_DENIED:
{
"insertId": "1kw7uaqg3tnzbqu",
"jsonPayload": {
"#type": "type.googleapis.com/google.cloud.scheduler.logging.AttemptFinished",
"url": "https://dataflow.googleapis.com/v1b3/projects/project-redacted/locations/europe-west2/templates:launch?gcsPath=gs%3A%2F%2Fdataflow-templates%2Flatest%2FGCS_Text_to_BigQuery",
"jobName": "projects/project-redacted/locations/europe-west2/jobs/aaa-schedule-dataflow-job",
"status": "PERMISSION_DENIED",
"targetType": "HTTP"
},
"httpRequest": {
"status": 403
},
"resource": {
"type": "cloud_scheduler_job",
"labels": {
"job_id": "aaa-schedule-dataflow-job",
"project_id": "project-redacted",
"location": "europe-west2"
}
},
"timestamp": "2021-12-16T16:41:17.349974291Z",
"severity": "ERROR",
"logName": "projects/project-redacted/logs/cloudscheduler.googleapis.com%2Fexecutions",
"receiveTimestamp": "2021-12-16T16:41:17.349974291Z"
}
I have no idea what permission is missing or what I need to grant in order to make this work and am hoping someone here can help me.
In order to reproduce the problem I have built a terraform configuration that creates the Dataflow job from the template along with all of its prerequisites and it executes successfully.
In that same terraform configuration I have created a Cloud Scheduler job that purports to execute an identical Dataflow job and it is that which fails with the error given above.
All this code is available at https://github.com/jamiet-msm/dataflow-scheduler-permission-problem/tree/6ef20824af0ec798634c146ee9073b4b40c965e0 and I have created a README that explains how to run it:
I figured it out, the service account needs to be granted roles/iam.serviceAccountUser on itself
resource "google_service_account_iam_member" "sa_may_act_as_itself" {
service_account_id = google_service_account.sa.name
role = "roles/iam.serviceAccountUser"
member = "serviceAccount:${google_service_account.sa.email}"
}
and roles/dataflow.admin is required also, roles/dataflow.worker isn't enough. I assume that's because dataflow.jobs.create is required which is not provided by roles/dataflow.worker (see https://cloud.google.com/dataflow/docs/concepts/access-control#roles for reference)
resource "google_project_iam_member" "df_admin" {
role = "roles/dataflow.admin"
member = "serviceAccount:${google_service_account.sa.email}"
}
Here is the commit with the required changes: https://github.com/jamiet-msm/dataflow-scheduler-permission-problem/commit/3fd7cabdf13d5465e01a928049f54b0bd486ed73

Why is the exact difference between "violation" and "deny" in OPA/Rego?

In Open Policy Agent (https://www.openpolicyagent.org/)
regarding to Kubernetes, depending which engine is used:
Gatekeeper: https://github.com/open-policy-agent/gatekeeper
OR
Plain OPA with kube-mgmt: https://www.openpolicyagent.org/docs/latest/kubernetes-introduction/#how-does-it-work-with-plain-opa-and-kube-mgmt
There are different ways to define validation rules:
In Gatekeeper the violation is used. See sample rules here: https://github.com/open-policy-agent/gatekeeper-library/tree/master/library/general
In plain OPA samples, the deny rule, see sample here:
https://www.openpolicyagent.org/docs/latest/kubernetes-introduction/#how-does-it-work-with-plain-opa-and-kube-mgmt
It seems to be the OPA constraint framework defines it as violation:
https://github.com/open-policy-agent/frameworks/tree/master/constraint#rule-schema
So what is the exact "story" behind this, why it is not consistent between the different engines?
Notes:
This doc reflects on this: https://www.openshift.com/blog/better-kubernetes-security-with-open-policy-agent-opa-part-2
Here is mentioned how to support interoperability in the script: https://github.com/open-policy-agent/gatekeeper/issues/1168#issuecomment-794759747
https://github.com/open-policy-agent/gatekeeper/issues/168 In this issue is the migration mentioned, is just because of "dry run" support?.
Plain OPA has no opinion on how you choose to name your rules. Using deny is just a convention in the tutorial. The real Kubernetes admission review response is going to look something like this:
{
"kind": "AdmissionReview",
"apiVersion": "admission.k8s.io/v1beta1",
"response": {
"allowed": false,
"status": {
"reason": "container image refers to illegal registry (must be hooli.com)"
}
}
}
So whatever you choose to name your rules the response will need to be transformed into a response like the above before it's sent back to the Kubernetes API server. If you scroll down a bit in the Detailed Admission Control Flow section of the Kubernetes primer docs, you'll see how this transformation is accomplished in the system.main rule:
package system
import data.kubernetes.admission
main = {
"apiVersion": "admission.k8s.io/v1beta1",
"kind": "AdmissionReview",
"response": response,
}
default response = {"allowed": true}
response = {
"allowed": false,
"status": {
"reason": reason,
},
} {
reason = concat(", ", admission.deny)
reason != ""
}
Note in particular how the "reason" attribute is just built by concatenating all the strings found in admission.deny:
reason = concat(", ", admission.deny)
If you'd rather use violation or some other rule name using plain OPA, this is where you would change it.

Netflix Conductor SQS

Has anyone successfully integrated Netflix Conductor with AWS SQS?
I have tried below steps but the workflow is not triggered.
Create SQS queue
Added AWS creds to environment
Registered tasks, workflows and the event listener below
{
"name": "sqs_event_listener",
"event": "sqs:name_of_sqs_queue",
"condition": "true",
"active": true,
"actions": [{
"action": "start_workflow",
"start_workflow": {
"name": "mywf"
}
}]
}
I know this is too late to help the original poster, but adding a response to improve the hive mind of SO:
In your Conductor application.properties file, make sure you have the following values
conductor.default-event-queue.type=sqs
conductor.event-queues.sqs.enabled=true
conductor.event-queues.sqs.authorized-accounts=(your AWS account number)
We need to update annotations-processor/awssqs-event-queue/src/main/java/com/netflix/conductor/SQSEventQueueConfiguration.java
#Bean
AWSCredentialsProvider createAWSCredentialsProvider() {
return new DefaultAWSCredentialsProviderChain();
}
With this configuration in Conductor, you can now restart your instance, and your event should receive events from the SQS message queue.
For a full post with workflow and tasks SENDING ans RECEIVING SQS messages - check out: https://orkes.io/content/docs/how-tos/Tasks/SQS-event-task

Adding custom analyzer to elasticsearch via grails plugin

I'm trying to add a custom analyzer to elasticsearch via grails plugin. I was able to change the used analyzer to a common analyzer using "searchable" on the domain:
static searchable = {
all = [analyzer: 'snowball']
}
but cannot get it to know a costum analyzer. It is unclear how to translate the following json in the REST API to a groovy closue:
PUT /my_index
{
"settings": {
"analysis": {
"filter": {
"my_synonym_filter": {
"type": "synonym",
"synonyms": [
"british,english",
"queen,monarch"
]
}
},
"analyzer": {
"my_synonyms": {
"tokenizer": "standard",
"filter": [
"lowercase",
"my_synonym_filter"
]
}
}
}
}
}
this question seems to have the same problem but the answer doesn't work, and this answer suggests that it might not be possible, but that doesn't seem reasonable because setting a custom analyzer is pretty basic.
Any suggestions?
There are two ways I see which would help you achieve that.
The first way is by going through the low level API using the injected elasticSearchHelper and accessing ES client directly.
elasticSearchHelper.withElasticSearch { client ->
// Do some stuff with the ElasticSearch client
client.admin()
.indices()
.prepareCreate(indexName)
.setSettings(settings) <--- your settings/analyzers go here
.execute()
.actionGet()
}
A second way involves using an undocumented feature of the ElasticSearchAdminService service, namely the createIndex() method, which allows you to pass in the settings and analyzers you need when creating a new index. The latter basically does exactly the same as the first option above, but you get to use the Grails service directly.

Resources