From e9e8b118deb54092a2b53ca7b1c29528a538e3c0 Mon Sep 17 00:00:00 2001 From: Lee Wright <258036@NTTDATA.COM> Date: Tue, 21 May 2024 13:17:06 -0700 Subject: [PATCH] Merge and updates for kafka connect --- .../workflows/build-push-kafka-connectors.yml | 75 +++++ backend/ApprovalFlow/Startup.cs | 1 - .../DIAMConfiguration.csproj | 6 +- backend/common/Common.csproj | 21 +- backend/jumwebapi/jumwebapi.csproj | 4 +- backend/service.edt/edt.service.csproj | 2 +- .../plr-intake.tests.csproj | 6 +- backend/webapi.tests/pidp.tests.csproj | 6 +- backend/webapi/pidp.csproj | 4 +- integration/kafka-connect/Dockerfile | 4 + .../plugins/amazon-sqs-connector/README.md | 277 ++++++++++++++++++ .../kafka-connect-sqs-1.6.1.jar | Bin 0 -> 34088 bytes 12 files changed, 380 insertions(+), 26 deletions(-) create mode 100644 .github/workflows/build-push-kafka-connectors.yml create mode 100644 integration/kafka-connect/Dockerfile create mode 100644 integration/kafka-connect/plugins/amazon-sqs-connector/README.md create mode 100644 integration/kafka-connect/plugins/amazon-sqs-connector/kafka-connect-sqs-1.6.1.jar diff --git a/.github/workflows/build-push-kafka-connectors.yml b/.github/workflows/build-push-kafka-connectors.yml new file mode 100644 index 00000000..8e086195 --- /dev/null +++ b/.github/workflows/build-push-kafka-connectors.yml @@ -0,0 +1,75 @@ +name: diam-kafka-connectors + +on: + push: + branches: [develop,test] + paths: + - "integration/kafka-connect/**" + - ".github/workflows/build-push-kafka-connectors.yml" + workflow_dispatch: +env: + IMAGE_NAME: diam-kafka-connectors + WORKING_DIRECTORY: ./integration/kafka-connect + BRANCH_NAME: develop + VALUES_FILE: dev + + +jobs: + build: + runs-on: ubuntu-latest + if: github.event_name == 'push' + + steps: + - uses: actions/checkout@v3 + + - name: Set environment for branch + run: | + if [[ ${{ github.ref_name }} == 'main' ]]; then + echo "BRANCH_NAME=main" >> "$GITHUB_ENV" + echo "VALUES_FILE=prod" >> "$GITHUB_ENV" + fi + + if [[ ${{ github.ref_name }} == 'test' ]]; then + echo "BRANCH_NAME=test" >> "$GITHUB_ENV" + echo "VALUES_FILE=test" >> "$GITHUB_ENV" + fi + + if [[ ${{ github.ref_name }} == 'develop' ]]; then + echo "BRANCH_NAME=develop" >> "$GITHUB_ENV" + echo "VALUES_FILE=dev" >> "$GITHUB_ENV" + fi + + - name: Extract branch name + shell: bash + run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT + id: extract_branch + + - name: Branch name + run: echo running on branch ${GITHUB_REF##*/} + + - name: Branch name + run: echo running on branch ${GITHUB_REF##*/} + + - name: Login to Artifactory + uses: docker/login-action@v1 + with: + registry: artifacts.developer.gov.bc.ca + username: ${{ secrets.ARTIFACTORY_USERNAME }} + password: ${{ secrets.ARTIFACTORY_PASSWORD }} + + # Get SHORT_SHA to tag images + - name: Get short SHA + id: short_sha + run: | + echo "::set-output name=SHORT_SHA::$(git rev-parse --short HEAD)" + echo "Short SHA: $SHORT_SHA" + + - name: Build Image + working-directory: ${{env.WORKING_DIRECTORY}} + run: | + docker build -t artifacts.developer.gov.bc.ca/de27-general-docker/${{env.IMAGE_NAME}}:${GITHUB_REF##*/} . + + - name: Docker Push to Artifactory + id: publish + run: | + docker push artifacts.developer.gov.bc.ca/de27-general-docker/${{env.IMAGE_NAME}}:${GITHUB_REF##*/} \ No newline at end of file diff --git a/backend/ApprovalFlow/Startup.cs b/backend/ApprovalFlow/Startup.cs index b2fcacb1..e2747715 100644 --- a/backend/ApprovalFlow/Startup.cs +++ b/backend/ApprovalFlow/Startup.cs @@ -101,7 +101,6 @@ public void ConfigureServices(IServiceCollection services) } - services .AddAutoMapper(typeof(Startup)) .AddKafkaConsumer(config) diff --git a/backend/DIAMConfiguration/DIAMConfiguration.csproj b/backend/DIAMConfiguration/DIAMConfiguration.csproj index 49ded8df..be2211aa 100644 --- a/backend/DIAMConfiguration/DIAMConfiguration.csproj +++ b/backend/DIAMConfiguration/DIAMConfiguration.csproj @@ -13,9 +13,9 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/backend/common/Common.csproj b/backend/common/Common.csproj index ba2ca7d6..55e056dc 100644 --- a/backend/common/Common.csproj +++ b/backend/common/Common.csproj @@ -9,14 +9,13 @@ - + - + - + - - + @@ -29,20 +28,20 @@ - - + + runtime; build; native; contentfiles; analyzers; buildtransitive - + - - + + - + diff --git a/backend/jumwebapi/jumwebapi.csproj b/backend/jumwebapi/jumwebapi.csproj index b6523a19..8735a59d 100644 --- a/backend/jumwebapi/jumwebapi.csproj +++ b/backend/jumwebapi/jumwebapi.csproj @@ -27,11 +27,11 @@ - + - + diff --git a/backend/service.edt/edt.service.csproj b/backend/service.edt/edt.service.csproj index 68f57830..80895188 100644 --- a/backend/service.edt/edt.service.csproj +++ b/backend/service.edt/edt.service.csproj @@ -14,7 +14,7 @@ - + diff --git a/backend/services.plr-intake.tests/plr-intake.tests.csproj b/backend/services.plr-intake.tests/plr-intake.tests.csproj index 3a1cddb5..3de2e3b1 100644 --- a/backend/services.plr-intake.tests/plr-intake.tests.csproj +++ b/backend/services.plr-intake.tests/plr-intake.tests.csproj @@ -11,9 +11,9 @@ - - - + + + diff --git a/backend/webapi.tests/pidp.tests.csproj b/backend/webapi.tests/pidp.tests.csproj index 24c49275..090c64ff 100644 --- a/backend/webapi.tests/pidp.tests.csproj +++ b/backend/webapi.tests/pidp.tests.csproj @@ -10,9 +10,9 @@ - - - + + + diff --git a/backend/webapi/pidp.csproj b/backend/webapi/pidp.csproj index b20e0e12..3bcb571f 100644 --- a/backend/webapi/pidp.csproj +++ b/backend/webapi/pidp.csproj @@ -14,8 +14,8 @@ - - + + diff --git a/integration/kafka-connect/Dockerfile b/integration/kafka-connect/Dockerfile new file mode 100644 index 00000000..5cdb8894 --- /dev/null +++ b/integration/kafka-connect/Dockerfile @@ -0,0 +1,4 @@ +FROM registry.redhat.io/amq7/amq-streams-kafka-32-rhel8:2.2.0-7 +USER root:root +COPY ./plugins/ /opt/kafka/plugins/ +USER 1001 \ No newline at end of file diff --git a/integration/kafka-connect/plugins/amazon-sqs-connector/README.md b/integration/kafka-connect/plugins/amazon-sqs-connector/README.md new file mode 100644 index 00000000..ca403cc1 --- /dev/null +++ b/integration/kafka-connect/plugins/amazon-sqs-connector/README.md @@ -0,0 +1,277 @@ +# kafka-connect-sqs + +The SQS connector plugin provides the ability to use AWS SQS queues as both a source (from an SQS queue into a Kafka topic) or sink (out of a Kafka topic into an SQS queue). + +## Compatibility matrix + +|Connector version|Kafka Connect API|AWS SDK| +|:---|:---|:---| +|1.4|3.1.1|1.12.241| +|1.5|3.3.2|1.12.409| +|1.6|3.4.1|1.12.669| + +Running the connector on versions of Kafka Connect prior to 3.0 is not recommended. + +## Building the distributable + +You can build the connector with Maven using the standard lifecycle goals: +``` +mvn clean +mvn package +``` + +## Source connector + +SQS source connector reads from an AWS SQS queue and publishes to a Kafka topic. + +Required properties: +* `topics`: Kafka topic to be written to. +* `sqs.queue.url`: URL of the SQS queue to be read from. + +Optional properties: +* `sqs.region`: AWS region of the SQS queue to be read from. +* `sqs.endpoint.url`: Override value for the AWS region specific endpoint. +* `sqs.max.messages`: Maximum number of messages to read from SQS queue for each poll interval. Range is 0 - 10 with default of 1. +* `sqs.wait.time.seconds`: Duration (in seconds) to wait for a message to arrive in the queue. Default is 1. +* `sqs.message.attributes.enabled`: If true, it gets the SQS MessageAttributes and inserts them as Kafka Headers (only string headers are currently supported). Default is false. +* `sqs.message.attributes.include.list`: The comma separated list of MessageAttribute names to be included, if empty it includes all the Message Attributes. Default is the empty string. +* `sqs.message.attributes.partition.key`: The name of a single AWS SQS MessageAttribute to use as the partition key. If this is not specified, default to the SQS message ID as the partition key. + +### Sample connector configuration + +```json +{ + "config": { + "connector.class": "com.nordstrom.kafka.connect.sqs.SqsSourceConnector", + "key.converter": "org.apache.kafka.connect.storage.StringConverter", + "name": "my-sqs-source", + "sqs.max.messages": "5", + "sqs.queue.url": "https://sqs..amazonaws.com//my-queue", + "sqs.wait.time.seconds": "5", + "topics": "my-topic", + "value.converter": "org.apache.kafka.connect.storage.StringConverter" + }, + "name": "my-sqs-source" +} +``` + +### Sample IAM policy + +Ensure the authentication principal has privileges to read messages from the SQS queue. + +```json +{ + "Version": "2012-10-17", + "Statement": [{ + "Sid": "kafka-connect-sqs-source", + "Effect": "Allow", + "Action": [ + "sqs:DeleteMessage", + "sqs:GetQueueUrl", + "sqs:ListQueues", + "sqs:ReceiveMessage" + ], + "Resource": "arn:aws:sqs:*:*:*" + }] +} +``` + +## Sink connector + +SQS sink connector reads from a Kafka topic and publishes to an AWS SQS queue. + +Required properties: +* `topics`: Kafka topic to be read from. +* `sqs.queue.url`: URL of the SQS queue to be written to. + +Optional properties: +* `sqs.region`: AWS region of the SQS queue to be written to. +* `sqs.endpoint.url`: Override value for the AWS region specific endpoint. +* `sqs.message.attributes.enabled`: If true, it gets the Kafka Headers and inserts them as SQS MessageAttributes (only string headers are currently supported). Default is false. +* `sqs.message.attributes.include.list`: The comma separated list of Header names to be included, if empty it includes all the Headers. Default is the empty string. + +### Sample connector configuration + +```json +{ + "config": { + "connector.class": "com.nordstrom.kafka.connect.sqs.SqsSinkConnector", + "key.converter": "org.apache.kafka.connect.storage.StringConverter", + "name": "my-sqs-sink", + "sqs.queue.url": "https://sqs..amazonaws.com//my-queue", + "sqs.region": "", + "topics": "my-topic", + "value.converter": "org.apache.kafka.connect.storage.StringConverter" + }, + "name": "my-sqs-sink" +} +``` + +### Sample SQS queue policy + +Define a corresponding SQS queue policy that allows the connector to send messages to the SQS queue: + +```json +{ + "Version": "2012-10-17", + "Id": "arn:aws:sqs:us-west-2::my-queue/SQSDefaultPolicy", + "Statement": [ + { + "Sid": "kafka-connect-sqs-sink", + "Effect": "Allow", + "Principal": { + "AWS": "" + }, + "Action": "sqs:SendMessage", + "Resource": "arn:aws:sqs:us-west-2::my-queue" + } + ] +} +``` + +### Sample IAM policy + +Ensure the authentication principal has privileges to send messages to the SQS queue. + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "kafka-connect-sqs-sink", + "Effect": "Allow", + "Action": [ + "sqs:SendMessage" + ], + "Resource": "arn:aws:sqs:*:*:*" + } + ] +} +``` + +## AWS authentication + +By default, the connector uses the AWS SDK `DefaultAWSCredentialsProviderChain` to determine the +identity of the connector. This works well in simple scenarios when the connector gains privileges +granted to the Kafka Connect worker (i.e., environment variables, EC2 instance metadata, etc.) + +When the identity of the connector must be separate from the worker, supply an implementation of +`sqs.credentials.provider.class` in the worker's classpath. There are two implementations directly +included within this library: + +- `com.nordstrom.kafka.connect.auth.AWSUserCredentialsProvider` +- `com.nordstrom.kafka.connect.auth.AWSAssumeRoleCredentialsProvider` + +### AWSUserCredentialsProvider + +Use this credentials provider to cause the connector to authenticate as a specific IAM user. + +Required properties: +* `sqs.credentials.provider.class`: Must be `com.nordstrom.kafka.connect.auth.AWSUserCredentialsProvider` +* `sqs.credentials.provider.accessKeyId`: AWS access key of the IAM user +* `sqs.credentials.provider.secretKey`: AWS secret key of the IAM user + +### AWSAssumeRoleCredentialsProvider + +Use this credentials provider to cause the connector to assume an IAM role. + +Required properties: +* `sqs.credentials.provider.class`: Must be `com.nordstrom.kafka.connect.auth.AWSAssumeRoleCredentialsProvider` +* `sqs.credentials.provider.role.arn`: ARN of the IAM role to assume +* `sqs.credentials.provider.session.name`: A session name specific to this connector + +Optional properties: +* `sqs.credentials.provider.external.id`: An [external identifier](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user_externalid.html) used when assuming the role + +The IAM role will have a corresponding trust policy. For example: + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam:::root" + }, + "Action": "sts:AssumeRole", + "Condition": { + "StringEquals": { + "sts:ExternalId": "my-external-id" + } + } + } + ] +} +``` + +## Running the demo + +### Build the connector plugin + +Build the connector jar file: + +```shell +mvn clean package +``` + +### Run the connector using Docker Compose + +Ensure you have `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` environment variables exported in your shell. Docker Compose will pass these values into the `connect` container. + +Use the provided [Docker Compose](https://docs.docker.com/compose) file and run `docker-compose up`. + +With the [Kafka Connect REST interface](https://docs.confluent.io/current/connect/references/restapi.html), verify the SQS sink and source connectors are installed and ready: `curl http://localhost:8083/connector-plugins`. + +### AWS + +The demo assumes you have an AWS account and valid credentials in ~/.aws/credentials as well as +setting the `AWS_PROFILE` and `AWS_REGION` to appropriate values. + +These are required so that Kafka Connect will have access to the SQS queues. + +### The flow + +We will use the AWS Console to put a message into an SQS queue. A source connector will read messages +from the queue and write the messages to a Kafka topic. A sink connector will read messages from the +topic and write to a _different_ SQS queue. + +``` + __ | s | | k | | s | +( o> chirp ---> | q | ---> | a | ---> | q | +///\ | s | | | f | | | s | +\V_/_ |_____| | |_____| | |_____| + chirps-q | chirps-t | chirped-q + | | + | | + source- sink- + connector connector +``` + +### Create AWS SQS queues + +Create `chirps-q` and `chirped-q` SQS queues using the AWS Console. Take note of the `URL:` values for each +as you will need them to configure the connectors later. + +### Create the connectors + +The `source` connector configuration is defined in `demos/sqs-source-chirps.json]`, The `sink` connector configuration +is defined in `demos/sqs-sink-chirped.json`. You will have to modify the `sqs.queue.url` parameter to reflect the +values noted when you created the queues. + +Create the connectors using the Confluent CLI: + +```shell +curl -XPOST -H 'Content-Type: application/json' http://localhost:8083/connectors -d @demos/sqs-source-chirps.json +curl -XPOST -H 'Content-Type: application/json' http://localhost:8083/connectors -d @demos/sqs-sink-chirped.json +``` + +### Send and receive messages + +Using the AWS Console (or the AWS CLI), send a message to the `chirps-q`. + +The source connector will read the message from the queue and write it to the `chirps-t` Kafka topic. + +The `sink` connector will read the message from the topic and write it to the `chirped-q` queue. + +Use the AWS Console (or the AWS CLI) to read your message from the `chirped-q` diff --git a/integration/kafka-connect/plugins/amazon-sqs-connector/kafka-connect-sqs-1.6.1.jar b/integration/kafka-connect/plugins/amazon-sqs-connector/kafka-connect-sqs-1.6.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..7e8e75ad8d2cb7f58765844687cd9c98d9dc8cd7 GIT binary patch literal 34088 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