Skip to content

Commit

Permalink
feat: expose notification secrets for request payload templating (#16055
Browse files Browse the repository at this point in the history
)

Signed-off-by: Etien Roznik <12816736+eroznik@users.noreply.github.com>
  • Loading branch information
eroznik authored Oct 25, 2023
1 parent f1d3d66 commit bc5fb81
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 0 deletions.
34 changes: 34 additions & 0 deletions docs/operator-manual/notifications/templates.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Each template has access to the following fields:
- `app` holds the application object.
- `context` is a user-defined string map and might include any string keys and values.
- `secrets` provides access to sensitive data stored in `argocd-notifications-secret`
- `serviceType` holds the notification service type name (such as "slack" or "email). The field can be used to conditionally
render service-specific fields.
- `recipient` holds the recipient name.
Expand All @@ -43,6 +44,39 @@ data:
message: "Something happened in {{ .context.environmentName }} in the {{ .context.region }} data center!"
```

## Defining and using secrets within notification templates

Some notification service use cases will require the use of secrets within templates. This can be achieved with the use of
the `secrets` data variable available within the templates.

Given that we have the following `argocd-notifications-secret`:

```yaml
apiVersion: v1
kind: Secret
metadata:
name: argocd-notifications-secret
stringData:
sampleWebhookToken: secret-token
type: Opaque
```

We can use the defined `sampleWebhookToken` in a template as such:

```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-notifications-cm
data:
template.trigger-webhook: |
webhook:
sample-webhook:
method: POST
path: 'webhook/endpoint/with/auth'
body: 'token={{ .secrets.sampleWebhookToken }}&variables[APP_SOURCE_PATH]={{ .app.spec.source.path }}
```

## Notification Service Specific Fields

The `message` field of the template definition allows creating a basic notification for any notification service. You can leverage notification service-specific
Expand Down
1 change: 1 addition & 0 deletions util/notification/settings/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ func initGetVars(argocdService service.Service, cfg *api.Config, configMap *v1.C
return expression.Spawn(&unstructured.Unstructured{Object: obj}, argocdService, map[string]interface{}{
"app": obj,
"context": injectLegacyVar(context, dest.Service),
"secrets": secret.Data,
})
}, nil
}
92 changes: 92 additions & 0 deletions util/notification/settings/settings_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package settings

import (
"fmt"
"testing"

"github.com/argoproj/argo-cd/v2/reposerver/apiclient/mocks"
service "github.com/argoproj/argo-cd/v2/util/notification/argocd"
"github.com/argoproj/notifications-engine/pkg/api"
"github.com/argoproj/notifications-engine/pkg/services"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/fake"
)

const testNamespace = "default"
const testContextKey = "test-context-key"
const testContextKeyValue = "test-context-key-value"

func TestInitGetVars(t *testing.T) {
notificationsCm := corev1.ConfigMap{
ObjectMeta: v1.ObjectMeta{
Namespace: testNamespace,
Name: "argocd-notifications-cm",
},
Data: map[string]string{
"context": fmt.Sprintf("%s: %s", testContextKey, testContextKeyValue),
"service.webhook.test": "url: https://test.com",
"template.app-created": "email:\n subject: Application {{.app.metadata.name}} has been created.\nmessage: Application {{.app.metadata.name}} has been created.\nteams:\n title: Application {{.app.metadata.name}} has been created.\n",
"trigger.on-created": "- description: Application is created.\n oncePer: app.metadata.name\n send:\n - app-created\n when: \"true\"\n",
},
}
notificationsSecret := corev1.Secret{
ObjectMeta: v1.ObjectMeta{
Name: "argocd-notifications-secret",
Namespace: testNamespace,
},
Data: map[string][]byte{
"notification-secret": []byte("secret-value"),
},
}
kubeclientset := fake.NewSimpleClientset(&corev1.ConfigMap{
ObjectMeta: v1.ObjectMeta{
Namespace: testNamespace,
Name: "argocd-notifications-cm",
},
Data: notificationsCm.Data,
},
&corev1.Secret{
ObjectMeta: v1.ObjectMeta{
Name: "argocd-notifications-secret",
Namespace: testNamespace,
},
Data: notificationsSecret.Data,
})
mockRepoClient := &mocks.Clientset{RepoServerServiceClient: &mocks.RepoServerServiceClient{}}
argocdService, err := service.NewArgoCDService(kubeclientset, testNamespace, mockRepoClient)
require.NoError(t, err)
defer argocdService.Close()
config := api.Config{}
testDestination := services.Destination{
Service: "webhook",
}
emptyAppData := map[string]interface{}{}

varsProvider, _ := initGetVars(argocdService, &config, &notificationsCm, &notificationsSecret)

t.Run("Vars provider serves Application data on app key", func(t *testing.T) {
appData := map[string]interface{}{
"name": "app-name",
}
result := varsProvider(appData, testDestination)
assert.NotNil(t, t, result["app"])
assert.Equal(t, result["app"], appData)
})
t.Run("Vars provider serves notification context data on context key", func(t *testing.T) {
expectedContext := map[string]string{
testContextKey: testContextKeyValue,
"notificationType": testDestination.Service,
}
result := varsProvider(emptyAppData, testDestination)
assert.NotNil(t, result["context"])
assert.Equal(t, result["context"], expectedContext)
})
t.Run("Vars provider serves notification secrets on secrets key", func(t *testing.T) {
result := varsProvider(emptyAppData, testDestination)
assert.NotNil(t, result["secrets"])
assert.Equal(t, result["secrets"], notificationsSecret.Data)
})
}

0 comments on commit bc5fb81

Please sign in to comment.