diff --git a/argocd-slack-plugin/README.md b/argocd-slack-plugin/README.md new file mode 100644 index 0000000..a5099ec --- /dev/null +++ b/argocd-slack-plugin/README.md @@ -0,0 +1,102 @@ +# Argo CD Slack Notifications Plugin + +## Overview + +This plugin enables Slack notifications for Argo CD application events. It configures Argo CD to send notifications to Slack channels when applications are synced, deployed, or experience health issues. + +## Features + +- Real-time Slack notifications for Argo CD events +- Customizable notification templates with rich formatting +- Support for multiple event types: + - Application created/deleted + - Sync started/succeeded/failed + - Application health degraded + - Deployment completed + +## Required Vault Configuration + +The gitops catalog implementation will add these 2 secrets to your Vault, +and bind them to your app using external secrets. For local development you +can add these secrets manually. + +```bash +vault kv put secret/argocd-slack-plugin \ + SLACK_TOKEN="xoxb-your-slack-token-here" \ + SLACK_CHANNEL="#deployments" +``` + +Note: While the channel name isn't sensitive, it's stored in Vault as a user input value. + +## Slack Setup + +1. Create an Incoming Webhook in Slack +2. Use the webhook URL as the SLACK_TOKEN value + +## Configuration + +### Default Channel + +The default Slack channel is configured via the `SLACK_CHANNEL` value in Vault. All notifications will be sent to this channel by default. + +### Custom Subscriptions + +You can configure per-application notifications by adding annotations: + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + annotations: + notifications.argoproj.io/subscribe.on-sync-succeeded.slack: "#deployments" + notifications.argoproj.io/subscribe.on-sync-failed.slack: "#alerts" +``` + +## Notification Events + +The following events trigger notifications: + +- `on-created`: Application created +- `on-deleted`: Application deleted +- `on-deployed`: Application successfully deployed +- `on-health-degraded`: Application health degraded +- `on-sync-failed`: Sync operation failed +- `on-sync-running`: Sync operation started +- `on-sync-succeeded`: Sync operation succeeded + +## Customization + +### Modifying Templates + +Edit the `argocd-notifications-cm` ConfigMap to customize notification templates and add new ones. + +### Adding New Triggers + +Add new triggers in the ConfigMap following the pattern: + +```yaml +trigger.on-your-event: | + - when: your.condition + send: [your-template] +``` + +## Troubleshooting + +Check the notification controller logs: + +```bash +kubectl logs -n argocd deployment/argocd-notifications-controller +``` + +Verify the secret is created: + +```bash +kubectl get secret -n argocd argocd-notifications-secret +``` + +Test notifications: + +```bash +kubectl exec -n argocd deployment/argocd-notifications-controller -- \ + /app/argocd-notifications template notify app-sync-succeeded --recipient slack: +``` diff --git a/argocd-slack-plugin/argocd-slack-plugin.yaml b/argocd-slack-plugin/argocd-slack-plugin.yaml new file mode 100644 index 0000000..77b89fb --- /dev/null +++ b/argocd-slack-plugin/argocd-slack-plugin.yaml @@ -0,0 +1,25 @@ +--- +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: -argocd-slack-plugin-components + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: '100' + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + project: default + source: + repoURL: + path: /components/argocd-slack-plugin + targetRevision: HEAD + destination: + name: in-cluster + namespace: argocd + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=false # argocd namespace should already exist diff --git a/argocd-slack-plugin/components/argocd-slack-plugin/application.yaml b/argocd-slack-plugin/components/argocd-slack-plugin/application.yaml new file mode 100644 index 0000000..135a78d --- /dev/null +++ b/argocd-slack-plugin/components/argocd-slack-plugin/application.yaml @@ -0,0 +1,26 @@ +--- +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: -argocd-slack-plugin + namespace: argocd + annotations: + kubefirst.konstruct.io/application-name: argocd-slack-plugin + kubefirst.konstruct.io/source: catalog-templates + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + destination: + namespace: argocd + name: + project: + source: + repoURL: + path: /components/argocd-slack-plugin + targetRevision: HEAD + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=false # argocd namespace should already exist diff --git a/argocd-slack-plugin/components/argocd-slack-plugin/external-secret.yaml b/argocd-slack-plugin/components/argocd-slack-plugin/external-secret.yaml new file mode 100644 index 0000000..f7435ca --- /dev/null +++ b/argocd-slack-plugin/components/argocd-slack-plugin/external-secret.yaml @@ -0,0 +1,22 @@ +--- +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: -argocd-slack-plugin-secret + namespace: argocd +spec: + target: + name: argocd-notifications-secret + secretStoreRef: + kind: ClusterSecretStore + name: vault-kv-secret + refreshInterval: 10s + data: + - secretKey: slack-token + remoteRef: + key: /argocd-slack-plugin + property: SLACK_TOKEN + - secretKey: slack-channel + remoteRef: + key: /argocd-slack-plugin + property: SLACK_CHANNEL diff --git a/argocd-slack-plugin/components/argocd-slack-plugin/notification-configmap.yaml b/argocd-slack-plugin/components/argocd-slack-plugin/notification-configmap.yaml new file mode 100644 index 0000000..ef19053 --- /dev/null +++ b/argocd-slack-plugin/components/argocd-slack-plugin/notification-configmap.yaml @@ -0,0 +1,228 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: argocd-notifications-cm + namespace: argocd +data: + # Slack service configuration + service.slack: | + token: $slack-token + + # Default subscriptions + defaultSubscriptions: | + - recipients: + - slack:$slack-channel + triggers: + - on-created + - on-deleted + - on-deployed + - on-health-degraded + - on-sync-failed + - on-sync-running + - on-sync-status-unknown + - on-sync-succeeded + + # Notification templates + template.app-created: | + email: + subject: Application {{.app.metadata.name}} has been created. + message: | + {{if eq .serviceType "slack"}}:white_check_mark:{{end}} Application {{.app.metadata.name}} has been created. + slack: + attachments: | + [{ + "title": "{{.app.metadata.name}}", + "title_link": "{{.context.argocdUrl}}/applications/{{.app.metadata.name}}", + "color": "#18be52", + "fields": [ + { + "title": "Sync Status", + "value": "{{.app.status.sync.status}}", + "short": true + }, + { + "title": "Repository", + "value": "{{.app.spec.source.repoURL}}", + "short": true + } + ] + }] + + template.app-deleted: | + email: + subject: Application {{.app.metadata.name}} has been deleted. + message: | + {{if eq .serviceType "slack"}}:exclamation:{{end}} Application {{.app.metadata.name}} has been deleted. + slack: + attachments: | + [{ + "title": "{{.app.metadata.name}}", + "color": "#E96D76", + "fields": [ + { + "title": "Deleted At", + "value": "{{.app.metadata.deletionTimestamp}}", + "short": true + } + ] + }] + + template.app-deployed: | + email: + subject: New version of an application {{.app.metadata.name}} is up and running. + message: | + {{if eq .serviceType "slack"}}:white_check_mark:{{end}} Application {{.app.metadata.name}} is now running new version of deployments manifests. + slack: + attachments: | + [{ + "title": "{{.app.metadata.name}}", + "title_link": "{{.context.argocdUrl}}/applications/{{.app.metadata.name}}", + "color": "#18be52", + "fields": [ + { + "title": "Sync Status", + "value": "{{.app.status.sync.status}}", + "short": true + }, + { + "title": "Revision", + "value": "{{.app.status.sync.revision}}", + "short": true + } + ] + }] + + template.app-health-degraded: | + email: + subject: Application {{.app.metadata.name}} has degraded. + message: | + {{if eq .serviceType "slack"}}:exclamation:{{end}} Application {{.app.metadata.name}} has degraded. + Application details: {{.context.argocdUrl}}/applications/{{.app.metadata.name}}. + slack: + attachments: | + [{ + "title": "{{.app.metadata.name}}", + "title_link": "{{.context.argocdUrl}}/applications/{{.app.metadata.name}}", + "color": "#f4c030", + "fields": [ + { + "title": "Health Status", + "value": "{{.app.status.health.status}}", + "short": true + }, + { + "title": "Repository", + "value": "{{.app.spec.source.repoURL}}", + "short": true + } + ] + }] + + template.app-sync-failed: | + email: + subject: Failed to sync application {{.app.metadata.name}}. + message: | + {{if eq .serviceType "slack"}}:exclamation:{{end}} The sync operation of application {{.app.metadata.name}} has failed at {{.app.status.operationState.finishedAt}} with the following error: {{.app.status.operationState.message}} + slack: + attachments: | + [{ + "title": "{{.app.metadata.name}}", + "title_link": "{{.context.argocdUrl}}/applications/{{.app.metadata.name}}", + "color": "#E96D76", + "fields": [ + { + "title": "Sync Status", + "value": "{{.app.status.sync.status}}", + "short": true + }, + { + "title": "Error", + "value": "{{.app.status.operationState.message}}", + "short": false + } + ] + }] + + template.app-sync-running: | + email: + subject: Start syncing application {{.app.metadata.name}}. + message: | + The sync operation of application {{.app.metadata.name}} has started at {{.app.status.operationState.startedAt}}. + slack: + attachments: | + [{ + "title": "{{.app.metadata.name}}", + "title_link": "{{.context.argocdUrl}}/applications/{{.app.metadata.name}}", + "color": "#0DADEA", + "fields": [ + { + "title": "Sync Status", + "value": "Running", + "short": true + }, + { + "title": "Started At", + "value": "{{.app.status.operationState.startedAt}}", + "short": true + } + ] + }] + + template.app-sync-succeeded: | + email: + subject: Application {{.app.metadata.name}} has been successfully synced. + message: | + {{if eq .serviceType "slack"}}:white_check_mark:{{end}} Application {{.app.metadata.name}} has been successfully synced at {{.app.status.operationState.finishedAt}}. + slack: + attachments: | + [{ + "title": "{{.app.metadata.name}}", + "title_link": "{{.context.argocdUrl}}/applications/{{.app.metadata.name}}", + "color": "#18be52", + "fields": [ + { + "title": "Sync Status", + "value": "{{.app.status.sync.status}}", + "short": true + }, + { + "title": "Revision", + "value": "{{.app.status.sync.revision}}", + "short": true + } + ] + }] + + # Triggers + trigger.on-created: | + - when: true + send: [app-created] + + trigger.on-deleted: | + - when: app.metadata.deletionTimestamp != nil + send: [app-deleted] + + trigger.on-deployed: | + - when: app.status.operationState.phase in ['Succeeded'] and app.status.health.status == 'Healthy' + send: [app-deployed] + + trigger.on-health-degraded: | + - when: app.status.health.status == 'Degraded' + send: [app-health-degraded] + + trigger.on-sync-failed: | + - when: app.status.operationState.phase in ['Error', 'Failed'] + send: [app-sync-failed] + + trigger.on-sync-running: | + - when: app.status.operationState.phase in ['Running'] + send: [app-sync-running] + + trigger.on-sync-status-unknown: | + - when: app.status.sync.status == 'Unknown' + send: [app-sync-status-unknown] + + trigger.on-sync-succeeded: | + - when: app.status.operationState.phase in ['Succeeded'] + send: [app-sync-succeeded] diff --git a/index.yaml b/index.yaml index 3560e7e..c13d2d6 100644 --- a/index.yaml +++ b/index.yaml @@ -14,6 +14,20 @@ apps: description: "A Kubernetes controller & set of CRDs which provide advanced deployment capabilities such as blue-green, canary, canary analysis, experimentation, & progressive delivery features to Kubernetes." category: CI/CD + - name: argocd-slack-plugin + displayName: Argo CD Slack Notifications + website: "https://argo-cd.readthedocs.io/en/stable/operator-manual/notifications/" + secretKeys: + - name: SLACK_TOKEN + label: Slack OAuth token or webhook URL + env: SLACK_TOKEN + - name: SLACK_CHANNEL + label: Slack channel name (e.g., deployments) + env: SLACK_CHANNEL + imageUrl: "https://raw.githubusercontent.com/konstructio/gitops-catalog/main/logos/argocd-slack-plugin.svg" + description: "Enable Slack notifications for Argo CD application events including sync status, health changes, and deployment updates." + category: CI/CD + - name: cilium displayName: Cilium website: "https://cilium.io/" diff --git a/logos/argocd-slack-plugin.svg b/logos/argocd-slack-plugin.svg new file mode 100644 index 0000000..6acbeb1 --- /dev/null +++ b/logos/argocd-slack-plugin.svg @@ -0,0 +1 @@ + \ No newline at end of file