diff --git a/builtin/templates_test.go b/builtin/templates_test.go index dad6a5a4..7d852553 100644 --- a/builtin/templates_test.go +++ b/builtin/templates_test.go @@ -15,7 +15,7 @@ func triggerWithTemplate(name string) (triggers.Trigger, error) { Name: "test", Template: name, Condition: "true", - }}) + }}, nil) if err != nil { return nil, err } diff --git a/builtin/triggers_test.go b/builtin/triggers_test.go index fdc83ccb..52bfbd3d 100644 --- a/builtin/triggers_test.go +++ b/builtin/triggers_test.go @@ -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 { diff --git a/cmd/controller.go b/cmd/controller.go index 9cb04c21..9bebebf4 100644 --- a/cmd/controller.go +++ b/cmd/controller.go @@ -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" @@ -52,6 +53,7 @@ func newControllerCommand() *cobra.Command { appLabelSelector string logLevel string metricsPort int + argocdRepoServer string ) var command = cobra.Command{ Use: "controller", @@ -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{})) @@ -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() @@ -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{} @@ -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) } diff --git a/cmd/controller_test.go b/cmd/controller_test.go index 5287c352..20dd7071 100644 --- a/cmd/controller_test.go +++ b/cmd/controller_test.go @@ -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{ @@ -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 diff --git a/cmd/tools/context.go b/cmd/tools/context.go index ff7ab81a..0db2341c 100644 --- a/cmd/tools/context.go +++ b/cmd/tools/context.go @@ -1,9 +1,11 @@ package tools import ( + "context" "io" "io/ioutil" "path/filepath" + "sync" "github.com/ghodss/yaml" v1 "k8s.io/api/core/v1" @@ -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) { @@ -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) { diff --git a/cmd/tools/template.go b/cmd/tools/template.go index 91ed1cab..640e7924 100644 --- a/cmd/tools/template.go +++ b/cmd/tools/template.go @@ -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 @@ -90,7 +90,7 @@ 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 } @@ -98,10 +98,12 @@ argocd-notifications tools template notify app-sync-succeeded guestbook 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 } } diff --git a/cmd/tools/tools.go b/cmd/tools/tools.go index 35cdfe44..992b07bc 100644 --- a/cmd/tools/tools.go +++ b/cmd/tools/tools.go @@ -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{ @@ -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 } diff --git a/docs/argocd-notifications-cm.yaml b/docs/argocd-notifications-cm.yaml index a5ee3f44..150ecd2d 100644 --- a/docs/argocd-notifications-cm.yaml +++ b/docs/argocd-notifications-cm.yaml @@ -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}}" diff --git a/docs/triggers_and_templates/functions.md b/docs/triggers_and_templates/functions.md new file mode 100644 index 00000000..515d49ab --- /dev/null +++ b/docs/triggers_and_templates/functions.md @@ -0,0 +1,54 @@ +## Functions + +Both templates and triggers have access to the set of functions. + +Trigger example: + +```yaml +name: app-operation-stuck +condition: time.Now().Sub(time.Parse(app.status.operationState.startedAt)).Minutes() >= 5 +template: my-template +``` + +Template example: +```yaml +name: my-template +title: Application {{.app.metadata.name}} sync status is {{.app.status.sync.status}} +body: "Author: {{(call .repo.GetCommitMetadata .app.status.sync.revision).Author}}" +``` + +### **time** +Time related functions. + +
+**`time.Now() Time`** + +Executes function built-in Golang [time.Now](https://golang.org/pkg/time/#Now) function. +Returns an instance of Golang [Time](https://golang.org/pkg/time/#Time). + +
+**`time.Parse(val string) Time`** + +Parses specified string using RFC3339 layout. Returns an instance of Golang [Time](https://golang.org/pkg/time/#Time). + +### **repo** +Functions that provide additional information about Application source repository. +
+**`repo.RepoURLToHTTPS(url string) string`** + +Transforms given GIT URL into HTTPs format. + +
+**`repo.FullNameByRepoURL(url string) string`** + +Returns repository URL full name `(/)`. Currently supports only Github, Gitlab and Bitbucket. + +
+**`repo.GetCommitMetadata(sha string) CommitMetadata`** + +Returns commit metadata. The commit must belong to the application source repository. `CommitMetadata` fields: + +* `Message string` commit message +* `Author string` - commit author +* `Date time.Time` - commit creation date +* `Tags []string` - Associated tags diff --git a/docs/triggers_and_templates/index.md b/docs/triggers_and_templates/index.md index 7f4876a7..81358cb4 100644 --- a/docs/triggers_and_templates/index.md +++ b/docs/triggers_and_templates/index.md @@ -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 (/). Currently supports only Github, Gitlab and Bitbucket. - ## Templates The notification template is used to generate the notification content. The template is leveraging @@ -61,3 +47,4 @@ 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. + diff --git a/go.mod b/go.mod index 4d96a4f0..aca6f86c 100644 --- a/go.mod +++ b/go.mod @@ -7,14 +7,20 @@ 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 @@ -22,17 +28,18 @@ require ( 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 diff --git a/go.sum b/go.sum index f38f1e87..eabe69d3 100644 --- a/go.sum +++ b/go.sum @@ -18,18 +18,27 @@ github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF0 github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/PuerkitoBio/purell v1.0.0 h1:0GoNN3taZV6QI81IXgCbxMyEaJDXMSIjArYBCYzVVvs= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2 h1:JCHLVE3B+kJde7bIEo5N4J+ZbLhp0J1Fs+ulyRws4gE= github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antlr/antlr4 v0.0.0-20191011202612-ad2bd05285ca h1:QHbltbNkVcw97h4zA/L8gA4o3dJiFvBZ0gyZHrYXHbs= github.com/antlr/antlr4 v0.0.0-20191011202612-ad2bd05285ca/go.mod h1:T7PbCXFs94rrTttyxjbyT5+/1V8T2TYDejxUfHJjw1Y= github.com/antonmedv/expr v1.4.1 h1:ienOlev0YSJVJbcWwFOVpu2Uxu4OVBMXtp9YGifrxGQ= github.com/antonmedv/expr v1.4.1/go.mod h1:xesgliOuukGf21740qhh8PvFdN66yZ9lJJ/PzSFAmzI= github.com/appscode/go v0.0.0-20190808133642-1d4ef1f1c1e0/go.mod h1:iy07dV61Z7QQdCKJCIvUoDL21u6AIceRhZzyleh2ymc= +github.com/argoproj/argo-cd v1.5.4 h1:44NnPUZrB5TPntrNHqKXhphekTv3WaxSmIFAbg1oy2g= +github.com/argoproj/argo-cd v1.5.4/go.mod h1:UPOPiF6Y1y/oTL3X1KcuLbu7ljD7f4+SIaXvlowBmhE= +github.com/argoproj/pkg v0.0.0-20200424003221-9b858eff18a1 h1:BCuMRvKYHPuPN2Gep3uY+XOScGNCBFbF/pXYlhmyJjg= +github.com/argoproj/pkg v0.0.0-20200424003221-9b858eff18a1/go.mod h1:2EZ44RG/CcgtPTwrRR0apOc7oU6UIw8GjCUJWZ8X3bM= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/beevik/ntp v0.2.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -47,6 +56,7 @@ github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8Nz github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -55,7 +65,10 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 h1:H2pdYOb3KQ1/YsqVWoWNLQO+fusocsw354rqGTZtAgw= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= +github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M= github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= @@ -64,6 +77,7 @@ github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqL github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/flosch/pongo2 v0.0.0-20181225140029-79872a7b2769/go.mod h1:tbAXHifHQWNSpWbiJHpJTZH5fi3XHhDMdP//vuz9WS4= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= @@ -71,21 +85,31 @@ github.com/gdamore/tcell v1.1.2/go.mod h1:h3kq4HO9l2On+V9ed8w8ewqQEmGCSSHOgQ+2h8 github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1 h1:wSt/4CYxs70xbATrGXhokKF1i0tZjENLOo1ioIO13zk= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9 h1:tF+augKRWlWx0J0B7ZyyKSiTyV6E1zZe+7b3qQlcEf8= github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501 h1:C1JKChikHGpXwT5UQDFaryIpDtyyGL/CR6C2kB7F1oc= github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87 h1:zP3nY8Tk2E6RTkqGYrarZXuzh+ffyLDljLxCy1iJw80= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I= github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7 h1:u4bArs140e9+AfE52mFHOXVFnOSBJBRlzTHrOPLOIhE= @@ -130,6 +154,8 @@ github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoA github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 h1:0IKlLyQ3Hs9nDaiK5cSHAGmcQEIC8l2Ts1u6x5Dfrqg= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.0/go.mod h1:mJzapYve32yjrKlk9GbyCZHuPgZsrbyIbyKhSzOpg6s= github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-retryablehttp v0.5.1 h1:Vsx5XKPqPs3M6sM4U4GWyUqFS8aBiL9U5gkgvpkg4SE= @@ -149,6 +175,9 @@ github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jaytaylor/html2text v0.0.0-20190408195923-01ec452cbe43/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -165,6 +194,11 @@ github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0b github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY= +github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -174,6 +208,7 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s= @@ -182,6 +217,7 @@ github.com/lusis/slack-test v0.0.0-20190426140909-c40012f20018/go.mod h1:sFlOUpQ github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailgun/mailgun-go v2.0.0+incompatible/go.mod h1:NWTyU+O4aczg/nsGhQnvHL6v2n5Gy6Sv5tNDVvC6FbU= +github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a h1:TpvdAwDAt1K4ANVOfcihouRdvP+MgAfDWwBuct4l6ZY= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= @@ -193,6 +229,7 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0j github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= @@ -221,8 +258,10 @@ github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opsgenie/opsgenie-go-sdk-v2 v1.0.5 h1:AnS8ZCC5dle8P4X4FZ+IOlX9v0jAkCMiZDIzRnYwBbs= github.com/opsgenie/opsgenie-go-sdk-v2 v1.0.5/go.mod h1:f0ezb0R/mrB9Hpm5RrIS6EX3ydjsR2nAB88nYYXZcNY= +github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -248,11 +287,15 @@ github.com/prometheus/procfs v0.0.11 h1:DhHlBtkHWPYi8O2y31JkK0TF+DGM+51OopZjH/Ia github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/rivo/tview v0.0.0-20190515161233-bd836ef13b4b/go.mod h1:+rKjP5+h9HMwWRpAfhIkkQ9KE3m3Nz5rwn7YtUpwgqk= github.com/rivo/uniseg v0.0.0-20190513083848-b9f5b9457d44/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ= +github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/sanity-io/litter v1.1.0 h1:BllcKWa3VbZmOZbDCoszYLk7zCsKHz5Beossi8SUcTc= github.com/sanity-io/litter v1.1.0/go.mod h1:CJ0VCw2q4qKU7LaQr3n7UOSHzgEMgcGco7N/SkZQPjw= +github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= @@ -270,9 +313,12 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4= +github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -281,17 +327,25 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/whilp/git-urls v0.0.0-20191001220047-6db9661140c0 h1:qqllXPzXh+So+mmANlX/gCJrgo+1kQyshMoQ+NASzm0= github.com/whilp/git-urls v0.0.0-20191001220047-6db9661140c0/go.mod h1:2rx5KE5FLD0HRfkkpyn8JwbVLBdhgeiOb2D2D9LLKM4= +github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70= +github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190422183909-d864b10871cd/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4 h1:ydJNl0ENAG67pFbB+9tfhiL2pYqLhfoaZFw/cjLhY4A= golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -307,6 +361,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190607181551-461777fb6f67/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -330,10 +386,12 @@ golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456 h1:ng0gs1AKnRRuEMZoTLLlbOd+C17zUDepwGQBb/n+JVg= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -347,6 +405,7 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -356,6 +415,7 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3 golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/envconfig v1.3.1-0.20190308184047-426f31af0d45 h1:juzzlx91nWAOsHuOVfXZPMXHtJEKouZvY9bBbwlOeYs= gomodules.xyz/envconfig v1.3.1-0.20190308184047-426f31af0d45/go.mod h1:41y72mzHT7+jFNgyBpJRrZWuZJcLmLrTpq6iGgOFJMQ= @@ -368,7 +428,11 @@ google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4 google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7 h1:ZUjXAXmrAyrmmCPHgCA/vChHcpsX27MZ3yBonD/z1KE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -392,8 +456,15 @@ gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= +gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg= +gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= +gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= +gopkg.in/src-d/go-git.v4 v4.13.1 h1:SRtFyV8Kxc0UP7aCHcijOMQGPxHSmMOPrzulQWolkYE= +gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= diff --git a/mkdocs.yml b/mkdocs.yml index 9ab85951..2f7ee73c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -22,6 +22,7 @@ nav: - Overview: index.md - Triggers and Templates: - triggers_and_templates/index.md + - triggers_and_templates/functions.md - triggers_and_templates/slack.md - Notification Services: - services/overview.md diff --git a/shared/argocd/mocks/service.go b/shared/argocd/mocks/service.go new file mode 100644 index 00000000..aeb78e55 --- /dev/null +++ b/shared/argocd/mocks/service.go @@ -0,0 +1,50 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/argoproj-labs/argocd-notifications/shared/argocd (interfaces: Service) + +// Package mocks is a generated GoMock package. +package mocks + +import ( + context "context" + shared "github.com/argoproj-labs/argocd-notifications/triggers/expr/shared" + gomock "github.com/golang/mock/gomock" + reflect "reflect" +) + +// MockService is a mock of Service interface +type MockService struct { + ctrl *gomock.Controller + recorder *MockServiceMockRecorder +} + +// MockServiceMockRecorder is the mock recorder for MockService +type MockServiceMockRecorder struct { + mock *MockService +} + +// NewMockService creates a new mock instance +func NewMockService(ctrl *gomock.Controller) *MockService { + mock := &MockService{ctrl: ctrl} + mock.recorder = &MockServiceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockService) EXPECT() *MockServiceMockRecorder { + return m.recorder +} + +// GetCommitMetadata mocks base method +func (m *MockService) GetCommitMetadata(arg0 context.Context, arg1, arg2 string) (*shared.CommitMetadata, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetCommitMetadata", arg0, arg1, arg2) + ret0, _ := ret[0].(*shared.CommitMetadata) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetCommitMetadata indicates an expected call of GetCommitMetadata +func (mr *MockServiceMockRecorder) GetCommitMetadata(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCommitMetadata", reflect.TypeOf((*MockService)(nil).GetCommitMetadata), arg0, arg1, arg2) +} diff --git a/shared/argocd/service.go b/shared/argocd/service.go new file mode 100644 index 00000000..904b80bc --- /dev/null +++ b/shared/argocd/service.go @@ -0,0 +1,71 @@ +package argocd + +import ( + "context" + + "github.com/argoproj/argo-cd/reposerver/apiclient" + "github.com/argoproj/argo-cd/util/db" + "github.com/argoproj/argo-cd/util/settings" + log "github.com/sirupsen/logrus" + "k8s.io/client-go/kubernetes" + + "github.com/argoproj-labs/argocd-notifications/triggers/expr/shared" +) + +//go:generate mockgen -destination=./mocks/service.go -package=mocks github.com/argoproj-labs/argocd-notifications/shared/argocd Service + +type Service interface { + GetCommitMetadata(ctx context.Context, repoURL string, commitSHA string) (*shared.CommitMetadata, error) +} + +func NewArgoCDService(clientset kubernetes.Interface, namespace string, repoServerAddress string) (*argoCDService, error) { + ctx, cancel := context.WithCancel(context.Background()) + settingsMgr := settings.NewSettingsManager(ctx, clientset, namespace) + repoClientset := apiclient.NewRepoServerClientset(repoServerAddress, 5) + closer, repoClient, err := repoClientset.NewRepoServerClient() + if err != nil { + cancel() + return nil, err + } + + dispose := func() { + cancel() + if err := closer.Close(); err != nil { + log.Warnf("Failed to close repo server connection: %v", err) + } + } + return &argoCDService{settingsMgr: settingsMgr, namespace: namespace, repoServerClient: repoClient, dispose: dispose}, nil +} + +type argoCDService struct { + clientset kubernetes.Interface + namespace string + settingsMgr *settings.SettingsManager + repoServerClient apiclient.RepoServerServiceClient + dispose func() +} + +func (svc *argoCDService) GetCommitMetadata(ctx context.Context, repoURL string, commitSHA string) (*shared.CommitMetadata, error) { + argocdDB := db.NewDB(svc.namespace, svc.settingsMgr, svc.clientset) + repo, err := argocdDB.GetRepository(ctx, repoURL) + if err != nil { + return nil, err + } + metadata, err := svc.repoServerClient.GetRevisionMetadata(ctx, &apiclient.RepoServerRevisionMetadataRequest{ + Repo: repo, + Revision: commitSHA, + }) + if err != nil { + return nil, err + } + return &shared.CommitMetadata{ + Message: metadata.Message, + Author: metadata.Author, + Date: metadata.Date.Time, + Tags: metadata.Tags, + }, nil +} + +func (svc *argoCDService) Close() { + svc.dispose() +} diff --git a/shared/settings/settings.go b/shared/settings/settings.go index a9d4f8ea..2a96f745 100644 --- a/shared/settings/settings.go +++ b/shared/settings/settings.go @@ -4,6 +4,7 @@ import ( "encoding/json" "github.com/argoproj-labs/argocd-notifications/notifiers" + "github.com/argoproj-labs/argocd-notifications/shared/argocd" "github.com/argoproj-labs/argocd-notifications/triggers" "github.com/ghodss/yaml" @@ -133,7 +134,7 @@ func (cfg *Config) Merge(other *Config) (*Config, error) { } // ParseConfig parses notifications configuration from the provided config map and secret. -func ParseConfig(configMap *v1.ConfigMap, secret *v1.Secret, defaultCfg Config) (map[string]triggers.Trigger, map[string]notifiers.Notifier, *Config, error) { +func ParseConfig(configMap *v1.ConfigMap, secret *v1.Secret, defaultCfg Config, argocdService argocd.Service) (map[string]triggers.Trigger, map[string]notifiers.Notifier, *Config, error) { cfg, err := ParseConfigMap(configMap) if err != nil { return nil, nil, nil, err @@ -142,7 +143,7 @@ func ParseConfig(configMap *v1.ConfigMap, secret *v1.Secret, defaultCfg Config) if err != nil { return nil, nil, nil, err } - t, err := triggers.GetTriggers(cfg.Templates, cfg.Triggers) + t, err := triggers.GetTriggers(cfg.Templates, cfg.Triggers, argocdService) if err != nil { return nil, nil, nil, err } diff --git a/testing/testing.go b/testing/testing.go index 58db4d6c..531569b0 100644 --- a/testing/testing.go +++ b/testing/testing.go @@ -84,6 +84,12 @@ func WithHealthStatus(status string) func(app *unstructured.Unstructured) { } } +func WithRepoURL(repo string) func(app *unstructured.Unstructured) { + return func(app *unstructured.Unstructured) { + _ = unstructured.SetNestedField(app.Object, repo, "spec", "source", "repoURL") + } +} + func NewApp(name string, modifiers ...func(app *unstructured.Unstructured)) *unstructured.Unstructured { app := unstructured.Unstructured{} app.SetGroupVersionKind(schema.GroupVersionKind{Group: "argoproj.io", Kind: "application", Version: "v1alpha1"}) diff --git a/triggers/expr/expr.go b/triggers/expr/expr.go index 554a5af4..45e2ad85 100644 --- a/triggers/expr/expr.go +++ b/triggers/expr/expr.go @@ -1,8 +1,10 @@ package expr import ( + "github.com/argoproj-labs/argocd-notifications/shared/argocd" "github.com/argoproj-labs/argocd-notifications/triggers/expr/repo" "github.com/argoproj-labs/argocd-notifications/triggers/expr/time" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) var helpers = map[string]interface{}{} @@ -10,18 +12,18 @@ var helpers = map[string]interface{}{} func init() { helpers = make(map[string]interface{}) register("time", time.NewExprs()) - register("repo", repo.NewExprs()) } func register(namespace string, entry map[string]interface{}) { helpers[namespace] = entry } -func Spawn() map[string]interface{} { +func Spawn(app *unstructured.Unstructured, argocdService argocd.Service) map[string]interface{} { clone := make(map[string]interface{}) for namespace, helper := range helpers { clone[namespace] = helper } + clone["repo"] = repo.NewExprs(argocdService, app) return clone } diff --git a/triggers/expr/expr_test.go b/triggers/expr/expr_test.go index 30d2f272..196e5e97 100644 --- a/triggers/expr/expr_test.go +++ b/triggers/expr/expr_test.go @@ -13,7 +13,7 @@ func TestExpr(t *testing.T) { } for _, ns := range namespaces { - helpers := Spawn() + helpers := Spawn(nil, nil) _, hasNamespace := helpers[ns] assert.True(t, hasNamespace) } diff --git a/triggers/expr/repo/repo.go b/triggers/expr/repo/repo.go index f5cf05f9..7e10f89b 100644 --- a/triggers/expr/repo/repo.go +++ b/triggers/expr/repo/repo.go @@ -1,18 +1,38 @@ package repo import ( + "context" + "errors" "regexp" "strings" - "github.com/argoproj-labs/argocd-notifications/shared/text" - giturls "github.com/whilp/git-urls" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + + "github.com/argoproj-labs/argocd-notifications/shared/argocd" + "github.com/argoproj-labs/argocd-notifications/shared/text" + "github.com/argoproj-labs/argocd-notifications/triggers/expr/shared" ) var ( gitSuffix = regexp.MustCompile(`\.git$`) ) +func getCommitMetadata(commitSHA string, app *unstructured.Unstructured, argocdService argocd.Service) (*shared.CommitMetadata, error) { + repoURL, ok, err := unstructured.NestedString(app.Object, "spec", "source", "repoURL") + if err != nil { + return nil, err + } + if !ok { + panic(errors.New("failed to get application source repo URL")) + } + meta, err := argocdService.GetCommitMetadata(context.Background(), repoURL, commitSHA) + if err != nil { + return nil, err + } + return meta, nil +} + func fullNameByRepoURL(rawURL string) string { parsed, err := giturls.Parse(rawURL) if err != nil { @@ -37,9 +57,17 @@ func repoURLToHTTPS(rawURL string) string { return parsed.String() } -func NewExprs() map[string]interface{} { +func NewExprs(argocdService argocd.Service, app *unstructured.Unstructured) map[string]interface{} { return map[string]interface{}{ "RepoURLToHTTPS": repoURLToHTTPS, "FullNameByRepoURL": fullNameByRepoURL, + "GetCommitMetadata": func(commitSHA string) interface{} { + meta, err := getCommitMetadata(commitSHA, app, argocdService) + if err != nil { + panic(err) + } + + return *meta + }, } } diff --git a/triggers/expr/repo/repo_test.go b/triggers/expr/repo/repo_test.go index cb820f75..46570d8f 100644 --- a/triggers/expr/repo/repo_test.go +++ b/triggers/expr/repo/repo_test.go @@ -1,9 +1,15 @@ package repo import ( + "context" "testing" + "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" + + "github.com/argoproj-labs/argocd-notifications/shared/argocd/mocks" + . "github.com/argoproj-labs/argocd-notifications/testing" + "github.com/argoproj-labs/argocd-notifications/triggers/expr/shared" ) func TestToHttps(t *testing.T) { @@ -29,3 +35,19 @@ func TestParseFullName(t *testing.T) { assert.Equal(t, actual, expected) } } + +func TestGetCommitMetadata(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + argocdService := mocks.NewMockService(ctrl) + expectedMeta := &shared.CommitMetadata{Message: "hello"} + argocdService.EXPECT().GetCommitMetadata(context.Background(), "http://myrepo-url.git", "abc").Return(expectedMeta, nil) + commitMeta, err := getCommitMetadata("abc", NewApp("guestbook", WithRepoURL("http://myrepo-url.git")), argocdService) + + if !assert.NoError(t, err) { + return + } + assert.Equal(t, expectedMeta, commitMeta) + +} diff --git a/triggers/expr/shared/commit.go b/triggers/expr/shared/commit.go new file mode 100644 index 00000000..c58fa9a8 --- /dev/null +++ b/triggers/expr/shared/commit.go @@ -0,0 +1,16 @@ +package shared + +import ( + "time" +) + +type CommitMetadata struct { + // Commit message + Message string + // Commit author + Author string + // Commit creation date + Date time.Time + // Associated tags + Tags []string +} diff --git a/triggers/triggers.go b/triggers/triggers.go index 055051c1..b747d6f5 100644 --- a/triggers/triggers.go +++ b/triggers/triggers.go @@ -7,12 +7,13 @@ import ( "fmt" texttemplate "text/template" + "github.com/Masterminds/sprig" "github.com/antonmedv/expr" "github.com/antonmedv/expr/vm" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "github.com/Masterminds/sprig" "github.com/argoproj-labs/argocd-notifications/notifiers" + "github.com/argoproj-labs/argocd-notifications/shared/argocd" exprHelpers "github.com/argoproj-labs/argocd-notifications/triggers/expr" ) @@ -50,12 +51,12 @@ type template struct { webhooks map[string]webhookTemplate } -func (tmpl template) formatNotification(app *unstructured.Unstructured, context map[string]string) (*notifiers.Notification, error) { +func (tmpl template) formatNotification(app *unstructured.Unstructured, context map[string]string, argocdService argocd.Service) (*notifiers.Notification, error) { vars := map[string]interface{}{ "app": app.Object, "context": context, } - for k, v := range exprHelpers.Spawn() { + for k, v := range exprHelpers.Spawn(app, argocdService) { vars[k] = v } var title bytes.Buffer @@ -111,20 +112,21 @@ func (tmpl template) formatNotification(app *unstructured.Unstructured, context } type trigger struct { - condition *vm.Program - template template + condition *vm.Program + template template + argocdService argocd.Service } -func GetTriggers(templatesCfg []NotificationTemplate, triggersCfg []NotificationTrigger) (map[string]Trigger, error) { +func GetTriggers(templatesCfg []NotificationTemplate, triggersCfg []NotificationTrigger, argocdService argocd.Service) (map[string]Trigger, error) { templates, err := parseTemplates(templatesCfg) if err != nil { return nil, err } - return parseTriggers(triggersCfg, templates) + return parseTriggers(triggersCfg, templates, argocdService) } -func spawnExprEnvs(opts map[string]interface{}) interface{} { - envs := exprHelpers.Spawn() +func spawnExprEnvs(app *unstructured.Unstructured, opts map[string]interface{}, argocdService argocd.Service) interface{} { + envs := exprHelpers.Spawn(app, argocdService) for name, env := range opts { envs[name] = env } @@ -134,7 +136,7 @@ func spawnExprEnvs(opts map[string]interface{}) interface{} { func (t *trigger) Triggered(app *unstructured.Unstructured) (bool, error) { envs := map[string]interface{}{"app": app.Object} - if res, err := expr.Run(t.condition, spawnExprEnvs(envs)); err != nil { + if res, err := expr.Run(t.condition, spawnExprEnvs(app, envs, t.argocdService)); err != nil { return false, err } else if boolRes, ok := res.(bool); ok { return boolRes, nil @@ -147,7 +149,7 @@ func (t *trigger) GetTemplateName() string { } func (t *trigger) FormatNotification(app *unstructured.Unstructured, context map[string]string) (*notifiers.Notification, error) { - return t.template.formatNotification(app, context) + return t.template.formatNotification(app, context, t.argocdService) } func parseTemplates(templates []NotificationTemplate) (map[string]template, error) { @@ -205,7 +207,7 @@ func parseTemplate(nt NotificationTemplate, f texttemplate.FuncMap) (*template, return &t, nil } -func parseTriggers(triggers []NotificationTrigger, templates map[string]template) (map[string]Trigger, error) { +func parseTriggers(triggers []NotificationTrigger, templates map[string]template, argocdService argocd.Service) (map[string]Trigger, error) { res := make(map[string]Trigger) for _, t := range triggers { if t.Enabled != nil && !*t.Enabled { @@ -222,7 +224,7 @@ func parseTriggers(triggers []NotificationTrigger, templates map[string]template if !ok { return nil, fmt.Errorf("trigger %s references unknown template %s", t.Name, t.Template) } - res[t.Name] = &trigger{condition: condition, template: template} + res[t.Name] = &trigger{condition: condition, template: template, argocdService: argocdService} } return res, nil } diff --git a/triggers/triggers_test.go b/triggers/triggers_test.go index b42a43bc..23683170 100644 --- a/triggers/triggers_test.go +++ b/triggers/triggers_test.go @@ -16,7 +16,7 @@ func TestGetTriggers_FailsIfReferencesNonExistingTemplate(t *testing.T) { Name: "test", Template: "bad", Condition: "true", - }}) + }}, nil) assert.EqualError(t, err, "trigger test references unknown template bad") } @@ -31,7 +31,7 @@ func TestGetTriggers(t *testing.T) { Name: "trigger", Template: "template", Condition: "app.metadata.name == 'foo'", - }}) + }}, nil) assert.NoError(t, err) trigger, ok := triggers["trigger"] @@ -62,7 +62,7 @@ func TestGetTriggers_UsingContext(t *testing.T) { Name: "trigger", Template: "template", Condition: "app.metadata.name == 'foo'", - }}) + }}, nil) assert.NoError(t, err) trigger, ok := triggers["trigger"] @@ -97,7 +97,7 @@ func TestGetTriggers_UsingSlack(t *testing.T) { Name: "trigger", Template: "template", Condition: "app.metadata.name == 'foo'", - }}) + }}, nil) assert.NoError(t, err) trigger, ok := triggers["trigger"] @@ -121,7 +121,7 @@ func TestGetTriggers_UsingSlack(t *testing.T) { func TestSpawnExprEnvs(t *testing.T) { opts := map[string]interface{}{"app": "dummy"} - envs, ok := spawnExprEnvs(opts).(map[string]interface{}) + envs, ok := spawnExprEnvs(testingutil.NewApp("test"), opts, nil).(map[string]interface{}) assert.True(t, ok) _, hasApp := envs["app"] @@ -139,7 +139,7 @@ func TestGetTriggers_UsingExprVm(t *testing.T) { Name: "trigger", Template: "template", Condition: "app.metadata.name == 'foo' && app.status.operationState.phase in ['Running'] && time.Now().Sub(time.Parse(app.status.operationState.startedAt)).Minutes() >= 5", - }}) + }}, nil) assert.NoError(t, err) trigger, ok := triggers["trigger"] @@ -179,7 +179,7 @@ func TestTrigger_FormatWebhookNotification(t *testing.T) { return } - nt, err := testTemplate.formatNotification(testingutil.NewApp("world"), map[string]string{}) + nt, err := testTemplate.formatNotification(testingutil.NewApp("world"), map[string]string{}, nil) if !assert.NoError(t, err) { return }