From 0d8b112e5d4e544229689323e34014583188b883 Mon Sep 17 00:00:00 2001 From: Abhisek Dwivedi Date: Thu, 5 Oct 2023 19:15:38 +0530 Subject: [PATCH] Added aerospike-jms-inbound connector helm-chart (#7) * Added aerospike-jms-inbound connector helm-chart --- aerospike-jms-inbound/.helmignore | 23 ++ aerospike-jms-inbound/Chart.yaml | 19 ++ aerospike-jms-inbound/README.md | 298 ++++++++++++++++++ .../examples/clear-text/README.md | 70 ++++ .../examples/clear-text/aerospike.yaml | 53 ++++ .../clear-text/as-jms-inbound-values.yaml | 69 ++++ .../examples/custom-code-plugin/README.md | 72 +++++ .../custom-code-plugin/aerospike.yaml | 53 ++++ .../as-jms-inbound-values.yaml | 75 +++++ aerospike-jms-inbound/examples/tls/README.md | 77 +++++ .../examples/tls/aerospike.yaml | 75 +++++ .../tls/as-jms-inbound-tls-values.yaml | 80 +++++ .../examples/tls/tls-certs/storepass | 1 + .../tls/tls-certs/svc.cluster.local.crt | 27 ++ .../tls/tls-certs/svc.cluster.local.key | 32 ++ .../tls-certs/svc.cluster.local.keycert.pem | 59 ++++ .../tls-certs/svc.cluster.local.keystore.jks | Bin 0 -> 2308 bytes .../svc.cluster.local.keystore.pkcs12 | Bin 0 -> 2814 bytes .../svc.cluster.local.truststore.jks | Bin 0 -> 1018 bytes aerospike-jms-inbound/templates/NOTES.txt | 3 + aerospike-jms-inbound/templates/_helpers.tpl | 84 +++++ .../templates/configmap.yaml | 10 + .../templates/deployment.yaml | 104 ++++++ aerospike-jms-inbound/templates/hpa.yaml | 28 ++ .../templates/serviceaccount.yaml | 12 + aerospike-jms-inbound/values.yaml | 147 +++++++++ docs/aerospike-jms-inbound-1.0.0.tgz | Bin 0 -> 17955 bytes docs/index.yaml | 22 +- 28 files changed, 1492 insertions(+), 1 deletion(-) create mode 100644 aerospike-jms-inbound/.helmignore create mode 100644 aerospike-jms-inbound/Chart.yaml create mode 100644 aerospike-jms-inbound/README.md create mode 100644 aerospike-jms-inbound/examples/clear-text/README.md create mode 100644 aerospike-jms-inbound/examples/clear-text/aerospike.yaml create mode 100644 aerospike-jms-inbound/examples/clear-text/as-jms-inbound-values.yaml create mode 100644 aerospike-jms-inbound/examples/custom-code-plugin/README.md create mode 100644 aerospike-jms-inbound/examples/custom-code-plugin/aerospike.yaml create mode 100644 aerospike-jms-inbound/examples/custom-code-plugin/as-jms-inbound-values.yaml create mode 100644 aerospike-jms-inbound/examples/tls/README.md create mode 100644 aerospike-jms-inbound/examples/tls/aerospike.yaml create mode 100644 aerospike-jms-inbound/examples/tls/as-jms-inbound-tls-values.yaml create mode 100644 aerospike-jms-inbound/examples/tls/tls-certs/storepass create mode 100644 aerospike-jms-inbound/examples/tls/tls-certs/svc.cluster.local.crt create mode 100644 aerospike-jms-inbound/examples/tls/tls-certs/svc.cluster.local.key create mode 100644 aerospike-jms-inbound/examples/tls/tls-certs/svc.cluster.local.keycert.pem create mode 100644 aerospike-jms-inbound/examples/tls/tls-certs/svc.cluster.local.keystore.jks create mode 100644 aerospike-jms-inbound/examples/tls/tls-certs/svc.cluster.local.keystore.pkcs12 create mode 100644 aerospike-jms-inbound/examples/tls/tls-certs/svc.cluster.local.truststore.jks create mode 100644 aerospike-jms-inbound/templates/NOTES.txt create mode 100644 aerospike-jms-inbound/templates/_helpers.tpl create mode 100644 aerospike-jms-inbound/templates/configmap.yaml create mode 100644 aerospike-jms-inbound/templates/deployment.yaml create mode 100644 aerospike-jms-inbound/templates/hpa.yaml create mode 100644 aerospike-jms-inbound/templates/serviceaccount.yaml create mode 100644 aerospike-jms-inbound/values.yaml create mode 100644 docs/aerospike-jms-inbound-1.0.0.tgz diff --git a/aerospike-jms-inbound/.helmignore b/aerospike-jms-inbound/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/aerospike-jms-inbound/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/aerospike-jms-inbound/Chart.yaml b/aerospike-jms-inbound/Chart.yaml new file mode 100644 index 0000000..b494c38 --- /dev/null +++ b/aerospike-jms-inbound/Chart.yaml @@ -0,0 +1,19 @@ +apiVersion: v2 +name: aerospike-jms-inbound +description: A Helm chart for Aerospike Connect for JMS - Inbound +type: application +icon: https://avatars0.githubusercontent.com/u/2214313?s=200&v=4 +keywords: + - Aerospike + - JMS + - Connector + - Inbound +maintainers: + - name: Aerospike + email: developers@aerospike.com + +# The helm chart version +version: 1.0.0 + +# The default version of Aerospike JMS Inbound Connector +appVersion: "3.0.0" diff --git a/aerospike-jms-inbound/README.md b/aerospike-jms-inbound/README.md new file mode 100644 index 0000000..93a4d50 --- /dev/null +++ b/aerospike-jms-inbound/README.md @@ -0,0 +1,298 @@ +# Aerospike JMS Inbound Connector + +This Helm chart allows you to configure and run our official [Aerospike JMS Inbound Connector](https://hub.docker.com/repository/docker/aerospike/aerospike-jms-inbound) +docker image on a Kubernetes cluster. + +This helm chart sets up a `Deployment` for JMS inbound connector deployment. + +**_NOTE:_** The helm chart appends `-aerospike-jms-inbound` suffix to all created Kubernetes resources to prevent name clashes with other applications. + +## Prerequisites +- Kubernetes cluster +- Helm v3 +- A JMS cluster with brokers reachable from the pods in the Kubernetes cluster +- An Aerospike cluster reachable from the connectors pods in the Kubernetes cluster. + The Aerospike cluster can be deployed in the same Kubernetes cluster using [Aerospike + Kubernetes Operator](https://docs.aerospike.com/cloud/kubernetes/operator) + +## Adding the helm chart repository + +Add the `aerospike` helm repository if not already done + +```shell +helm repo add aerospike https://aerospike.github.io/helm-charts +``` + +### Supported configuration + +### Configuration + +| Parameter | Description | Default | +|----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------| +| `replicaCount` | Configures the number Aerospike JMS connector pods to run. | '1' | +| `image` | Configures Aerospike JMS connector image repository, tag and pull policy. | see [values.yaml](values.yaml) | +| `connectorConfig` | Connector configuration deployed to `/etc/aerospike-jms-inbound/aerospike-jms-inbound.yml`. | see [values.yaml](values.yaml) | +| `connectorSecrets` | List of secrets mounted to `/etc/aerospike-jms-inbound/secrets` for each connector pod. | `[]` | +| `initContainers` | List of initContainers added to each connector pods for custom code plugin jars. | `[]` | +| `serviceAccount` | Service Account details like name and annotations. | see [values.yaml](values.yaml) | +| `podAnnotations` | Additional pod [annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/). Should be specified as a map of annotation names to annotation values. | `{}` | +| `podSecurityContext` | Pod [security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/) | `{}` | +| `securityContext` | Container [security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) | `{}` | +| `resources` | Resource [requests and limits](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) for the connector pods. | `{}` | +| `autoscaling` | Enable the horizontal pod auto-scaler. | see [values.yaml](values.yaml) | +| `affinity` | [Affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) rules if any for the pods. | `{}` | +| `nodeSelector` | [Node selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) for the pods. | `{}` | +| `tolerations` | [Tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) for the pods. | `{}` | + +## Deploy the connectors + +We recommend creating a new `.yaml` for providing configuration values to the helm chart for deployment. +See the [examples](examples) folder for examples. + +A sample values yaml file is shown below: + +```yaml + +replicaCount: 3 + +image: + tag: "3.0.0" + +connectorConfig: + # Optional HTTP Server configuration to expose Manage API and Prometheus metrics + service: + manage: + port: 8081 + + # JMS sources to consume messages from. + queues: + MyJmsQueue: + aerospike-operation: + type: write + parsing: + format: json + mapping: + bins: + type: multi-bins + all-value-fields: true + key-field: + source: value-field + field-name: key + namespace: + mode: static + value: test + + # topics: {} + + # The connection properties to the JMS message broker. + jms: + # # RabbitMQ example. + factory: com.rabbitmq.jms.admin.RMQConnectionFactory + config: + host: rabbitmq.rabbitmq-system + port: 5672 + username: guest + password: guest + + # # ActiveMQ example. + # factory: org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory + # jndi-cf-name: ConnectionFactory + # config: + # java.naming.provider.url: tcp://127.0.0.1:61616 + # java.naming.security.principal: admin + # java.naming.security.credentials: password + + # # IBM MQ example. + # factory: com.ibm.mq.jms.MQConnectionFactory + # config: + # hostName: 127.0.0.1 + # port: 1414 + # queueManager: QM1 + # transportType: 1 + # channel: DEV.APP.SVRCONN + + # The Aerospike cluster connection properties. + aerospike: + seeds: + - aerocluster.aerospike: # Aerospike Headless Service name or seed node address. + port: 3000 + + # The logging properties. + logging: + enable-console-logging: true +``` + +Here `replicaCount` is the count of connectors pods that are deployed. +The connector configuration is provided as yaml under the key `connectorConfig`. +See [Aerospike JMS Inbound configuration](https://docs.aerospike.com/connect/jms/to-asdb/configuring) for details. + +Update the `jms` configuration to point to you JMS cluster endpoints and update `queues` and `topics` to point to the JMS queues and topics you want to consume messages from. +Also, update the `aerospike` configuration to point to your Aerospike cluster. + +We recommend naming the file with the name of the connector cluster. For example if you want to name your connector cluster as +`as-jms-inbound`, create a file `as-jms-inbound-values.yaml`. +Once you have created this custom values file, deploy the connectors, using the following command. + +## Configure connector to write to Aerospike cluster + +Aerospike cluster Pod DNS names or service name can be used directly if the Aerospike cluster is also running in the same Kubernetes cluster. + +To get the service name and port to be used for `aerospike` section in the connectorConfig, run the following command. + +```shell +# kubectl get svc --namespace --no-headers -o custom-columns=":metadata.name,:spec.ports[].port" \ +# | sed -e "s//./g" +kubectl get svc --namespace aerospike aerocluster --no-headers -o custom-columns=":metadata.name,:spec.ports[].port" \ + | sed -e "s/aerocluster/aerocluster.aerospike/g" +``` + +You should see output similar to the following +```shell +aerocluster.aerospike 3000 +``` + +OR + +To get the pod DNS names and ports to be used for `aerospike` section in the connectorConfig, run the following command. + +```shell +# kubectl get pods --namespace --selector=aerospike.com/cr= --no-headers -o custom-columns=":metadata.name" \ +# | sed -e "s/$/. /g" +kubectl get pods --namespace aerospike --selector=aerospike.com/cr=aerocluster --no-headers -o custom-columns=":metadata.name" \ + | sed -e "s/$/.aerospike 3000/g" +``` + +You should see output similar to the following +```shell +aerocluster-0-0.aerospike 3000 +aerocluster-0-1.aerospike 3000 +``` + +If you are using [Aerospike Kubernetes Operator](https://docs.aerospike.com/connect/jms/to-asdb/configuring), +see [clear text](examples/clear-text) and [tls](examples/tls) for reference. + +### Create a new namespace +We recommend using `aerospike` namespace for the connector cluster. If the namespace does not exist run the following command: +```shell +kubectl create namespace aerospike +``` + +### Create secrets + +You can create additional secrets, for confidential data like TLS certificates, that are mounted to the connector pods. +The connector can then be configured to use these secrets. See [examples/tls](examples/tls) for example. + +### Deploy the connector cluster + +```shell +# helm install --namespace -f aerospike/aerospike-jms-inbound +helm install --namespace aerospike as-jms-inbound -f as-jms-inbound-values.yaml aerospike/aerospike-jms-inbound +``` + +Here `as-jms-inbound` is the release name for the connector cluster and also its cluster name. + +On successful deployment you should see output similar to below: + +```shell +AME: as-jms-inbound +LAST DEPLOYED: Mon Oct 17 20:44:34 2022 +NAMESPACE: aerospike +STATUS: deployed +REVISION: 1 +NOTES: +1. Get the list of connector pods by running the command: + +kubectl get pods --namespace aerospike --selector=app=as-jms-inbound-aerospike-jms-inbound --no-headers -o custom-columns=":metadata.name" +``` + +## List pods for the connector +To list the pods for the connector run the following command: +```shell +# kubectl get pods --namespace aerospike --selector=app=-aerospike-jms-inbound +kubectl get pods --namespace aerospike --selector=app=as-jms-inbound-aerospike-jms-inbound +``` + +You should see output similar to the following: +```shell +NAME READY STATUS RESTARTS AGE +as-jms-inbound-aerospike-jms-inbound-5c448c9945-7hgkc 1/1 Running 0 37m +as-jms-inbound-aerospike-jms-inbound-5c448c9945-jxtsh 1/1 Running 0 38m +as-jms-inbound-aerospike-jms-inbound-5c448c9945-nh2gd 1/1 Running 0 38m +``` + +## Get logs for all connector instances + +```shell +# kubectl -n aerospike logs -f deployment/-aerospike-jms-inbound +# Skip the -f flag to get a one time dump of the log +kubectl -n aerospike logs -f deployment/as-jms-inbound-aerospike-jms-inbound +``` + +## Get logs for one connector pod + +```shell +# kubectl -n aerospike logs -f +# Skip the -f flag to get a one time dump of the log +kubectl -n aerospike logs -f as-jms-inbound-aerospike-jms-inbound-5c448c9945-7hgkc +``` + +## Updating connector configuration + +Edit the `connectorConfig` section in the custom values file and save the changes. + +Upgrade the connector deployment using the following command. + +```shell +#helm upgrade --namespace -f aerospike/aerospike-jms-inbound +helm upgrade --namespace aerospike as-jms-inbound -f as-jms-inbound-values.yaml aerospike/aerospike-jms-inbound +``` + +On successful execution of the command the connector pods will undergo a rolling restart and come up with the new configuration. + +To verify the changes are applied + - [List the pods](#list-pods-for-the-connector) + - [Verify the configuration in connector logs](#get-logs-for-all-connector-instances) + +**_NOTE:_** The changes might take some time to apply. If you do not see the desired connector config try again after some time. +If connector pods are not being listed or report status as crashed see [troubleshooting](#troubleshooting). + +## Scaling up/down the connectors + +Edit the `replicaCount` to the desired connector count and upgrade the connector deployment using the following command. + +```shell +#helm upgrade --namespace -f aerospike/aerospike-jms-inbound +helm upgrade --namespace aerospike as-jms-inbound -f as-jms-inbound-values.yaml aerospike/aerospike-jms-inbound +``` + +Verify that the connectors have been scaled. + - [List the pods](#list-pods-for-the-connector) and verify the count of connectors is as desired + +**_NOTE:_** The changes might take some time to apply. If you do not see the desired count try again after some time. +If connector pods are not being listed or report status as crashed see [troubleshooting](#troubleshooting). + +## Troubleshooting + +### Connector pods not listed + +Check for any error events on the StatefulSet created for the connectors. +```shell +# kubectl -n aerospike describe deployment -aerospike-jms-inbound +kubectl -n aerospike describe deployment as-jms-inbound-aerospike-jms-inbound +``` + +### Connector pods stuck in `init` or `pending` state + +Check for any error events on the pod created for the connectors. +```shell +# kubectl -n aerospike describe pod +kubectl -n aerospike describe pod as-jms-inbound-aerospike-jms-inbound-5c448c9945-7hgkc +``` + +The most likely reason is secret listed in `connectorSecrets` has not been created in the connector namespace. + +### Connector pods in crashed state + +The most likely reason is connector configuration provided in `connectorConfig` is invalid. +Verify this by [viewing the connector logs](#get-logs-for-all-connector-instances), fix and [update](#updating-connector-configuration) +the connector configuration. diff --git a/aerospike-jms-inbound/examples/clear-text/README.md b/aerospike-jms-inbound/examples/clear-text/README.md new file mode 100644 index 0000000..d4fadf7 --- /dev/null +++ b/aerospike-jms-inbound/examples/clear-text/README.md @@ -0,0 +1,70 @@ +# Aerospike JMS Inbound clear text example + +This example deploys Aerospike JMS Inbound connectors and an Aerospike cluster without TLS configured. + +## Prerequisites + - Kubernetes cluster + - Helm v3 + - A JMS cluster with brokers reachable from the pods in the Kubernetes cluster + - An Aerospike cluster reachable from the connectors pods in the Kubernetes cluster. + The Aerospike cluster can be deployed in the same Kubernetes cluster using [Aerospike Kubernetes Operator](https://docs.aerospike.com/cloud/kubernetes/operator) + - Aerospike JMS Connector [Helm chart](../../README.md#adding-the-helm-chart-repository) + +## Clone this repository. + - A clone of this git repository + +## Deploy connectors. + +All subsequent commands are run from this directory. + +### Create a new Kubernetes namespace +Create a Kubernetes namespace if not already done +```shell +kubectl create namespace aerospike +``` + +## Deploy the Aerospike cluster +If you do not have a preexisting Aerospike server, install [Aerospike Kubernetes Operator](https://docs.aerospike.com/cloud/kubernetes/operator/install-operator). +The steps below will deploy an Aerospike cluster using Aerospike Kubernetes Operator and this [sample](aerospike.yaml) custom resource. + +### Create secrets +Create the secret for aerospike using your Aerospike licence file +```shell +kubectl -n aerospike create secret generic aerospike-secret --from-file= +``` + +### Launch the Aerospike cluster +```shell +kubectl -n aerospike create -f aerospike.yaml +``` + +### Deploy the connectors. +Update the [as-jms-inbound-values.yaml](as-jms-inbound-values.yaml) file to change `connectorConfig.jms` to consume messages from JMS cluster with broker and update `connectorConfig.aerospike` section to point to your destination Aerospike cluster. +Deploy the connectors using configuration from [as-jms-inbound-values.yaml](as-jms-inbound-values.yaml) + +```shell +helm install --namespace aerospike as-jms-inbound -f as-jms-inbound-values.yaml aerospike/aerospike-jms-inbound +``` + +## Write data to Aerospike + +You can write data into JMS queue or topic using tools/clients of your choice. An Aerospike record corresponding +to each message should show up in your Aerospike database. + +## Cleanup + +### Remove the Aerospike cluster +```shell +kubectl -n aerospike delete -f aerospike.yaml +``` + +### Remove the connectors +```shell +helm -n aerospike uninstall as-jms-inbound +``` + +### Delete the secrets +```shell +kubectl -n aerospike delete secrets aerospike-secret +``` + diff --git a/aerospike-jms-inbound/examples/clear-text/aerospike.yaml b/aerospike-jms-inbound/examples/clear-text/aerospike.yaml new file mode 100644 index 0000000..9890e94 --- /dev/null +++ b/aerospike-jms-inbound/examples/clear-text/aerospike.yaml @@ -0,0 +1,53 @@ +# This is a simple example for demonstration only, that launches an in-memory, +# non-secure Aerospike cluster, with no dependencies. +# +# This is not recommended for production use. +# +# Visit https://docs.aerospike.com/cloud/kubernetes/operator for details. +# +# Before creating this cluster, +# - Prepare the aerospike namespace by following instruction here +# +# https://docs.aerospike.com/cloud/kubernetes/operator/create-cluster-kubectl#prepare-the-namespace +apiVersion: asdb.aerospike.com/v1beta1 +kind: AerospikeCluster +metadata: + name: aerocluster + namespace: aerospike + +spec: + size: 2 + image: aerospike/aerospike-server-enterprise:6.1.0.1 + podSpec: + multiPodPerHost: true + + storage: + volumes: + - name: aerospike-secret + source: + secret: + secretName: aerospike-secret + aerospike: + path: /etc/aerospike/secrets + + validationPolicy: + skipWorkDirValidate: true + skipXdrDlogFileValidate: true + + aerospikeConfig: + service: + feature-key-file: /etc/aerospike/secrets/features.conf + network: + service: + port: 3000 + fabric: + port: 3001 + heartbeat: + port: 3002 + + namespaces: + - name: test + memory-size: 3000000000 + replication-factor: 1 + storage-engine: + type: memory diff --git a/aerospike-jms-inbound/examples/clear-text/as-jms-inbound-values.yaml b/aerospike-jms-inbound/examples/clear-text/as-jms-inbound-values.yaml new file mode 100644 index 0000000..8b3fa27 --- /dev/null +++ b/aerospike-jms-inbound/examples/clear-text/as-jms-inbound-values.yaml @@ -0,0 +1,69 @@ +replicaCount: 3 + +# Default Connector configuration. +# Visit: https://docs.aerospike.com/connect/jms/to-asdb/configuring +connectorConfig: + # Optional HTTP Server configuration to expose Manage API and Prometheus metrics + service: + manage: + port: 8081 + + # JMS sources to consume messages from. + queues: + MyJmsQueue: + aerospike-operation: + type: write + parsing: + format: json + mapping: + bins: + type: multi-bins + all-value-fields: true + key-field: + source: value-field + field-name: key + namespace: + mode: static + value: test + + # topics: {} + + # The connection properties to the JMS message broker. + jms: + # # RabbitMQ example. + factory: com.rabbitmq.jms.admin.RMQConnectionFactory + config: + host: rabbitmq.rabbitmq-system + port: 5672 + username: guest + password: guest + + # # ActiveMQ example. + # factory: org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory + # jndi-cf-name: ConnectionFactory + # config: + # java.naming.provider.url: tcp://127.0.0.1:61616 + # java.naming.security.principal: admin + # java.naming.security.credentials: password + + # # IBM MQ example. + # factory: com.ibm.mq.jms.MQConnectionFactory + # config: + # hostName: 127.0.0.1 + # port: 1414 + # queueManager: QM1 + # transportType: 1 + # channel: DEV.APP.SVRCONN + + # The Aerospike cluster connection properties. + aerospike: + seeds: + - aerocluster.aerospike: # Aerospike Headless Service name or seed node address. + port: 3000 + + # The logging properties. + logging: + enable-console-logging: true + + + diff --git a/aerospike-jms-inbound/examples/custom-code-plugin/README.md b/aerospike-jms-inbound/examples/custom-code-plugin/README.md new file mode 100644 index 0000000..fd423b9 --- /dev/null +++ b/aerospike-jms-inbound/examples/custom-code-plugin/README.md @@ -0,0 +1,72 @@ +# Aerospike JMS Inbound clear text example + +This example deploys Aerospike JMS Inbound connectors with custom code plugin and an Aerospike cluster without TLS configured. + +## Prerequisites + - Kubernetes cluster + - Helm v3 + - A JMS cluster with brokers reachable from the pods in the Kubernetes cluster + - An Aerospike cluster reachable from the connectors pods in the Kubernetes cluster. + The Aerospike cluster can be deployed in the same Kubernetes cluster using [Aerospike Kubernetes Operator](https://docs.aerospike.com/cloud/kubernetes/operator) + - Aerospike JMS Connector [Helm chart](../../README.md#adding-the-helm-chart-repository) + - A container image containing custom code plugin jars. See [Custom Code Plugin](https://docs.aerospike.com/connect/jms/to-asdb/configuring/message-transformer) for more details. + +## Clone this repository. + - A clone of this git repository + +## Deploy connectors. + +All subsequent commands are run from this directory. + +### Create a new Kubernetes namespace +Create a Kubernetes namespace if not already done +```shell +kubectl create namespace aerospike +``` + +## Deploy the Aerospike cluster +If you do not have a preexisting Aerospike server, install [Aerospike Kubernetes Operator](https://docs.aerospike.com/cloud/kubernetes/operator/install-operator). +The steps below will deploy an Aerospike cluster using Aerospike Kubernetes Operator and this [sample](aerospike.yaml) custom resource. + +### Create secrets +Create the secret for aerospike using your Aerospike licence file +```shell +kubectl -n aerospike create secret generic aerospike-secret --from-file= +``` + +### Launch the Aerospike cluster +```shell +kubectl -n aerospike create -f aerospike.yaml +``` + +### Deploy the connectors. +Update the [as-jms-inbound-values.yaml](as-jms-inbound-values.yaml) file to change `connectorConfig.jms` to consume messages from JMS cluster with broker and +update `connectorConfig.aerospike` section to point to your destination Aerospike cluster. Also, add the `initContainer` section to the file to use your custom code plugin image. + +Deploy the connectors using configuration from [as-jms-inbound-values.yaml](as-jms-inbound-values.yaml) + +```shell +helm install --namespace aerospike as-jms-inbound -f as-jms-inbound-values.yaml aerospike/aerospike-jms-inbound +``` + +## Write data to Aerospike + +You can write data into JMS queue or topic using tools/clients of your choice. An Aerospike record corresponding +to each message should show up in your Aerospike database. + +## Cleanup + +### Remove the Aerospike cluster +```shell +kubectl -n aerospike delete -f aerospike.yaml +``` + +### Remove the connectors +```shell +helm -n aerospike uninstall as-jms-inbound +``` + +### Delete the secrets +```shell +kubectl -n aerospike delete secrets aerospike-secret +``` \ No newline at end of file diff --git a/aerospike-jms-inbound/examples/custom-code-plugin/aerospike.yaml b/aerospike-jms-inbound/examples/custom-code-plugin/aerospike.yaml new file mode 100644 index 0000000..9890e94 --- /dev/null +++ b/aerospike-jms-inbound/examples/custom-code-plugin/aerospike.yaml @@ -0,0 +1,53 @@ +# This is a simple example for demonstration only, that launches an in-memory, +# non-secure Aerospike cluster, with no dependencies. +# +# This is not recommended for production use. +# +# Visit https://docs.aerospike.com/cloud/kubernetes/operator for details. +# +# Before creating this cluster, +# - Prepare the aerospike namespace by following instruction here +# +# https://docs.aerospike.com/cloud/kubernetes/operator/create-cluster-kubectl#prepare-the-namespace +apiVersion: asdb.aerospike.com/v1beta1 +kind: AerospikeCluster +metadata: + name: aerocluster + namespace: aerospike + +spec: + size: 2 + image: aerospike/aerospike-server-enterprise:6.1.0.1 + podSpec: + multiPodPerHost: true + + storage: + volumes: + - name: aerospike-secret + source: + secret: + secretName: aerospike-secret + aerospike: + path: /etc/aerospike/secrets + + validationPolicy: + skipWorkDirValidate: true + skipXdrDlogFileValidate: true + + aerospikeConfig: + service: + feature-key-file: /etc/aerospike/secrets/features.conf + network: + service: + port: 3000 + fabric: + port: 3001 + heartbeat: + port: 3002 + + namespaces: + - name: test + memory-size: 3000000000 + replication-factor: 1 + storage-engine: + type: memory diff --git a/aerospike-jms-inbound/examples/custom-code-plugin/as-jms-inbound-values.yaml b/aerospike-jms-inbound/examples/custom-code-plugin/as-jms-inbound-values.yaml new file mode 100644 index 0000000..a085d8e --- /dev/null +++ b/aerospike-jms-inbound/examples/custom-code-plugin/as-jms-inbound-values.yaml @@ -0,0 +1,75 @@ +replicaCount: 3 + +# Default Connector configuration. +# Visit: https://docs.aerospike.com/connect/jms/to-asdb/configuring +connectorConfig: + # Optional HTTP Server configuration to expose Manage API and Prometheus metrics + service: + manage: + port: 8081 + + # JMS sources to consume messages from. + queues: + MyJmsQueue: + message-transformer: + # custom message transformer class to be used for the JMS message. + class: com.aerospike.connect.jms.inbound.transformer.CompositeRecordOperationInboundMessageTransformer + unsafe-composite-record-operations: true + parsing: + format: json + + # topics: {} + + # The connection properties to the JMS message broker. + jms: + # # RabbitMQ example. + factory: com.rabbitmq.jms.admin.RMQConnectionFactory + config: + host: rabbitmq.rabbitmq-system + port: 5672 + username: guest + password: guest + + # # ActiveMQ example. + # factory: org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory + # jndi-cf-name: ConnectionFactory + # config: + # java.naming.provider.url: tcp://127.0.0.1:61616 + # java.naming.security.principal: admin + # java.naming.security.credentials: password + + # # IBM MQ example. + # factory: com.ibm.mq.jms.MQConnectionFactory + # config: + # hostName: 127.0.0.1 + # port: 1414 + # queueManager: QM1 + # transportType: 1 + # channel: DEV.APP.SVRCONN + + # The Aerospike cluster connection properties. + aerospike: + seeds: + - aerocluster.aerospike: # Aerospike Headless Service name or seed node address. + port: 3000 + + # The logging properties. + logging: + enable-console-logging: true + + +# List of initContainers to be added for each connector pod. +# Can be used to copy custom code plugins jars to /opt/aerospike-jms-inbound-init +initContainers: + - name: aerospike-jms-inbound-init + image: "aerospike/aerospike-connect-custom-transformers:1.0.0" # Image containing custom code plugins jars + imagePullPolicy: IfNotPresent + command: ["/bin/sh", "-c"] + # Update args to copy all the jars from init container to emptyDir volume path + args: + - | + set -e + cp -r /etc/aerospike-connector-custom-transformers/aerospike-jms-inbound-custom-transformers.jar /opt/aerospike-jms-inbound-init + volumeMounts: + - name: aerospike-jms-inbound-init + mountPath: /opt/aerospike-jms-inbound-init \ No newline at end of file diff --git a/aerospike-jms-inbound/examples/tls/README.md b/aerospike-jms-inbound/examples/tls/README.md new file mode 100644 index 0000000..198db51 --- /dev/null +++ b/aerospike-jms-inbound/examples/tls/README.md @@ -0,0 +1,77 @@ +# Aerospike JMS Inbound TLS example + +This example deploys Aerospike JMS Inbound connector and an Aerospike cluster with TLS configured. + +## Prerequisites + - Kubernetes cluster + - Helm v3 + - A JMS cluster with brokers reachable from the pods in the Kubernetes cluster + - An Aerospike cluster reachable from the connectors pods in the Kubernetes cluster. + The Aerospike cluster can be deployed in the same Kubernetes cluster using [Aerospike Kubernetes Operator](https://docs.aerospike.com/cloud/kubernetes/operator) + - Aerospike JMS Connector [Helm chart](../../README.md#adding-the-helm-chart-repository) + +## Clone this repository. + - A clone of this git repository + +## Deploy connectors. + +All subsequent commands are run from this directory. + +### Create a new Kubernetes namespace +Create a Kubernetes namespace if not already done +```shell +kubectl create namespace aerospike +``` + +## Deploy the Aerospike cluster +If you do not have a preexisting Aerospike server, install [Aerospike Kubernetes Operator](https://docs.aerospike.com/cloud/kubernetes/operator/install-operator). +The steps below will deploy an Aerospike cluster using Aerospike Kubernetes Operator and this [sample](aerospike.yaml) custom resource. + +### Create secrets +Create the secret for aerospike using your Aerospike licence file +```shell +kubectl -n aerospike create secret generic aerospike-secret --from-file= +``` +### Create a secret for TLS artifacts +Create a secret containing your TLS certificates and files. +There are few sample TLS certificates, keys and keystores in the [tls-certs](tls-certs) folder that the following command uses. +Use a folder with your TLS files. +```shell +kubectl -n aerospike create secret generic tls-certs --from-file=tls-certs +``` + +### Launch the Aerospike cluster +```shell +kubectl -n aerospike create -f aerospike.yaml +``` + +### Deploy the connectors. +Update the [as-jms-inbound-tls-values.yaml](as-jms-inbound-tls-values.yaml) file to change `connectorConfig` to consume messages from JMS cluster with broker and update `aerospike` section to point to your destination cluster. + +Deploy the connectors using configuration from [as-jms-inbound-tls-values.yaml](as-jms-inbound-tls-values.yaml) +```shell +helm install --namespace aerospike as-jms-inbound-tls -f as-jms-inbound-tls-values.yaml aerospike/aerospike-jms-inbound +``` + +## Write data to Aerospike + +You can write data into JMS queue or topic using tools of your choice or using your own code. An Aerospike record corresponding +to each message show up in your Aerospike database. + +## Cleanup + +### Remove the Aerospike cluster +```shell +kubectl -n aerospike delete -f aerospike.yaml +``` + +### Remove the connectors +```shell +helm -n aerospike uninstall as-jms-inbound-tls +``` + +### Delete the secrets +```shell +kubectl -n aerospike delete secrets aerospike-secret tls-certs +``` + diff --git a/aerospike-jms-inbound/examples/tls/aerospike.yaml b/aerospike-jms-inbound/examples/tls/aerospike.yaml new file mode 100644 index 0000000..a728c52 --- /dev/null +++ b/aerospike-jms-inbound/examples/tls/aerospike.yaml @@ -0,0 +1,75 @@ +# This is a simple example for demonstration only, that launches an in-memory, +# non-secure Aerospike cluster, with no dependencies. +# +# This is not recommended for production use. +# +# Visit https://docs.aerospike.com/cloud/kubernetes/operator for details. +# +# Before creating this cluster, +# - Prepare the aerospike namespace by following instruction here +# +# https://docs.aerospike.com/cloud/kubernetes/operator/create-cluster-kubectl#prepare-the-namespace +apiVersion: asdb.aerospike.com/v1beta1 +kind: AerospikeCluster +metadata: + name: aerocluster + namespace: aerospike + +spec: + size: 2 + image: aerospike/aerospike-server-enterprise:6.1.0.1 + podSpec: + multiPodPerHost: true + + storage: + volumes: + - name: aerospike-secret + source: + secret: + secretName: aerospike-secret + aerospike: + path: /etc/aerospike/secrets + - name: tls-certs + source: + secret: + secretName: tls-certs + aerospike: + path: /etc/aerospike/tls-certs + + validationPolicy: + skipWorkDirValidate: true + skipXdrDlogFileValidate: true + + operatorClientCert: + tlsClientName: aerospike.svc.cluster.local + secretCertSource: + secretName: tls-certs + caCertsFilename: svc.cluster.local.crt + clientCertFilename: svc.cluster.local.crt + clientKeyFilename: svc.cluster.local.key + + aerospikeConfig: + service: + feature-key-file: /etc/aerospike/secrets/features.conf + network: + service: + tls-name: aerospike.svc.cluster.local + tls-authenticate-client: + - aerospike.svc.cluster.local + tls-port: 4333 + fabric: + port: 3001 + heartbeat: + port: 3002 + tls: + - name: aerospike.svc.cluster.local # Subject name for which the certificate is issued for + cert-file: /etc/aerospike/tls-certs/svc.cluster.local.crt + key-file: /etc/aerospike/tls-certs/svc.cluster.local.key + ca-file: /etc/aerospike/tls-certs/svc.cluster.local.crt + + namespaces: + - name: test + memory-size: 3000000000 + replication-factor: 1 + storage-engine: + type: memory \ No newline at end of file diff --git a/aerospike-jms-inbound/examples/tls/as-jms-inbound-tls-values.yaml b/aerospike-jms-inbound/examples/tls/as-jms-inbound-tls-values.yaml new file mode 100644 index 0000000..5f93d94 --- /dev/null +++ b/aerospike-jms-inbound/examples/tls/as-jms-inbound-tls-values.yaml @@ -0,0 +1,80 @@ +replicaCount: 3 + +connectorSecrets: + - tls-certs + +connectorConfig: + # Optional HTTP Server configuration to expose Manage API and Prometheus metrics + service: + manage: + port: 8081 + + # JMS sources to consume messages from. + queues: + MyJmsQueue: + aerospike-operation: + type: write + parsing: + format: json + mapping: + bins: + type: multi-bins + all-value-fields: true + key-field: + source: value-field + field-name: key + namespace: + mode: static + value: test + + # topics: {} + + # The connection properties to the JMS message broker. + jms: + # # RabbitMQ example. + factory: com.rabbitmq.jms.admin.RMQConnectionFactory + config: + host: rabbitmq.rabbitmq-system + port: 5672 + username: guest + password: guest + + # # ActiveMQ example. + # factory: org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory + # jndi-cf-name: ConnectionFactory + # config: + # java.naming.provider.url: tcp://127.0.0.1:61616 + # java.naming.security.principal: admin + # java.naming.security.credentials: password + + # # IBM MQ example. + # factory: com.ibm.mq.jms.MQConnectionFactory + # config: + # hostName: 127.0.0.1 + # port: 1414 + # queueManager: QM1 + # transportType: 1 + # channel: DEV.APP.SVRCONN + + # The Aerospike cluster connection properties. + aerospike: + seeds: + - aerocluster.aerospike: # Aerospike Headless Service name or seed node address. + port: 4333 + tls-name: aerospike.svc.cluster.local # The TLS name of the Aerospike database. + tls: + key-store: + store-file: secrets/tls-certs/svc.cluster.local.keystore.jks + store-password-file: secrets/tls-certs/storepass + store-type: JKS + trust-store: + store-file: secrets/tls-certs/svc.cluster.local.truststore.jks + store-password-file: secrets/tls-certs/storepass + store-type: JKS + + # The logging properties. + logging: + enable-console-logging: true + levels: + root: DEBUG + diff --git a/aerospike-jms-inbound/examples/tls/tls-certs/storepass b/aerospike-jms-inbound/examples/tls/tls-certs/storepass new file mode 100644 index 0000000..8a9ec1f --- /dev/null +++ b/aerospike-jms-inbound/examples/tls/tls-certs/storepass @@ -0,0 +1 @@ +aerospike \ No newline at end of file diff --git a/aerospike-jms-inbound/examples/tls/tls-certs/svc.cluster.local.crt b/aerospike-jms-inbound/examples/tls/tls-certs/svc.cluster.local.crt new file mode 100644 index 0000000..7838b67 --- /dev/null +++ b/aerospike-jms-inbound/examples/tls/tls-certs/svc.cluster.local.crt @@ -0,0 +1,27 @@ +Bag Attributes + friendlyName: connector + localKeyID: 54 69 6D 65 20 31 36 39 33 35 35 30 33 33 32 31 36 31 +subject=C = US, ST = NY, L = NY, O = Local Developement, OU = Local Developement, CN = aerospike.svc.cluster.local +issuer=C = US, ST = NY, L = NY, O = Local Developement, OU = Local Developement, CN = aerospike.svc.cluster.local +-----BEGIN CERTIFICATE----- +MIIDtDCCApygAwIBAgIJAIzp/w6mYWADMA0GCSqGSIb3DQEBDAUAMIGHMQswCQYD +VQQGEwJVUzELMAkGA1UECBMCTlkxCzAJBgNVBAcTAk5ZMRswGQYDVQQKExJMb2Nh +bCBEZXZlbG9wZW1lbnQxGzAZBgNVBAsTEkxvY2FsIERldmVsb3BlbWVudDEkMCIG +A1UEAxMbYWVyb3NwaWtlLnN2Yy5jbHVzdGVyLmxvY2FsMB4XDTIzMDkwMTA2MzYz +NVoXDTMzMDgyOTA2MzYzNVowgYcxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJOWTEL +MAkGA1UEBxMCTlkxGzAZBgNVBAoTEkxvY2FsIERldmVsb3BlbWVudDEbMBkGA1UE +CxMSTG9jYWwgRGV2ZWxvcGVtZW50MSQwIgYDVQQDExthZXJvc3Bpa2Uuc3ZjLmNs +dXN0ZXIubG9jYWwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIYNA4 +FD0W1wAdVtQG3h8jmsE6zVw1PWsSwO11UjXrUJpGWKUzGO7X6K8sX0Bv6b7cGtA4 +0zPnmHMvRqNdwz6maIRdHQf9nw8YzVaoNqNkYz9Uww2qzoogPpdXDlCambZA+unZ +pIEZOfWvkIX4EojXEoRJ5EwSR97fs1RaxAjxHi1pvEYBTVzJyWeshVIzrUkL6KW8 +OsY0T9Y7ElrCPgzG3UxnpQMo59JyCRu92qf0uJiZx8VnoluRPr98/3NGasEtwj3C +QPTwxWzDZk5VLCK/l1B5M33sBwkSbFFQU71gPcSg4chn/lRfowokfY+XX5RN1d+D +07bEjTxnCTel5qlfAgMBAAGjITAfMB0GA1UdDgQWBBSJYmkRASZ6yuxe1PABvJwK +r4vsoTANBgkqhkiG9w0BAQwFAAOCAQEAb++qliZVfnW7ZlQb8ks3zX5ritAfmQAw +jC+lHJdD0QwUhvcflSAsXvSc9VC75HDznYAJpCBPso1JYk8B8PH/SV6cYoaGx2nh +WaM1DVn2qbmIQ0kGklHa00/8rkAw3UGexr/dvmSwCphtn8iO2oHMlK0BMRv+RgHq +AAjbaZLjsE/CS+8zrolT5Y5CjdoKQvsoVXkcEzKXtmjKm+MDycSDie38CC/FKx+l +P4UXukeBO8U++BnJdClz35pe7QC41dkBems3BIogc5YAArM6c8AecxrBiFo+UC0s +0yMxmBa3sz7PESWF1V2A6lTg0im8TVWnfZ1XccQH8c9cNrnztjFEoQ== +-----END CERTIFICATE----- diff --git a/aerospike-jms-inbound/examples/tls/tls-certs/svc.cluster.local.key b/aerospike-jms-inbound/examples/tls/tls-certs/svc.cluster.local.key new file mode 100644 index 0000000..5f25b1c --- /dev/null +++ b/aerospike-jms-inbound/examples/tls/tls-certs/svc.cluster.local.key @@ -0,0 +1,32 @@ +Bag Attributes + friendlyName: connector + localKeyID: 54 69 6D 65 20 31 36 39 33 35 35 30 33 33 32 31 36 31 +Key Attributes: +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDIYNA4FD0W1wAd +VtQG3h8jmsE6zVw1PWsSwO11UjXrUJpGWKUzGO7X6K8sX0Bv6b7cGtA40zPnmHMv +RqNdwz6maIRdHQf9nw8YzVaoNqNkYz9Uww2qzoogPpdXDlCambZA+unZpIEZOfWv +kIX4EojXEoRJ5EwSR97fs1RaxAjxHi1pvEYBTVzJyWeshVIzrUkL6KW8OsY0T9Y7 +ElrCPgzG3UxnpQMo59JyCRu92qf0uJiZx8VnoluRPr98/3NGasEtwj3CQPTwxWzD +Zk5VLCK/l1B5M33sBwkSbFFQU71gPcSg4chn/lRfowokfY+XX5RN1d+D07bEjTxn +CTel5qlfAgMBAAECggEAUFgxZSsIS64tx5XXMmHCDp6wuAk73cUgt8ZP8GjkYdRR +578HjnOC4WPVSW9wAO+yrcK6lu5os4Y3OU+TlbcB2euylztaxuEORp2fD5fI8Cmz +bSNAyapDTCaMvEVrIHJd17RELBaABUNuNdU3Qn78C3DLRm2Z6wKSZRQc0MAl7be7 +9XfG4agRgzaT5zOzNX6b2G8rrBP1cEG18Z8hBg7IVKDiUPyreuzFizUpjIyGIRmj +kwNlypm3ta321rNzO1iWeo0+djdjvkzgZ/oDjnE8DEAi6HRhsr9pkDm5WhQvYBwU +2/Li92tgOvQM1v37Q4rSR2to5YxedUBaOsHAUAHoIQKBgQDxHbY1QkeRKAiynIAS +biIiyrCFdIpljcfLFQe/20R3PM70W5qUKLNHFBtNfDHLGnWApUUYXnI+AEALI9cH +sG0riQh42qDGMtQG9JEfewYvlRVZwKOSXN7OAUr9G7YjqgAUekuUVjwGqAfo5xs7 +wihq/d0VUikqjTzZNH+n/Vnh5wKBgQDUv1Ubx5YaFNMC5zmUPhe7ltxk/OTBU/J2 +jQD5RWVslvZKn7NB+i6n2knNOgVsquEaIYdJKTlx/xAoNog0Q1xbOCnju9PBz2AF +VZ5rCKEac2U7q0L0+nvLY2YDmiuu1GNSWsRuEfhRC7cwVGf5r2VwBHR4lfUX5oGM +9dNRLPX9yQKBgDesAJIzh4rAj6GN+RRIUaFu42stCXeBUQxXAr8NxbUyWSaasApd +i2dyAQvXDTg0F8VoY5fI1t/x9tebot6noc1Xipk9AbpBqisg9RErurXp2lAq5pm4 +z8kJC2N9roR1kCcqRaEueBWhXpIsKmGV5vctPorZuZ+VlOMh+Ae1u7cpAoGBAJF0 +CvVGPMqyKB6ZBhXjagh4OVDWUnQXN/NzrhG/L3vRrqwQidd2/EUIimLE44aLQgHC +oKMVi0/0KhDET9UjpNY9DO7FsR6V8LlU802MhdD2ymTET6/8PX0uBUY7Nth+HzXk +Ft6GbLbEgseJPdlI4Np4QUVciUiA8Vv1ije6vKh5AoGBALpq7jdHM5P430zAKF/+ +QxeJJXtm1xDmYm1YgxlRsj0Nd5mGYc+tYEdCHZmkfFabVdBMXrz5cP58MvWgJJVH +DblGLw40eqFW2kekVjaNzNmcHDsu3ye85J4xXaM/jJMdpykrOw5TmEIeSIvtHyAf +VNL23IxP0/GAWYgo/8pYf7Zi +-----END PRIVATE KEY----- diff --git a/aerospike-jms-inbound/examples/tls/tls-certs/svc.cluster.local.keycert.pem b/aerospike-jms-inbound/examples/tls/tls-certs/svc.cluster.local.keycert.pem new file mode 100644 index 0000000..622c3e4 --- /dev/null +++ b/aerospike-jms-inbound/examples/tls/tls-certs/svc.cluster.local.keycert.pem @@ -0,0 +1,59 @@ +Bag Attributes + friendlyName: connector + localKeyID: 54 69 6D 65 20 31 36 39 33 35 35 30 33 33 32 31 36 31 +Key Attributes: +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDIYNA4FD0W1wAd +VtQG3h8jmsE6zVw1PWsSwO11UjXrUJpGWKUzGO7X6K8sX0Bv6b7cGtA40zPnmHMv +RqNdwz6maIRdHQf9nw8YzVaoNqNkYz9Uww2qzoogPpdXDlCambZA+unZpIEZOfWv +kIX4EojXEoRJ5EwSR97fs1RaxAjxHi1pvEYBTVzJyWeshVIzrUkL6KW8OsY0T9Y7 +ElrCPgzG3UxnpQMo59JyCRu92qf0uJiZx8VnoluRPr98/3NGasEtwj3CQPTwxWzD +Zk5VLCK/l1B5M33sBwkSbFFQU71gPcSg4chn/lRfowokfY+XX5RN1d+D07bEjTxn +CTel5qlfAgMBAAECggEAUFgxZSsIS64tx5XXMmHCDp6wuAk73cUgt8ZP8GjkYdRR +578HjnOC4WPVSW9wAO+yrcK6lu5os4Y3OU+TlbcB2euylztaxuEORp2fD5fI8Cmz +bSNAyapDTCaMvEVrIHJd17RELBaABUNuNdU3Qn78C3DLRm2Z6wKSZRQc0MAl7be7 +9XfG4agRgzaT5zOzNX6b2G8rrBP1cEG18Z8hBg7IVKDiUPyreuzFizUpjIyGIRmj +kwNlypm3ta321rNzO1iWeo0+djdjvkzgZ/oDjnE8DEAi6HRhsr9pkDm5WhQvYBwU +2/Li92tgOvQM1v37Q4rSR2to5YxedUBaOsHAUAHoIQKBgQDxHbY1QkeRKAiynIAS +biIiyrCFdIpljcfLFQe/20R3PM70W5qUKLNHFBtNfDHLGnWApUUYXnI+AEALI9cH +sG0riQh42qDGMtQG9JEfewYvlRVZwKOSXN7OAUr9G7YjqgAUekuUVjwGqAfo5xs7 +wihq/d0VUikqjTzZNH+n/Vnh5wKBgQDUv1Ubx5YaFNMC5zmUPhe7ltxk/OTBU/J2 +jQD5RWVslvZKn7NB+i6n2knNOgVsquEaIYdJKTlx/xAoNog0Q1xbOCnju9PBz2AF +VZ5rCKEac2U7q0L0+nvLY2YDmiuu1GNSWsRuEfhRC7cwVGf5r2VwBHR4lfUX5oGM +9dNRLPX9yQKBgDesAJIzh4rAj6GN+RRIUaFu42stCXeBUQxXAr8NxbUyWSaasApd +i2dyAQvXDTg0F8VoY5fI1t/x9tebot6noc1Xipk9AbpBqisg9RErurXp2lAq5pm4 +z8kJC2N9roR1kCcqRaEueBWhXpIsKmGV5vctPorZuZ+VlOMh+Ae1u7cpAoGBAJF0 +CvVGPMqyKB6ZBhXjagh4OVDWUnQXN/NzrhG/L3vRrqwQidd2/EUIimLE44aLQgHC +oKMVi0/0KhDET9UjpNY9DO7FsR6V8LlU802MhdD2ymTET6/8PX0uBUY7Nth+HzXk +Ft6GbLbEgseJPdlI4Np4QUVciUiA8Vv1ije6vKh5AoGBALpq7jdHM5P430zAKF/+ +QxeJJXtm1xDmYm1YgxlRsj0Nd5mGYc+tYEdCHZmkfFabVdBMXrz5cP58MvWgJJVH +DblGLw40eqFW2kekVjaNzNmcHDsu3ye85J4xXaM/jJMdpykrOw5TmEIeSIvtHyAf +VNL23IxP0/GAWYgo/8pYf7Zi +-----END PRIVATE KEY----- +Bag Attributes + friendlyName: connector + localKeyID: 54 69 6D 65 20 31 36 39 33 35 35 30 33 33 32 31 36 31 +subject=C = US, ST = NY, L = NY, O = Local Developement, OU = Local Developement, CN = aerospike.svc.cluster.local +issuer=C = US, ST = NY, L = NY, O = Local Developement, OU = Local Developement, CN = aerospike.svc.cluster.local +-----BEGIN CERTIFICATE----- +MIIDtDCCApygAwIBAgIJAIzp/w6mYWADMA0GCSqGSIb3DQEBDAUAMIGHMQswCQYD +VQQGEwJVUzELMAkGA1UECBMCTlkxCzAJBgNVBAcTAk5ZMRswGQYDVQQKExJMb2Nh +bCBEZXZlbG9wZW1lbnQxGzAZBgNVBAsTEkxvY2FsIERldmVsb3BlbWVudDEkMCIG +A1UEAxMbYWVyb3NwaWtlLnN2Yy5jbHVzdGVyLmxvY2FsMB4XDTIzMDkwMTA2MzYz +NVoXDTMzMDgyOTA2MzYzNVowgYcxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJOWTEL +MAkGA1UEBxMCTlkxGzAZBgNVBAoTEkxvY2FsIERldmVsb3BlbWVudDEbMBkGA1UE +CxMSTG9jYWwgRGV2ZWxvcGVtZW50MSQwIgYDVQQDExthZXJvc3Bpa2Uuc3ZjLmNs +dXN0ZXIubG9jYWwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIYNA4 +FD0W1wAdVtQG3h8jmsE6zVw1PWsSwO11UjXrUJpGWKUzGO7X6K8sX0Bv6b7cGtA4 +0zPnmHMvRqNdwz6maIRdHQf9nw8YzVaoNqNkYz9Uww2qzoogPpdXDlCambZA+unZ +pIEZOfWvkIX4EojXEoRJ5EwSR97fs1RaxAjxHi1pvEYBTVzJyWeshVIzrUkL6KW8 +OsY0T9Y7ElrCPgzG3UxnpQMo59JyCRu92qf0uJiZx8VnoluRPr98/3NGasEtwj3C +QPTwxWzDZk5VLCK/l1B5M33sBwkSbFFQU71gPcSg4chn/lRfowokfY+XX5RN1d+D +07bEjTxnCTel5qlfAgMBAAGjITAfMB0GA1UdDgQWBBSJYmkRASZ6yuxe1PABvJwK +r4vsoTANBgkqhkiG9w0BAQwFAAOCAQEAb++qliZVfnW7ZlQb8ks3zX5ritAfmQAw +jC+lHJdD0QwUhvcflSAsXvSc9VC75HDznYAJpCBPso1JYk8B8PH/SV6cYoaGx2nh +WaM1DVn2qbmIQ0kGklHa00/8rkAw3UGexr/dvmSwCphtn8iO2oHMlK0BMRv+RgHq +AAjbaZLjsE/CS+8zrolT5Y5CjdoKQvsoVXkcEzKXtmjKm+MDycSDie38CC/FKx+l +P4UXukeBO8U++BnJdClz35pe7QC41dkBems3BIogc5YAArM6c8AecxrBiFo+UC0s +0yMxmBa3sz7PESWF1V2A6lTg0im8TVWnfZ1XccQH8c9cNrnztjFEoQ== +-----END CERTIFICATE----- diff --git a/aerospike-jms-inbound/examples/tls/tls-certs/svc.cluster.local.keystore.jks b/aerospike-jms-inbound/examples/tls/tls-certs/svc.cluster.local.keystore.jks new file mode 100644 index 0000000000000000000000000000000000000000..88ad694b1e44b2d49a4550828b2f2f476ff42c98 GIT binary patch literal 2308 zcmd5-`8$-07k}RwhLK%~>`V6LWg-dLH8&c?gefM=Bu2Keq^y&@tl5`JW4R{JY;Tk`IRcto_Qar&pQ%@7({xL>Vg zz-E2OU!#%rgX$u?h8_OLcSIAV`htOqcBbzO*PVxPt(UuK+&A_oqz~pbiB0N(J~~w1 z`_5vQ^=oiP{1eD=?kf+f$3v(tx#g3|Ti^U1MXcpcmf~axfa|FKr{PA z*uooR)dt}u*SS}IZ2J`7R?XU<<7qly{C$zZj+Zy3zR*_S1J?w(y}?6jeQH%_eW!h~ z%K48?E-Z%4SCXH_CW$B1g$fu5fBw`kK2b?DVzU%)6y*!swW0=gP}v!ST6ynzMxXIb z#HmGcP3o71XBg*8pKQ?;&3h)9roufAU+e#psyNodI|P4EZe1DS;Y*pl^3kaCd@qBb zYD^xGVwaany5H;*KZ{4pbt0CcluD=?ggpu0bk8*0QP#5@3<>&;UYjYbc zi%%nlvkxi~G@LyNVH(ey=#Lr6c5o_ZL|VDs5_xj3TUyQV#CHZUC32{s0)IrLa&O`& zT#(om-sV$CNr!9LrpH2maVk9SW;~VttSv~R=aEe826vHj=%4Q6_QK)$ot$+1qkJ-7 zuD1xo(74<$_BQQEfgk~wWi_WeP5zjoo<2^B=%m5Zgm78P%ueB|zF>Bb2RETVB9R-{a1n^wQAV7y{?30dbnI&lQ1gC-KnxP$fmW-Tzn;!(!yqrH&uCj zVNT3qRI!>iIklT=g^3v}%+|{unTWqizdwygZY{oHVU7um7;s`ndvSE*;20#A^5zRevz4(uZEbbG>hJ(+G28i8=>0Rl{ z4=&A4kHS6@(P#IojX?W`yeP+0(>dIF6;SJA-IZPWXKN5+;UZhFSza?u>CDgWNrhs+ zys8LQBhbnn)Ycd%*xb+zvFC{_@HXr?TV;NhLy~;)M~*qmW=EbmwTZBfRS{j&l&{C7 zFRYP``oUe=-7tuD_m62vw*A)95|@7tl*p>Fv2NaH?fX=PATc}Ws=L||+B$}DHY$46 zvZ@T3epLx>@0}d0s|axQaD2*K5%Tt}bjV+0y_vy8M7)gW?HLv37h>&!8YY#y(}!27 zZ1X2tfe3C}9O%yw)ACtiDV_QwQp5PsW|Cr|s$;<1Huk39sY=>D?)Pz|w^UGp&Hw!U1c(?R7ZBf^xJ7XFQu>EH@;^y&Nc2}GoQuzv z;ORzkcOnuLNIv!o_AcHeFM_9n%ddKn;BgjZ6m$ksf>cqeC>7js78D9nQ$F(#{6FfH z42u3&#=}Yn$)|lsb~^8U#)mwR#`I5 zFtp4=@q8JcFO|bcCIrG;n zMcvWDTB#PSrWxr~+CLUXb3=H~d@GF)`_36T%o&QFHRd#$dS79VYhnBR$)z~9vYE&X{;Z!SE3C~ z>O6YWI7xFTV@kXAxIK0(q%-L`sKmE#2rdGQ<4%bm%P`FsR#7EUW^=K+F=I@+yHZ#` zelF$Is+(^!Kf*g&?nSNaF)Fso3gw*(J6>xPa<*0LJ8y@VH0gbY%?dyr90l(XPt!#U zlTrX!g*xe#An8O?Bu>jzUantEDUG|jLaT@4WZ0l}@S^#LchdDH*n)s;3y&6tuRYgP fYc{Hs^mFb7ndztx@sM2uRx^ue>}F@lwvK-TNQ3?a literal 0 HcmV?d00001 diff --git a/aerospike-jms-inbound/examples/tls/tls-certs/svc.cluster.local.keystore.pkcs12 b/aerospike-jms-inbound/examples/tls/tls-certs/svc.cluster.local.keystore.pkcs12 new file mode 100644 index 0000000000000000000000000000000000000000..5ef32452c0f14222d8ffb73fe37fc8cc2b505c20 GIT binary patch literal 2814 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