Skip to content

Commit

Permalink
feat: add notifications API (argoproj#10279)
Browse files Browse the repository at this point in the history
* add notifications API

we allow to list triggers, templates and services

Signed-off-by: Pavel <[email protected]>

* fix: add notification manifest to tests

Signed-off-by: Pavel <[email protected]>

* fix: add sleep to fix integration tests

for some reason notification configmap has old data, trying to fix with
the sleep

Signed-off-by: Pavel <[email protected]>

* add proposal

Signed-off-by: Pavel <[email protected]>

* more info to proposal

Signed-off-by: Pavel <[email protected]>

* use struct for notifications objects instead of just strings

to be able easily extend API in the future return list of
trigger/template/service as list of structs

Signed-off-by: Pavel <[email protected]>

Signed-off-by: Pavel <[email protected]>
  • Loading branch information
aborilov authored Aug 25, 2022
1 parent a23bfc3 commit 37ad433
Show file tree
Hide file tree
Showing 21 changed files with 2,908 additions and 61 deletions.
126 changes: 126 additions & 0 deletions assets/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -2139,6 +2139,75 @@
}
}
},
"/api/v1/notifications/services": {
"get": {
"tags": [
"NotificationService"
],
"summary": "List returns list of services",
"operationId": "NotificationService_ListServices",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/notificationServiceList"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/runtimeError"
}
}
}
}
},
"/api/v1/notifications/templates": {
"get": {
"tags": [
"NotificationService"
],
"summary": "List returns list of templates",
"operationId": "NotificationService_ListTemplates",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/notificationTemplateList"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/runtimeError"
}
}
}
}
},
"/api/v1/notifications/triggers": {
"get": {
"tags": [
"NotificationService"
],
"summary": "List returns list of triggers",
"operationId": "NotificationService_ListTriggers",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/notificationTriggerList"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/runtimeError"
}
}
}
}
},
"/api/v1/projects": {
"get": {
"tags": [
Expand Down Expand Up @@ -3979,6 +4048,63 @@
"type": "object",
"title": "Generic (empty) response for GPG public key CRUD requests"
},
"notificationService": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
}
},
"notificationServiceList": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/notificationService"
}
}
}
},
"notificationTemplate": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
}
},
"notificationTemplateList": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/notificationTemplate"
}
}
}
},
"notificationTrigger": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
}
},
"notificationTriggerList": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/notificationTrigger"
}
}
}
},
"oidcClaim": {
"type": "object",
"properties": {
Expand Down
20 changes: 19 additions & 1 deletion cmd/argocd-notification/commands/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ import (
"strings"

"github.com/argoproj/argo-cd/v2/common"
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"

"github.com/argoproj/argo-cd/v2/util/env"
"github.com/argoproj/argo-cd/v2/util/errors"
service "github.com/argoproj/argo-cd/v2/util/notification/argocd"
"github.com/argoproj/argo-cd/v2/util/tls"

notificationscontroller "github.com/argoproj/argo-cd/v2/notification_controller/controller"

Expand Down Expand Up @@ -105,7 +108,22 @@ func NewCommand() *cobra.Command {
return fmt.Errorf("Unknown log format '%s'", logFormat)
}

argocdService, err := service.NewArgoCDService(k8sClient, namespace, argocdRepoServer, argocdRepoServerPlaintext, argocdRepoServerStrictTLS)
tlsConfig := apiclient.TLSConfiguration{
DisableTLS: argocdRepoServerPlaintext,
StrictValidation: argocdRepoServerStrictTLS,
}
if !tlsConfig.DisableTLS && tlsConfig.StrictValidation {
pool, err := tls.LoadX509CertPool(
fmt.Sprintf("%s/reposerver/tls/tls.crt", env.StringFromEnv(common.EnvAppConfigPath, common.DefaultAppConfigPath)),
fmt.Sprintf("%s/reposerver/tls/ca.crt", env.StringFromEnv(common.EnvAppConfigPath, common.DefaultAppConfigPath)),
)
if err != nil {
return err
}
tlsConfig.Certificates = pool
}
repoClientset := apiclient.NewRepoServerClientset(argocdRepoServer, 5, tlsConfig)
argocdService, err := service.NewArgoCDService(k8sClient, namespace, repoClientset)
if err != nil {
return err
}
Expand Down
22 changes: 21 additions & 1 deletion cmd/argocd/commands/admin/notifications.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
package admin

import (
"fmt"
"log"

"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"

"github.com/argoproj/argo-cd/v2/common"
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
"github.com/argoproj/argo-cd/v2/util/env"
service "github.com/argoproj/argo-cd/v2/util/notification/argocd"
settings "github.com/argoproj/argo-cd/v2/util/notification/settings"
"github.com/argoproj/argo-cd/v2/util/tls"

"github.com/argoproj/notifications-engine/pkg/cmd"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -39,7 +44,22 @@ func NewNotificationsCommand() *cobra.Command {
if err != nil {
log.Fatalf("Failed to parse k8s config: %v", err)
}
argocdService, err = service.NewArgoCDService(kubernetes.NewForConfigOrDie(k8sCfg), ns, argocdRepoServer, argocdRepoServerPlaintext, argocdRepoServerStrictTLS)
tlsConfig := apiclient.TLSConfiguration{
DisableTLS: argocdRepoServerPlaintext,
StrictValidation: argocdRepoServerStrictTLS,
}
if !tlsConfig.DisableTLS && tlsConfig.StrictValidation {
pool, err := tls.LoadX509CertPool(
fmt.Sprintf("%s/reposerver/tls/tls.crt", env.StringFromEnv(common.EnvAppConfigPath, common.DefaultAppConfigPath)),
fmt.Sprintf("%s/reposerver/tls/ca.crt", env.StringFromEnv(common.EnvAppConfigPath, common.DefaultAppConfigPath)),
)
if err != nil {
log.Fatalf("Failed to load tls certs: %v", err)
}
tlsConfig.Certificates = pool
}
repoClientset := apiclient.NewRepoServerClientset(argocdRepoServer, 5, tlsConfig)
argocdService, err = service.NewArgoCDService(kubernetes.NewForConfigOrDie(k8sCfg), ns, repoClientset)
if err != nil {
log.Fatalf("Failed to initialize Argo CD service: %v", err)
}
Expand Down
8 changes: 5 additions & 3 deletions common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ const (

// Kubernetes ConfigMap and Secret resource names which hold Argo CD settings
const (
ArgoCDConfigMapName = "argocd-cm"
ArgoCDSecretName = "argocd-secret"
ArgoCDRBACConfigMapName = "argocd-rbac-cm"
ArgoCDConfigMapName = "argocd-cm"
ArgoCDSecretName = "argocd-secret"
ArgoCDNotificationsConfigMapName = "argocd-notifications-cm"
ArgoCDNotificationsSecretName = "argocd-notifications-secret"
ArgoCDRBACConfigMapName = "argocd-rbac-cm"
// Contains SSH known hosts data for connecting repositories. Will get mounted as volume to pods
ArgoCDKnownHostsConfigMapName = "argocd-ssh-known-hosts-cm"
// Contains TLS certificate data for connecting repositories. Will get mounted as volume to pods
Expand Down
99 changes: 99 additions & 0 deletions docs/proposals/notifications-API.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
---
title: Subscribe to a notification from the Application Details page
authors:
- "@aborilov"
sponsors:
- TBD
reviewers:
- "@alexmt"
- TBD
approvers:
- "@alexmt"
- TBD

creation-date: 2022-08-16
last-updated: 2022-08-16
---

# Subscribe to a notification from the Application Details page

Provide the ability to subscribe to a notification from the Application Details page

## Summary

Allow users to subscribe application with a notification from the Application Details page
using provided instruments for selecting available triggers and services.

## Motivation

It is already possible to subscribe to notifications by modifying annotations however this is a pretty
poor user experience. Users have to understand annotation structure and have to find available services and triggers in configmap.

### Goals

Be able to subscribe to a notification from the Application Details page without forcing users to read the notification configmap.

### Non-Goals

We provide only ability to select existing services and triggers, we don't provide instruments to add/edit/delete notification services and triggers.

## Proposal

Two changes are required:

* Implement notifications API that would expose a list of configured triggers and services
* Implement UI that leverages notifications API and helps users to create a correct annotation.

### Use cases

Add a list of detailed use cases this enhancement intends to take care of.

#### Use case 1:
As a user, I would like to be able to subscribe application to a notification from the Application Details Page
without reading knowing of annotation format and reading notification configmap.

### Implementation Details/Notes/Constraints [optional]

Three read-only API endpoints will be added to provide a list of notification services, triggers, and templates.

```
message Triggers { repeated string triggers = 1; }
message TriggersListRequest {}
message Services { repeated string services = 1; }
message ServicesListRequest {}
message Templates { repeated string templates = 1; }
message TemplatesListRequest {}
service NotificationService {
rpc ListTriggers(TriggersListRequest) returns (Triggers) {
option (google.api.http).get = "/api/v1/notifications/triggers";
}
rpc ListServices(ServicesListRequest) returns (Services) {
option (google.api.http).get = "/api/v1/notifications/services";
}
rpc ListTemplates(TemplatesListRequest) returns (Templates) {
option (google.api.http).get = "/api/v1/notifications/templates";
}
}
```

### Detailed examples

### Security Considerations

New API endpoints are available only for authenticated users. API endpoints response does not contain any sensitive data.

### Risks and Mitigations

TBD

### Upgrade / Downgrade Strategy

By default, we don't have a notification configmap in the system; in that case, API should return an empty list instead of erroring.

## Drawbacks


## Alternatives

Continue to do that manually.

3 changes: 2 additions & 1 deletion hack/generate-proto.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ set -o pipefail

PROJECT_ROOT=$(cd $(dirname ${BASH_SOURCE})/..; pwd)
PATH="${PROJECT_ROOT}/dist:${PATH}"
GOPATH=$(go env GOPATH)

# output tool versions
protoc --version
Expand Down Expand Up @@ -121,7 +122,7 @@ clean_swagger() {
}

echo "If additional types are added, the number of expected collisions may need to be increased"
EXPECTED_COLLISION_COUNT=62
EXPECTED_COLLISION_COUNT=64
collect_swagger server ${EXPECTED_COLLISION_COUNT}
clean_swagger server
clean_swagger reposerver
Expand Down
Loading

0 comments on commit 37ad433

Please sign in to comment.