Skip to content
This repository has been archived by the owner on Feb 7, 2024. It is now read-only.

Commit

Permalink
feat: support loading commit metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
alexmt committed May 10, 2020
1 parent bc5ab4b commit fe6fd03
Show file tree
Hide file tree
Showing 23 changed files with 414 additions and 56 deletions.
2 changes: 1 addition & 1 deletion builtin/templates_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func triggerWithTemplate(name string) (triggers.Trigger, error) {
Name: "test",
Template: name,
Condition: "true",
}})
}}, nil)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion builtin/triggers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func TestBuiltInTriggers(t *testing.T) {
if testCase, ok := testCases[trigger.Name]; !ok {
t.Fatalf("No tests for trigger %s", trigger.Name)
} else {
builtInTriggers, err := triggers.GetTriggers(Templates, []triggers.NotificationTrigger{trigger})
builtInTriggers, err := triggers.GetTriggers(Templates, []triggers.NotificationTrigger{trigger}, nil)
assert.NoError(t, err)
item := builtInTriggers[trigger.Name]
for i := range testCase.negativeInputs {
Expand Down
15 changes: 12 additions & 3 deletions cmd/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/argoproj-labs/argocd-notifications/builtin"
"github.com/argoproj-labs/argocd-notifications/controller"
"github.com/argoproj-labs/argocd-notifications/notifiers"
"github.com/argoproj-labs/argocd-notifications/shared/argocd"
"github.com/argoproj-labs/argocd-notifications/shared/cmd"
"github.com/argoproj-labs/argocd-notifications/shared/settings"
"github.com/argoproj-labs/argocd-notifications/triggers"
Expand Down Expand Up @@ -52,6 +53,7 @@ func newControllerCommand() *cobra.Command {
appLabelSelector string
logLevel string
metricsPort int
argocdRepoServer string
)
var command = cobra.Command{
Use: "controller",
Expand Down Expand Up @@ -80,6 +82,12 @@ func newControllerCommand() *cobra.Command {
}
log.SetLevel(level)

argocdService, err := argocd.NewArgoCDService(k8sClient, namespace, argocdRepoServer)
if err != nil {
return err
}
defer argocdService.Close()

registry := controller.NewMetricsRegistry()
http.Handle("/metrics", promhttp.HandlerFor(prometheus.Gatherers{registry, prometheus.DefaultGatherer}, promhttp.HandlerOpts{}))

Expand All @@ -90,7 +98,7 @@ func newControllerCommand() *cobra.Command {
log.Infof("loading configuration %d", metricsPort)

var cancelPrev context.CancelFunc
watchConfig(context.Background(), k8sClient, namespace, func(triggers map[string]triggers.Trigger, notifiers map[string]notifiers.Notifier, cfg *settings.Config) error {
watchConfig(context.Background(), argocdService, k8sClient, namespace, func(triggers map[string]triggers.Trigger, notifiers map[string]notifiers.Notifier, cfg *settings.Config) error {
if cancelPrev != nil {
log.Info("Settings had been updated. Restarting controller...")
cancelPrev()
Expand Down Expand Up @@ -121,10 +129,11 @@ func newControllerCommand() *cobra.Command {
command.Flags().StringVar(&namespace, "namespace", "", "Namespace which controller handles. Current namespace if empty.")
command.Flags().StringVar(&logLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
command.Flags().IntVar(&metricsPort, "metrics-port", defaultMetricsPort, "Metrics port")
command.Flags().StringVar(&argocdRepoServer, "argocd-repo-server", "argocd-repo-server:8081", "Argo CD repo server address")
return &command
}

func watchConfig(ctx context.Context, clientset kubernetes.Interface, namespace string, callback func(map[string]triggers.Trigger, map[string]notifiers.Notifier, *settings.Config) error) {
func watchConfig(ctx context.Context, argocdService argocd.Service, clientset kubernetes.Interface, namespace string, callback func(map[string]triggers.Trigger, map[string]notifiers.Notifier, *settings.Config) error) {
var secret *v1.Secret
var configMap *v1.ConfigMap
lock := &sync.Mutex{}
Expand All @@ -138,7 +147,7 @@ func watchConfig(ctx context.Context, clientset kubernetes.Interface, namespace
configMap = newConfigMap
}
if secret != nil && configMap != nil {
if t, n, c, err := settings.ParseConfig(configMap, secret, defaultCfg); err == nil {
if t, n, c, err := settings.ParseConfig(configMap, secret, defaultCfg, argocdService); err == nil {
if err = callback(t, n, c); err != nil {
log.Fatalf("Failed to start controller: %v", err)
}
Expand Down
8 changes: 7 additions & 1 deletion cmd/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,22 @@ import (
"context"
"testing"

"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/fake"

"github.com/argoproj-labs/argocd-notifications/notifiers"
"github.com/argoproj-labs/argocd-notifications/shared/argocd/mocks"
"github.com/argoproj-labs/argocd-notifications/shared/settings"
"github.com/argoproj-labs/argocd-notifications/triggers"
)

func TestWatchConfig(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

ctx, cancel := context.WithCancel(context.Background())
defer cancel()
configMap := &v1.ConfigMap{
Expand Down Expand Up @@ -46,8 +51,9 @@ templates:

triggersMap := make(map[string]triggers.Trigger)
notifiersMap := make(map[string]notifiers.Notifier)
argocdService := mocks.NewMockService(ctrl)
clientset := fake.NewSimpleClientset(configMap, secret)
watchConfig(ctx, clientset, "default", func(t map[string]triggers.Trigger, n map[string]notifiers.Notifier, cfg *settings.Config) error {
watchConfig(ctx, argocdService, clientset, "default", func(t map[string]triggers.Trigger, n map[string]notifiers.Notifier, cfg *settings.Config) error {
triggersMap = t
notifiersMap = n
return nil
Expand Down
42 changes: 40 additions & 2 deletions cmd/tools/context.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package tools

import (
"context"
"io"
"io/ioutil"
"path/filepath"
"sync"

"github.com/ghodss/yaml"
v1 "k8s.io/api/core/v1"
Expand All @@ -14,18 +16,54 @@ import (
"k8s.io/client-go/tools/clientcmd"

"github.com/argoproj-labs/argocd-notifications/notifiers"
"github.com/argoproj-labs/argocd-notifications/shared/argocd"
"github.com/argoproj-labs/argocd-notifications/shared/clients"
"github.com/argoproj-labs/argocd-notifications/shared/settings"
"github.com/argoproj-labs/argocd-notifications/triggers"
"github.com/argoproj-labs/argocd-notifications/triggers/expr/shared"
)

type clientsSource = func() (kubernetes.Interface, dynamic.Interface, string, error)

type commandContext struct {
configMapPath string
secretPath string
defaultCfg settings.Config
stdout io.Writer
stderr io.Writer
getK8SClients func() (kubernetes.Interface, dynamic.Interface, string, error)
getK8SClients clientsSource
argocdService *lazyArgocdServiceInitializer
}

type lazyArgocdServiceInitializer struct {
argocdRepoServer *string
argocdService argocd.Service
init sync.Once
getK8SClients clientsSource
}

func (svc *lazyArgocdServiceInitializer) initArgoCDService() error {
k8sClient, _, ns, err := svc.getK8SClients()
if err != nil {
return err
}
argocdService, err := argocd.NewArgoCDService(k8sClient, ns, *svc.argocdRepoServer)
if err != nil {
return err
}
svc.argocdService = argocdService
return nil
}

func (svc *lazyArgocdServiceInitializer) GetCommitMetadata(ctx context.Context, repoURL string, commitSHA string) (*shared.CommitMetadata, error) {
var err error
svc.init.Do(func() {
err = svc.initArgoCDService()
})
if err != nil {
return nil, err
}
return svc.argocdService.GetCommitMetadata(ctx, repoURL, commitSHA)
}

func getK8SClients(clientConfig clientcmd.ClientConfig) (kubernetes.Interface, dynamic.Interface, string, error) {
Expand Down Expand Up @@ -93,7 +131,7 @@ func (c *commandContext) getConfig() (map[string]triggers.Trigger, map[string]no
}
}

return settings.ParseConfig(&configMap, &secret, c.defaultCfg)
return settings.ParseConfig(&configMap, &secret, c.defaultCfg, &lazyArgocdServiceInitializer{})
}

func (c *commandContext) loadApplication(application string) (*unstructured.Unstructured, error) {
Expand Down
10 changes: 6 additions & 4 deletions cmd/tools/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ argocd-notifications tools template notify app-sync-succeeded guestbook
Name: "__test__",
Template: name,
Condition: "true",
}})
}}, cmdContext.argocdService)
if err != nil {
_, _ = fmt.Fprintf(cmdContext.stderr, "failed to parse config: %v\n", err)
return nil
Expand All @@ -90,18 +90,20 @@ argocd-notifications tools template notify app-sync-succeeded guestbook
notifierType := parts[0]
notifier, ok := notifiersByName[notifierType]
if !ok {
_, _ = fmt.Fprintf(cmdContext.stderr, "%s is not valid recipient type.", notifierType)
_, _ = fmt.Fprintf(cmdContext.stderr, "%s is not valid recipient type.\n", notifierType)
return nil
}

ctx := sharedrecipients.CopyStringMap(config.Context)
ctx["notificationType"] = notifierType
notification, err := trigger.FormatNotification(app, ctx)
if err != nil {
_, _ = fmt.Fprintf(cmdContext.stderr, "failed to format notification: %v", err)
_, _ = fmt.Fprintf(cmdContext.stderr, "failed to format notification: %v\n", err)
return nil
}
if err = notifier.Send(*notification, parts[1]); err != nil {
_, _ = fmt.Fprintf(cmdContext.stderr, "failed to notify '%s': %v", recipient, err)
_, _ = fmt.Fprintf(cmdContext.stderr, "failed to notify '%s': %v\n", recipient, err)
return nil
}
}

Expand Down
13 changes: 9 additions & 4 deletions cmd/tools/tools.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,12 @@ func printFormatted(input interface{}, output string, out io.Writer) error {

func NewToolsCommand(defaultCfg settings.Config) *cobra.Command {
var (
cmdContext = commandContext{
defaultCfg: defaultCfg,
stdout: os.Stdout,
stderr: os.Stderr,
argocdRepoServer string
cmdContext = commandContext{
defaultCfg: defaultCfg,
stdout: os.Stdout,
stderr: os.Stderr,
argocdService: &lazyArgocdServiceInitializer{argocdRepoServer: &argocdRepoServer},
}
)
var command = cobra.Command{
Expand All @@ -72,9 +74,12 @@ func NewToolsCommand(defaultCfg settings.Config) *cobra.Command {
"config-map", "", "argocd-notifications-cm.yaml file path")
command.PersistentFlags().StringVar(&cmdContext.secretPath,
"secret", "", "argocd-notifications-secret.yaml file path. Use empty secret if provided value is ':empty'")
command.PersistentFlags().StringVar(&argocdRepoServer,
"argocd-repo-server", "argocd-repo-server:8081", "Argo CD repo server address")
clientConfig := cmd.AddK8SFlagsToCmd(&command)
cmdContext.getK8SClients = func() (kubernetes.Interface, dynamic.Interface, string, error) {
return getK8SClients(clientConfig)
}
cmdContext.argocdService.getK8SClients = cmdContext.getK8SClients
return &command
}
3 changes: 2 additions & 1 deletion docs/argocd-notifications-cm.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,6 @@ data:
body: |
Application details: {{.context.argocdUrl}}/applications/{{.app.metadata.name}}.
# Override one field in built-in template
- name: app-sync-status
- name: on-sync-succeeded
title: Application {{.app.metadata.name}} sync status is {{.app.status.sync.status}}
body: "{{call .git.GetCommitMetadata .app.status.sync.revision}}"
17 changes: 17 additions & 0 deletions docs/triggers_and_templates/functions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
### Functions

Following functions can be used with-in the condition expression:

**time** (v0.5+)

* `time.Now()` - executes function built-in Golang [time.Now](https://golang.org/pkg/time/#Now) function.
* `time.Parse(val string)` - parses specified string using RFC3339 layout

**repo** (v0.6+)

* `repo.RepoURLToHTTPS(url string)` - transforms given GIT URL into HTTPs format
* `repo.FullNameByRepoURL(url string)` - returns repository URL full name (<owner>/<repoName>). Currently supports only Github, Gitlab and Bitbucket.

**git** (v0.7+)

* `git.GetCommitMetadata(sha string)` - returns commit metadata
16 changes: 2 additions & 14 deletions docs/triggers_and_templates/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,6 @@ evaluation is powered by [antonmedv/expr](https://github.com/antonmedv/expr). Th
at [Language-Definition.md](https://github.com/antonmedv/expr/blob/master/docs/Language-Definition.md).
* **enabled** - flag that indicates if trigger is enabled or not. By default trigger is enabled.

### Functions

Following functions can be used with-in the condition expression:

**time** (v0.5+)

* `time.Now` - executes function built-in Golang [time.Now](https://golang.org/pkg/time/#Now) function.
* `time.Parse` - parses specified string using RFC3339 layout

**repo** (v0.6+)

* `repo.RepoURLToHTTPS` - transforms given GIT URL into HTTPs format
* `repo.FullNameByRepoURL` - returns repository URL full name (<owner>/<repoName>). Currently supports only Github, Gitlab and Bitbucket.

## Templates

The notification template is used to generate the notification content. The template is leveraging
Expand All @@ -61,3 +47,5 @@ Each template has access to the `app` and `context` fields:

- `app` holds the application object.
- `context` is user defined string map and might include any string keys and values.

Both templates and triggers has access to set of [functions](./functions.md)
9 changes: 8 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,39 @@ require (
github.com/Masterminds/semver v1.5.0 // indirect
github.com/Masterminds/sprig v2.22.0+incompatible
github.com/antonmedv/expr v1.4.1
github.com/argoproj/argo-cd v1.5.4
github.com/argoproj/pkg v0.0.0-20200424003221-9b858eff18a1 // indirect
github.com/evanphx/json-patch v4.5.0+incompatible // indirect
github.com/ghodss/yaml v1.0.0
github.com/gobwas/glob v0.2.3 // indirect
github.com/gogo/protobuf v1.3.1 // indirect
github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7 // indirect
github.com/golang/mock v1.3.1
github.com/googleapis/gnostic v0.3.1 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 // indirect
github.com/huandu/xstrings v1.3.0 // indirect
github.com/imdario/mergo v0.3.8 // indirect
github.com/jstemmer/go-junit-report v0.9.1 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
github.com/magiconair/properties v1.8.0
github.com/mitchellh/copystructure v1.0.0 // indirect
github.com/nlopes/slack v0.6.0
github.com/olekukonko/tablewriter v0.0.4
github.com/opsgenie/opsgenie-go-sdk-v2 v1.0.5
github.com/prometheus/client_golang v1.6.0
github.com/robfig/cron v1.2.0 // indirect
github.com/sirupsen/logrus v1.4.2
github.com/spf13/cobra v0.0.5
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/testify v1.4.0
github.com/whilp/git-urls v0.0.0-20191001220047-6db9661140c0
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4 // indirect
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 // indirect
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6 // indirect
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
gomodules.xyz/notify v0.1.0
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/src-d/go-git.v4 v4.13.1 // indirect
k8s.io/api v0.0.0-20191114100352-16d7abae0d2a
k8s.io/apimachinery v0.0.0-20191028221656-72ed19daf4bb
k8s.io/client-go v0.0.0-20191114101535-6c5935290e33
Expand Down
Loading

0 comments on commit fe6fd03

Please sign in to comment.