From 431e8b60b2205055c92da64c5499ef574764324e Mon Sep 17 00:00:00 2001 From: Doug Goldstein Date: Tue, 3 Sep 2024 12:21:03 -0500 Subject: [PATCH] feat(workflows): sensor and trigger to sync keystone to nautobot Added an EventSource listener for keystone notifications. Added a Sensor to trigger a workflow to be run when projects are created, updated or deleted in keystone. Added a workflow to execute the keystone sync script on each of those triggers. --- .github/workflows/yamllint.yaml | 1 + .../eventsource-openstack/argo-rabbitmq.yaml | 16 +++++ .../eventsource-openstack/kustomization.yaml | 1 + .../openstack-event-source.yaml | 39 ++++++++++- .../sensor-keystone-event-project.yaml | 67 +++++++++++++++++++ apps/understack-workflows/kustomization.yaml | 1 + .../workflows/kustomization.yaml | 10 +++ .../workflows/openstack-svc-acct.yaml | 28 ++++++++ .../workflows/sensor-submit-rbac.yaml | 40 +++++++++++ .../wf-keystone-event-project.yaml | 37 ++++++++++ docs/component-understack-workflows.md | 13 ++++ 11 files changed, 252 insertions(+), 1 deletion(-) create mode 100644 apps/understack-workflows/eventsource-openstack/sensor-keystone-event-project.yaml create mode 100644 apps/understack-workflows/workflows/kustomization.yaml create mode 100644 apps/understack-workflows/workflows/openstack-svc-acct.yaml create mode 100644 apps/understack-workflows/workflows/sensor-submit-rbac.yaml create mode 100644 apps/understack-workflows/workflows/workflowtemplates/wf-keystone-event-project.yaml diff --git a/.github/workflows/yamllint.yaml b/.github/workflows/yamllint.yaml index cb6827d7..1f4ec222 100644 --- a/.github/workflows/yamllint.yaml +++ b/.github/workflows/yamllint.yaml @@ -46,6 +46,7 @@ jobs: argo-workflows/**/workflowtemplates/*.y*ml argo-workflows/**/sensors/*.y*ml argo-workflows/**/workflows/*.y*ml + apps/understack-workflows/workflows/workflowtemplates/*.y*ml shellcheck: runs-on: ubuntu-latest diff --git a/apps/understack-workflows/eventsource-openstack/argo-rabbitmq.yaml b/apps/understack-workflows/eventsource-openstack/argo-rabbitmq.yaml index e8ac747e..373926ed 100644 --- a/apps/understack-workflows/eventsource-openstack/argo-rabbitmq.yaml +++ b/apps/understack-workflows/eventsource-openstack/argo-rabbitmq.yaml @@ -23,3 +23,19 @@ spec: rabbitmqClusterReference: name: rabbitmq # rabbitmqCluster must exist in the same namespace as this resource namespace: openstack +--- +apiVersion: rabbitmq.com/v1beta1 +kind: Permission +metadata: + name: argo-to-keystone-permission +spec: + vhost: "keystone" + userReference: + name: "argo" # name of a user.rabbitmq.com in the same namespace; must specify either spec.userReference or spec.user + permissions: + write: ".*" + configure: ".*" + read: ".*" + rabbitmqClusterReference: + name: rabbitmq # rabbitmqCluster must exist in the same namespace as this resource + namespace: openstack diff --git a/apps/understack-workflows/eventsource-openstack/kustomization.yaml b/apps/understack-workflows/eventsource-openstack/kustomization.yaml index 5f308fc5..6828ca22 100644 --- a/apps/understack-workflows/eventsource-openstack/kustomization.yaml +++ b/apps/understack-workflows/eventsource-openstack/kustomization.yaml @@ -7,3 +7,4 @@ resources: - argo-rabbitmq.yaml - eventbus-default.yaml - openstack-event-source.yaml + - sensor-keystone-event-project.yaml diff --git a/apps/understack-workflows/eventsource-openstack/openstack-event-source.yaml b/apps/understack-workflows/eventsource-openstack/openstack-event-source.yaml index 3c0c22f4..4e71dfe5 100644 --- a/apps/understack-workflows/eventsource-openstack/openstack-event-source.yaml +++ b/apps/understack-workflows/eventsource-openstack/openstack-event-source.yaml @@ -7,7 +7,6 @@ spec: openstack: # amqp server url url: amqp://rabbitmq-server-0.rabbitmq-nodes.openstack.svc.cluster.local:5672/ironic - routingKey: 'ironic_versioned_notifications.info' # jsonBody specifies that all event body payload coming from this # source will be JSON jsonBody: true @@ -16,6 +15,44 @@ spec: exchangeType: topic exchangeDeclare: durable: false + # routing key for messages within the exchange + routingKey: 'ironic_versioned_notifications.info' + # optional consume settings + # if not provided, default values will be used + consume: + consumerTag: "argo-events" + autoAck: true + exclusive: false + noLocal: false + # username and password for authentication + # use secret selectors + auth: + username: + name: argo-user-credentials + key: username + password: + name: argo-user-credentials + key: password +--- +apiVersion: argoproj.io/v1alpha1 +kind: EventSource +metadata: + name: openstack-keystone +spec: + amqp: + notifications: + # amqp server url + url: amqp://rabbitmq-server-0.rabbitmq-nodes.openstack.svc.cluster.local:5672/keystone + # jsonBody specifies that all event body payload coming from this + # source will be JSON + jsonBody: true + # name of the exchange. + exchangeName: keystone + exchangeType: topic + exchangeDeclare: + durable: false + # routing key for messages within the exchange + routingKey: 'notifications.info' # optional consume settings # if not provided, default values will be used consume: diff --git a/apps/understack-workflows/eventsource-openstack/sensor-keystone-event-project.yaml b/apps/understack-workflows/eventsource-openstack/sensor-keystone-event-project.yaml new file mode 100644 index 00000000..fa800dba --- /dev/null +++ b/apps/understack-workflows/eventsource-openstack/sensor-keystone-event-project.yaml @@ -0,0 +1,67 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: sensor-submit-workflow +--- +apiVersion: argoproj.io/v1alpha1 +kind: Sensor +metadata: + finalizers: + - sensor-controller + labels: + argocd.argoproj.io/instance: argo-events + name: keystone-event-project + namespace: argo-events + annotations: + workflows.argoproj.io/description: | + Defined in `apps/understack-workflows/sensors/sensor-keystone-event-project.yaml` +spec: + dependencies: + - eventName: notifications + eventSourceName: openstack-keystone + name: keystone-msg + transform: + jq: ".body[\"oslo.message\"] | fromjson" + filters: + dataLogicalOperator: "and" + data: + - path: "event_type" + type: "string" + value: + - "identity.project.created" + - "identity.project.updated" + - "identity.project.deleted" + template: + serviceAccountName: sensor-submit-workflow + triggers: + - template: + name: keystone-event-project + argoWorkflow: + operation: submit + parameters: + - dest: spec.arguments.parameters.0.value + src: + dataKey: event_type + dependencyName: keystone-msg + - dest: spec.arguments.parameters.1.value + src: + dataKey: payload.target.id + dependencyName: keystone-msg + source: + resource: + apiVersion: argoproj.io/v1alpha1 + kind: Workflow + metadata: + generateName: keystone-event-project- + namespace: argo-events + spec: + arguments: + parameters: + - name: event_type + value: "replaced by parameters section" + - name: project_uuid + value: "replaced by parameters section" + serviceAccountName: workflow + workflowTemplateRef: + name: keystone-event-project diff --git a/apps/understack-workflows/kustomization.yaml b/apps/understack-workflows/kustomization.yaml index bc3323f1..d8a7641f 100644 --- a/apps/understack-workflows/kustomization.yaml +++ b/apps/understack-workflows/kustomization.yaml @@ -3,3 +3,4 @@ kind: Kustomization resources: - eventsource-openstack + - workflowtemplates diff --git a/apps/understack-workflows/workflows/kustomization.yaml b/apps/understack-workflows/workflows/kustomization.yaml new file mode 100644 index 00000000..a68ceb14 --- /dev/null +++ b/apps/understack-workflows/workflows/kustomization.yaml @@ -0,0 +1,10 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +# this is where our workflows currently run +namespace: argo-events + +resources: + - openstack-svc-acct.yaml + - sensor-submit-rbac.yaml + - workflowtemplates/keystone-event-project.yaml diff --git a/apps/understack-workflows/workflows/openstack-svc-acct.yaml b/apps/understack-workflows/workflows/openstack-svc-acct.yaml new file mode 100644 index 00000000..0793273c --- /dev/null +++ b/apps/understack-workflows/workflows/openstack-svc-acct.yaml @@ -0,0 +1,28 @@ +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: openstack-svc-acct +spec: + refreshInterval: 1h + secretStoreRef: + kind: ClusterSecretStore + name: openstack + target: + name: openstack-svc-acct + template: + engineVersion: v2 + data: + clouds.yaml: | + clouds: + understack: + auth_url: http://keystone-api.openstack.svc.cluster.local:5000/v3 + user_domain_name: {{ .user_domain }} + username: {{ .username }} + password: {{ .password }} + # this should switch to where we will be creating the ironic nodes + # in the future + project_domain_name: default + project_name: undercloud + dataFrom: + - extract: + key: svc-acct-argoworkflow diff --git a/apps/understack-workflows/workflows/sensor-submit-rbac.yaml b/apps/understack-workflows/workflows/sensor-submit-rbac.yaml new file mode 100644 index 00000000..f5f69a16 --- /dev/null +++ b/apps/understack-workflows/workflows/sensor-submit-rbac.yaml @@ -0,0 +1,40 @@ +--- +# Similarly you can use a ClusterRole and ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: sensor-submit-workflow-role +rules: + - apiGroups: + - argoproj.io + verbs: + - get + - watch + - list + resources: + - workflowtemplates + - clusterworkflowtemplates + - apiGroups: + - argoproj.io + verbs: + - create + - get + - list + - watch + - update + - patch + resources: + - workflows +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: openstack-sensor-submit-workflow +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: sensor-submit-workflow-role +subjects: + - kind: ServiceAccount + name: sensor-submit-workflow + namespace: openstack diff --git a/apps/understack-workflows/workflows/workflowtemplates/wf-keystone-event-project.yaml b/apps/understack-workflows/workflows/workflowtemplates/wf-keystone-event-project.yaml new file mode 100644 index 00000000..a2772b2a --- /dev/null +++ b/apps/understack-workflows/workflows/workflowtemplates/wf-keystone-event-project.yaml @@ -0,0 +1,37 @@ +apiVersion: argoproj.io/v1alpha1 +metadata: + name: keystone-event-project + annotations: + workflows.argoproj.io/description: | + Defined in `apps/understack-workflows/workflowtemplates/wf-keystone-event-project.yaml` +kind: WorkflowTemplate +spec: + entrypoint: sync-keystone + templates: + - name: sync-keystone + container: + image: ghcr.io/rackerlabs/understack/ironic-nautobot-client:latest + command: + - sync-keystone + args: + - "--only-domain" + - "default" + - "{{workflow.parameters.event_type}}" + - "{{workflow.parameters.project_uuid}}" + volumeMounts: + - mountPath: /etc/nb-token/ + name: nb-token + readOnly: true + - mountPath: /etc/openstack + name: openstack-svc-acct + readOnly: true + inputs: + parameters: + - name: project_uuid + volumes: + - name: nb-token + secret: + secretName: nautobot-token + - name: openstack-svc-acct + secret: + secretName: openstack-svc-acct diff --git a/docs/component-understack-workflows.md b/docs/component-understack-workflows.md index b9f6c213..5310c067 100644 --- a/docs/component-understack-workflows.md +++ b/docs/component-understack-workflows.md @@ -27,6 +27,19 @@ time it is listening to keystone and ironic only. 1. An [Argo Events][argo-events] Event Bus to push the received notifications into. +1. A Kubernetes Service account `sensor-submit-workflow` which +allows an Argo Events Trigger from a Sensor to read look up +[Argo Workflows][argo-wf] Workflow Templates and use them to +execute a Workflow. +1. An [Argo Events][argo-events] Sensors and Triggers that +execute workflows. + +## workflowtemplates + +1. A Kubernetes Role Binding allowing the `sensor-submit-workflow` +the access it needs to run Workflows. +1. A number of Workflow Templates. [argo-events]: +[argo-wf]: [eso]: