diff --git a/cmd/dinghy.go b/cmd/dinghy.go index 2b9ecc2b..22d7fe23 100644 --- a/cmd/dinghy.go +++ b/cmd/dinghy.go @@ -85,6 +85,7 @@ func Setup() (*logr.Logger, *web.WebAPI) { } client := setupPlankClient(config, log) + clientReadOnly := util.PlankReadOnly{Plank: client} // Create the EventClient ctx, cancel := context.WithCancel(context.Background()) @@ -105,7 +106,11 @@ func Setup() (*logr.Logger, *web.WebAPI) { if _, err := redisClient.Client.Ping().Result(); err != nil { log.Fatalf("Redis Server at %s could not be contacted: %v", config.Redis.BaseURL, err) } - api := web.NewWebAPI(config, redisClient, client, ec, log) + redisClientReadOnly := cache.RedisCacheReadOnly{ + Client: redisClient.Client, + Logger: redisClient.Logger, + } + api := web.NewWebAPI(config, redisClient, client, ec, log, &redisClientReadOnly, clientReadOnly) api.AddDinghyfileUnmarshaller(&dinghyfile.DinghyJsonUnmarshaller{}) if config.ParserFormat == "json" { api.SetDinghyfileParser(dinghyfile.NewDinghyfileParser(&dinghyfile.PipelineBuilder{})) diff --git a/pkg/cache/redis.go b/pkg/cache/redis.go index 0f5f7525..f9f7d369 100644 --- a/pkg/cache/redis.go +++ b/pkg/cache/redis.go @@ -140,6 +140,10 @@ func (c *RedisCache) SetDeps(parent string, deps []string) { // GetRoots grabs roots func (c *RedisCache) GetRoots(url string) []string { + return returnRoots(c.Client, url) +} + +func returnRoots (c *redis.Client, url string) []string { roots := make([]string, 0) visited := map[string]bool{} loge := log.WithFields(log.Fields{"func": "GetRoots"}) @@ -151,7 +155,7 @@ func (c *RedisCache) GetRoots(url string) []string { visited[curr] = true key := compileKey("parents", curr) - parents, err := c.Client.SMembers(key).Result() + parents, err := c.SMembers(key).Result() if err != nil { loge.WithFields(log.Fields{"operation": "parents", "key": key}).Error(err) break @@ -186,10 +190,14 @@ func (c *RedisCache) SetRawData(url string, rawData string) error{ } func (c *RedisCache) GetRawData(url string) (string, error) { + return returnRawData(c.Client, url) +} + +func returnRawData(c *redis.Client, url string) (string, error) { loge := log.WithFields(log.Fields{"func": "GetRawData"}) key := compileKey("rawdata", url) - stringCmd := c.Client.Get(key) + stringCmd := c.Get(key) if stringCmd.Err() != nil { loge.WithFields(log.Fields{"operation": "get value", "key": key}).Error(stringCmd.Err()) return "", stringCmd.Err() diff --git a/pkg/cache/redis_readonly.go b/pkg/cache/redis_readonly.go new file mode 100644 index 00000000..5e3d238c --- /dev/null +++ b/pkg/cache/redis_readonly.go @@ -0,0 +1,56 @@ +/* +* Copyright 2020 Armory, Inc. + +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at + +* http://www.apache.org/licenses/LICENSE-2.0 + +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. + */ + +package cache + +import ( + "context" + "github.com/go-redis/redis" + log "github.com/sirupsen/logrus" + "os" +) + +// RedisCacheReadOnly maintains a dependency graph inside Redis +type RedisCacheReadOnly struct { + Client *redis.Client + Logger *log.Entry + ctx context.Context + stop chan os.Signal +} + + +// SetDeps sets dependencies for a parent +func (c *RedisCacheReadOnly) SetDeps(parent string, deps []string) { + +} + +// GetRoots grabs roots +func (c *RedisCacheReadOnly) GetRoots(url string) []string { + return returnRoots(c.Client, url) +} + +// Set RawData +func (c *RedisCacheReadOnly) SetRawData(url string, rawData string) error{ + return nil +} + +func (c *RedisCacheReadOnly) GetRawData(url string) (string, error) { + return returnRawData(c.Client, url) +} + +// Clear clears everything +func (c *RedisCacheReadOnly) Clear() { +} diff --git a/pkg/dinghyfile/builder.go b/pkg/dinghyfile/builder.go index 36666a68..4ffc5981 100644 --- a/pkg/dinghyfile/builder.go +++ b/pkg/dinghyfile/builder.go @@ -21,6 +21,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/armory/dinghy/pkg/dinghyfile/pipebuilder" "path/filepath" "regexp" "time" @@ -58,6 +59,7 @@ type PipelineBuilder struct { GlobalVariablesMap map[string]interface{} RepositoryRawdataProcessing bool RebuildingModules bool + Action pipebuilder.BuilderAction } // DependencyManager is an interface for assigning dependencies and looking up root nodes @@ -264,10 +266,14 @@ func (b *PipelineBuilder) ProcessDinghyfile(org, repo, path, branch string) erro } b.Logger.Info("Validations for app notifications were successful") - if err := b.updatePipelines(&d.ApplicationSpec, d.Pipelines, d.DeleteStalePipelines, b.AutolockPipelines); err != nil { - b.Logger.Errorf("Failed to update Pipelines for %s: %s", path, err.Error()) - b.NotifyFailure(org, repo, path, err, buf.String() ) - return err + if b.Action == pipebuilder.Validate { + b.Logger.Info("Validation finished successfully") + } else { + if err := b.updatePipelines(&d.ApplicationSpec, d.Pipelines, d.DeleteStalePipelines, b.AutolockPipelines); err != nil { + b.Logger.Errorf("Failed to update Pipelines for %s: %s", path, err.Error()) + b.NotifyFailure(org, repo, path, err, buf.String() ) + return err + } } b.NotifySuccess(org, repo, path, d.ApplicationSpec.Notifications) @@ -338,11 +344,17 @@ func (b *PipelineBuilder) RebuildModuleRoots(org, repo, path, branch string) err } if errEncountered { - b.Logger.Error("The following dinghyfiles weren't updated successfully:") + var word string + if b.Action == pipebuilder.Validate { + word = "validated" + } else { + word = "updated" + } + b.Logger.Errorf("The following dinghyfiles weren't %v successfully:", word) for _, url := range failedUpdates { b.Logger.Error(url) } - return errors.New("Not all upstream dinghyfiles were updated successfully") + return fmt.Errorf("Not all upstream dinghyfiles were %v successfully", word) } return nil } diff --git a/pkg/dinghyfile/builder_test.go b/pkg/dinghyfile/builder_test.go index 4d5a7d57..6fc1a9eb 100644 --- a/pkg/dinghyfile/builder_test.go +++ b/pkg/dinghyfile/builder_test.go @@ -19,6 +19,7 @@ package dinghyfile import ( "bytes" "errors" + "github.com/armory/dinghy/pkg/dinghyfile/pipebuilder" "reflect" "testing" @@ -68,6 +69,39 @@ func TestProcessDinghyfile(t *testing.T) { assert.Nil(t, pb.ProcessDinghyfile("myorg", "myrepo", "the/full/path", "mybranch")) } + + +func TestProcessDinghyfileValidate(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + rendered := `{"application":"biff"}` + + renderer := NewMockParser(ctrl) + renderer.EXPECT().Parse(gomock.Eq("myorg"), gomock.Eq("myrepo"), gomock.Eq("the/full/path"), gomock.Eq("mybranch"), gomock.Any()).Return(bytes.NewBuffer([]byte(rendered)), nil).Times(1) + + client := NewMockPlankClient(ctrl) + + logger := mock.NewMockFieldLogger(ctrl) + logger.EXPECT().Infof(gomock.Eq("Unmarshalled: %v"), gomock.Any()).Times(1) + logger.EXPECT().Infof(gomock.Eq("Dinghyfile struct: %v"), gomock.Any()).Times(1) + logger.EXPECT().Infof(gomock.Eq("Updated: %s"), gomock.Any()).Times(1) + logger.EXPECT().Infof(gomock.Eq("Compiled: %s"), gomock.Any()).Times(1) + logger.EXPECT().Info(gomock.Eq("Validations for stage refs were successful")).Times(1) + logger.EXPECT().Info(gomock.Eq("Validations for app notifications were successful")).Times(1) + logger.EXPECT().Info(gomock.Eq("Validation finished successfully")).Times(1) + + // Because we've set the renderer, we should NOT get this message... + logger.EXPECT().Info(gomock.Eq("Calling DetermineRenderer")).Times(0) + + pb := testPipelineBuilder() + pb.Action = pipebuilder.Validate + pb.Parser = renderer + pb.Client = client + pb.Logger = logger + assert.Nil(t, pb.ProcessDinghyfile("myorg", "myrepo", "the/full/path", "mybranch")) +} + // Test updateapp func TestUpdateApplication(t *testing.T) { ctrl := gomock.NewController(t) diff --git a/pkg/dinghyfile/pipebuilder/action.go b/pkg/dinghyfile/pipebuilder/action.go new file mode 100644 index 00000000..f39aef7a --- /dev/null +++ b/pkg/dinghyfile/pipebuilder/action.go @@ -0,0 +1,26 @@ +/* +* Copyright 2020 Armory, Inc. + +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at + +* http://www.apache.org/licenses/LICENSE-2.0 + +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. + */ + +package pipebuilder + +// BuilderAction wires to the action the builder will do +type BuilderAction string + +// BuilderAction types +const ( + Validate BuilderAction = "validate" + Process BuilderAction = "process" +) diff --git a/pkg/dinghyfile/test_common.go b/pkg/dinghyfile/test_common.go index 390f70a7..a49877b4 100644 --- a/pkg/dinghyfile/test_common.go +++ b/pkg/dinghyfile/test_common.go @@ -18,6 +18,7 @@ package dinghyfile import ( "fmt" + "github.com/armory/dinghy/pkg/dinghyfile/pipebuilder" "strings" "github.com/armory/dinghy/pkg/cache" @@ -52,6 +53,7 @@ func testBasePipelineBuilder() *PipelineBuilder { Logger: logrus.New(), Ums: []Unmarshaller{&DinghyJsonUnmarshaller{}}, TemplateOrg: "armory", + Action: pipebuilder.Process, } } diff --git a/pkg/git/status.go b/pkg/git/status.go index 3451c5c1..6803e823 100644 --- a/pkg/git/status.go +++ b/pkg/git/status.go @@ -16,6 +16,8 @@ package git +import pipebuilder "github.com/armory/dinghy/pkg/dinghyfile/pipebuilder" + // Status wires up to the green check or red x next to a GitHub commit. type Status string @@ -27,4 +29,17 @@ const ( StatusFailure = "failure" DefaultPendingMessage string = "Updating pipeline definitions..." DefaultSuccessMessage string = "Pipeline definitions updated!" + DefaultValidatePendingMessage string = "Validating pipeline definitions..." + DefaultValidateSuccessMessage string = "Pipeline definitions validation was successful!" ) + +var DefaultMessagesByBuilderAction = map[pipebuilder.BuilderAction]map[Status]string { + pipebuilder.Process: { + StatusPending: DefaultPendingMessage, + StatusSuccess: DefaultSuccessMessage, + }, + pipebuilder.Validate: { + StatusPending: DefaultValidatePendingMessage, + StatusSuccess: DefaultValidateSuccessMessage, + }, +} \ No newline at end of file diff --git a/pkg/util/plank_readonly.go b/pkg/util/plank_readonly.go new file mode 100644 index 00000000..b1916af1 --- /dev/null +++ b/pkg/util/plank_readonly.go @@ -0,0 +1,66 @@ +/* +* Copyright 2020 Armory, Inc. + +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at + +* http://www.apache.org/licenses/LICENSE-2.0 + +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. + */ + +package util + +import "github.com/armory/plank/v3" + +type PlankReadOnly struct { + Plank *plank.Client +} + +func (p PlankReadOnly) GetApplication(string string) (*plank.Application, error){ + return p.Plank.GetApplication(string) +} + +func (p PlankReadOnly) UpdateApplicationNotifications(plank.NotificationsType, string) error{ + return nil +} + +func (p PlankReadOnly) GetApplicationNotifications(app string) (*plank.NotificationsType, error){ + return p.Plank.GetApplicationNotifications(app) +} + +func (p PlankReadOnly) CreateApplication(*plank.Application) error{ + return nil +} + +func (p PlankReadOnly) UpdateApplication(plank.Application) error{ + return nil +} + +func (p PlankReadOnly) GetPipelines(string string) ([]plank.Pipeline, error){ + return p.Plank.GetPipelines(string) +} + +func (p PlankReadOnly) DeletePipeline(plank.Pipeline) error{ + return nil +} + +func (p PlankReadOnly) UpsertPipeline(pipe plank.Pipeline, str string) error{ + return nil +} + +func (p PlankReadOnly) ResyncFiat() error{ + return nil +} + +func (p PlankReadOnly) ArmoryEndpointsEnabled() bool{ + return false +} + +func (p PlankReadOnly) EnableArmoryEndpoints(){ +} diff --git a/pkg/web/routes.go b/pkg/web/routes.go index e4cdb071..a22130fd 100644 --- a/pkg/web/routes.go +++ b/pkg/web/routes.go @@ -21,6 +21,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/armory/dinghy/pkg/dinghyfile/pipebuilder" "io/ioutil" "net/http" "strings" @@ -58,7 +59,9 @@ type Push interface { type WebAPI struct { Config *settings.Settings Client util.PlankClient + ClientReadOnly util.PlankClient Cache dinghyfile.DependencyManager + CacheReadOnly dinghyfile.DependencyManager EventClient *events.Client Logger log.FieldLogger Ums []dinghyfile.Unmarshaller @@ -66,7 +69,7 @@ type WebAPI struct { Parser dinghyfile.Parser } -func NewWebAPI(s *settings.Settings, r dinghyfile.DependencyManager, c util.PlankClient, e *events.Client, l log.FieldLogger) *WebAPI { +func NewWebAPI(s *settings.Settings, r dinghyfile.DependencyManager, c util.PlankClient, e *events.Client, l log.FieldLogger, depreadonly dinghyfile.DependencyManager, clientreadonly util.PlankClient) *WebAPI { return &WebAPI{ Config: s, Client: c, @@ -75,6 +78,8 @@ func NewWebAPI(s *settings.Settings, r dinghyfile.DependencyManager, c util.Plan Logger: l, Ums: []dinghyfile.Unmarshaller{}, Notifiers: []notifiers.Notifier{}, + ClientReadOnly: clientreadonly, + CacheReadOnly: depreadonly, } } @@ -131,6 +136,7 @@ func (wa *WebAPI) manualUpdateHandler(w http.ResponseWriter, r *http.Request) { AutolockPipelines: wa.Config.AutoLockPipelines, Logger: wa.Logger, Ums: wa.Ums, + Action: pipebuilder.Process, } builder.Parser = wa.Parser @@ -471,7 +477,7 @@ func (wa *WebAPI) ProcessPush(p Push, b *dinghyfile.PipelineBuilder) error { wa.Logger.Info("Dinghyfile found in commit for repo " + p.Repo()) // Set commit status to the pending yellow dot. - p.SetCommitStatus(git.StatusPending, git.DefaultPendingMessage) + p.SetCommitStatus(git.StatusPending, git.DefaultMessagesByBuilderAction[b.Action][git.StatusPending]) for _, filePath := range p.Files() { components := strings.Split(filePath, "/") @@ -489,7 +495,7 @@ func (wa *WebAPI) ProcessPush(p Push, b *dinghyfile.PipelineBuilder) error { } return err } - p.SetCommitStatus(git.StatusSuccess, git.DefaultSuccessMessage) + p.SetCommitStatus(git.StatusSuccess, git.DefaultMessagesByBuilderAction[b.Action][git.StatusSuccess]) } } return nil @@ -500,17 +506,18 @@ func (wa *WebAPI) ProcessPush(p Push, b *dinghyfile.PipelineBuilder) error { func (wa *WebAPI) buildPipelines(p Push, rawPush []byte, f dinghyfile.Downloader, w http.ResponseWriter) { // see if we have any configurations for this repo. // if we do have configurations, see if this is the branch we want to use. If it's not, skip and return. + var validation bool if rc := wa.Config.GetRepoConfig(p.Name(), p.Repo()); rc != nil { if !p.IsBranch(rc.Branch) { - wa.Logger.Infof("Received request from branch %s. Does not match configured branch %s. Skipping.", p.Branch(), rc.Branch) - return + wa.Logger.Infof("Received request from branch %s. Does not match configured branch %s. Proceeding as validation.", p.Branch(), rc.Branch) + validation = true } } else { // if we didn't find any configurations for this repo, proceed with master wa.Logger.Infof("Found no custom configuration for repo: %s, proceeding with master", p.Repo()) if !p.IsMaster() { - wa.Logger.Infof("Skipping Spinnaker pipeline update because this branch (%s) is not master", p.Branch()) - return + wa.Logger.Infof("Skipping Spinnaker pipeline update because this branch (%s) is not master. Proceeding as validation.", p.Branch()) + validation = true } } @@ -538,6 +545,14 @@ func (wa *WebAPI) buildPipelines(p Push, rawPush []byte, f dinghyfile.Downloader Notifiers: wa.Notifiers, PushRaw: rawPushData, RepositoryRawdataProcessing: wa.Config.RepositoryRawdataProcessing, + Action: pipebuilder.Process, + } + + if validation { + builder.Notifiers = nil + builder.Client = wa.ClientReadOnly + builder.Depman = wa.CacheReadOnly + builder.Action = pipebuilder.Validate } builder.Parser = wa.Parser @@ -559,7 +574,7 @@ func (wa *WebAPI) buildPipelines(p Push, rawPush []byte, f dinghyfile.Downloader // Check if we're in a template repo if p.Repo() == wa.Config.TemplateRepo { // Set status to pending while we process modules - p.SetCommitStatus(git.StatusPending, git.DefaultPendingMessage) + p.SetCommitStatus(git.StatusPending, git.DefaultMessagesByBuilderAction[builder.Action][git.StatusPending]) // For each module pushed, rebuild dependent dinghyfiles for _, file := range p.Files() { @@ -575,7 +590,7 @@ func (wa *WebAPI) buildPipelines(p Push, rawPush []byte, f dinghyfile.Downloader return } } - p.SetCommitStatus(git.StatusSuccess, git.DefaultSuccessMessage) + p.SetCommitStatus(git.StatusSuccess, git.DefaultMessagesByBuilderAction[builder.Action][git.StatusSuccess]) } w.Write([]byte(`{"status":"accepted"}`)) diff --git a/pkg/web/routes_test.go b/pkg/web/routes_test.go index a7c20b26..71c9ae10 100644 --- a/pkg/web/routes_test.go +++ b/pkg/web/routes_test.go @@ -46,7 +46,7 @@ func testHealthCheckLogging(t *testing.T, endpoint string) { logger.EXPECT().Debug(gomock.Any()).Times(1) logger.EXPECT().Info(gomock.Any()).Times(0) - wa := NewWebAPI(nil, nil, nil, nil, logger) + wa := NewWebAPI(nil, nil, nil, nil, logger, nil, nil) req, err := http.NewRequest("GET", endpoint, nil) if err != nil { t.Fatal(err) @@ -75,7 +75,7 @@ func TestGithubWebhookHandlerBadJSON(t *testing.T) { logger.EXPECT().Infof(gomock.Eq("Received payload: %s"), gomock.Any()).Times(1) logger.EXPECT().Errorf(gomock.Eq("failed to decode github webhook: %s"), gomock.Any()).Times(1) - wa := NewWebAPI(nil, nil, nil, nil, logger) + wa := NewWebAPI(nil, nil, nil, nil, logger, nil, nil) payload := bytes.NewBufferString(`{broken`) req := httptest.NewRequest("POST", "/v1/webhooks/github", payload) @@ -91,7 +91,7 @@ func TestGithubWebhookHandlerNoRef(t *testing.T) { logger.EXPECT().Infof(gomock.Eq("Received payload: %s"), gomock.Any()).Times(1) logger.EXPECT().Info(gomock.Eq("Possibly a non-Push notification received (blank ref)")).Times(1) - wa := NewWebAPI(nil, nil, nil, nil, logger) + wa := NewWebAPI(nil, nil, nil, nil, logger, nil, nil) payload := bytes.NewBufferString(`{}`) req := httptest.NewRequest("POST", "/v1/webhooks/github", payload) @@ -110,7 +110,7 @@ func TestStashWebhookHandlerBadJSON(t *testing.T) { logger.EXPECT().Infof(gomock.Eq("Received payload: %s"), gomock.Any()).Times(1) logger.EXPECT().Errorf(gomock.Eq("failed to decode stash webhook: %s"), gomock.Any()).Times(1) - wa := NewWebAPI(nil, nil, nil, nil, logger) + wa := NewWebAPI(nil, nil, nil, nil, logger, nil, nil) payload := bytes.NewBufferString(`{broken`) req := httptest.NewRequest("POST", "/v1/webhooks/stash", payload) @@ -128,7 +128,7 @@ func TestStashWebhookBadPayload(t *testing.T) { logger.EXPECT().Infof(gomock.Eq("Received payload: %s"), gomock.Any()).Times(1) logger.EXPECT().Errorf(gomock.Eq("failed to decode stash webhook: %s"), gomock.Any()).Times(1) - wa := NewWebAPI(nil, nil, nil, nil, logger) + wa := NewWebAPI(nil, nil, nil, nil, logger, nil, nil) payload := bytes.NewBufferString(`{"event_type": "stash", "refChanges": "not an array"}`) @@ -146,7 +146,7 @@ func TestBitbucketWebhookHandlerBadJSON(t *testing.T) { logger := mock.NewMockFieldLogger(ctrl) logger.EXPECT().Errorf(gomock.Eq("Unable to determine bitbucket event type: %s"), gomock.Any()).Times(1) - wa := NewWebAPI(nil, nil, nil, nil, logger) + wa := NewWebAPI(nil, nil, nil, nil, logger, nil, nil) payload := bytes.NewBufferString(`{broken`) req := httptest.NewRequest("POST", "/v1/webhooks/bitbucket", payload) @@ -164,7 +164,7 @@ func TestBitbucketWebhookBadPayload(t *testing.T) { logger.EXPECT().Infof(gomock.Eq("Received payload: %s"), gomock.Any()).Times(1) logger.EXPECT().Errorf(gomock.Eq("failed to decode bitbucket-server webhook: %s"), gomock.Any()).Times(1) - wa := NewWebAPI(nil, nil, nil, nil, logger) + wa := NewWebAPI(nil, nil, nil, nil, logger, nil, nil) payload := bytes.NewBufferString(`{"event_type": "repo:refs_changed", "changes": "not an array"}`) @@ -182,7 +182,7 @@ func TestBitbucketCloudWebhookHandlerBadJSON(t *testing.T) { logger := mock.NewMockFieldLogger(ctrl) logger.EXPECT().Errorf(gomock.Eq("Unable to determine bitbucket event type: %s"), gomock.Any()).Times(1) - wa := NewWebAPI(nil, nil, nil, nil, logger) + wa := NewWebAPI(nil, nil, nil, nil, logger, nil, nil) payload := bytes.NewBufferString(`{broken`) req := httptest.NewRequest("POST", "/v1/webhooks/bitbucket-cloud", payload) @@ -200,7 +200,7 @@ func TestBitbucketCloudWebhookBadPayload(t *testing.T) { logger.EXPECT().Infof(gomock.Eq("Received payload: %s"), gomock.Any()).Times(1) logger.EXPECT().Errorf(gomock.Eq("failed to decode bitbucket-cloud webhook: %s"), gomock.Any()).Times(1) - wa := NewWebAPI(nil, nil, nil, nil, logger) + wa := NewWebAPI(nil, nil, nil, nil, logger, nil, nil) payload := bytes.NewBufferString(`{"event_type": "repo:push", "push": {"changes": "not an array"}}`) @@ -216,7 +216,7 @@ func TestUnknownEventType(t *testing.T) { logger := mock.NewMockFieldLogger(ctrl) - wa := NewWebAPI(nil, nil, nil, nil, logger) + wa := NewWebAPI(nil, nil, nil, nil, logger, nil, nil) payload := bytes.NewBufferString(`{"event_type": "", "changes": "not an array"}`)