diff --git a/api/v1alpha1/conditions.go b/api/v1alpha1/conditions.go index 54e3e1c..da5c36b 100644 --- a/api/v1alpha1/conditions.go +++ b/api/v1alpha1/conditions.go @@ -10,6 +10,8 @@ const ( TargetClusterNotReadyReason string = "TargetClusterNotReady" // ReconciliationSucceededReason signals that a Pipeline has been successfully reconciled. ReconciliationSucceededReason string = "ReconciliationSucceeded" + // EnvironmentNotReadyReason signals the environment is not ready. + EnvironmentNotReadyReason string = "EnvironmentNotReady" ) // Reasons used by the level-triggered controller. diff --git a/controllers/leveltriggered/controller.go b/controllers/leveltriggered/controller.go index f0c49af..499bcdc 100644 --- a/controllers/leveltriggered/controller.go +++ b/controllers/leveltriggered/controller.go @@ -21,6 +21,7 @@ import ( "github.com/weaveworks/pipeline-controller/api/v1alpha1" "github.com/weaveworks/pipeline-controller/pkg/conditions" + "github.com/weaveworks/pipeline-controller/server/strategy" ) // PipelineReconciler reconciles a Pipeline object @@ -30,9 +31,10 @@ type PipelineReconciler struct { targetScheme *runtime.Scheme ControllerName string recorder record.EventRecorder + stratReg strategy.StrategyRegistry } -func NewPipelineReconciler(c client.Client, s *runtime.Scheme, controllerName string) *PipelineReconciler { +func NewPipelineReconciler(c client.Client, s *runtime.Scheme, controllerName string, stratReg strategy.StrategyRegistry) *PipelineReconciler { targetScheme := runtime.NewScheme() return &PipelineReconciler{ @@ -40,6 +42,7 @@ func NewPipelineReconciler(c client.Client, s *runtime.Scheme, controllerName st Scheme: s, targetScheme: targetScheme, ControllerName: controllerName, + stratReg: stratReg, } } @@ -182,9 +185,152 @@ func (r *PipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c pipeline.GetNamespace(), pipeline.GetName(), ) + firstEnv := pipeline.Spec.Environments[0] + + latestRevision := checkAllTargetsHaveSameRevision(pipeline.Status.Environments[firstEnv.Name]) + if latestRevision == "" { + // not all targets have the same revision, or have no revision set, so we can't proceed + if err := r.setPendingCondition(ctx, pipeline, v1alpha1.EnvironmentNotReadyReason, "Waiting for all targets to have the same revision"); err != nil { + return ctrl.Result{Requeue: true}, fmt.Errorf("error setting pending condition: %w", err) + } + + return ctrl.Result{}, nil + } + + if !checkAllTargetsAreReady(pipeline.Status.Environments[firstEnv.Name]) { + // not all targets are ready, so we can't proceed + if err := r.setPendingCondition(ctx, pipeline, v1alpha1.EnvironmentNotReadyReason, "Waiting for all targets to be ready"); err != nil { + return ctrl.Result{}, fmt.Errorf("error setting pending condition: %w", err) + } + + return ctrl.Result{}, nil + } + + if err := r.removePendingCondition(ctx, pipeline); err != nil { + return ctrl.Result{}, fmt.Errorf("error removing pending condition: %w", err) + } + + for _, env := range pipeline.Spec.Environments[1:] { + // if all targets run the latest revision and are ready, we can skip this environment + if checkAllTargetsRunRevision(pipeline.Status.Environments[env.Name], latestRevision) && checkAllTargetsAreReady(pipeline.Status.Environments[env.Name]) { + continue + } + + if checkAnyTargetHasRevision(pipeline.Status.Environments[env.Name], latestRevision) { + return ctrl.Result{}, nil + } + + err := r.promoteLatestRevision(ctx, pipeline, env, latestRevision) + if err != nil { + return ctrl.Result{}, fmt.Errorf("error promoting new version: %w", err) + } + + break + } + return ctrl.Result{}, nil } +func (r *PipelineReconciler) setPendingCondition(ctx context.Context, pipeline v1alpha1.Pipeline, reason, message string) error { + condition := metav1.Condition{ + Type: conditions.PromotionPendingCondition, + Status: metav1.ConditionTrue, + Reason: reason, + Message: message, + } + + apimeta.SetStatusCondition(&pipeline.Status.Conditions, condition) + + if err := r.patchStatus(ctx, client.ObjectKeyFromObject(&pipeline), pipeline.Status); err != nil { + return err + } + + return nil +} + +func (r *PipelineReconciler) removePendingCondition(ctx context.Context, pipeline v1alpha1.Pipeline) error { + apimeta.RemoveStatusCondition(&pipeline.Status.Conditions, conditions.PromotionPendingCondition) + + if err := r.patchStatus(ctx, client.ObjectKeyFromObject(&pipeline), pipeline.Status); err != nil { + return err + } + + return nil +} + +func (r *PipelineReconciler) promoteLatestRevision(ctx context.Context, pipeline v1alpha1.Pipeline, env v1alpha1.Environment, revision string) error { + // none of the current strategies are idepontent, using it now to keep the ball rolling, but we need to implement + // strategies that are. + + promotion := pipeline.Spec.GetPromotion(env.Name) + if promotion == nil { + return nil + } + + strat, err := r.stratReg.Get(*promotion) + if err != nil { + return fmt.Errorf("error getting strategy from registry: %w", err) + } + + prom := strategy.Promotion{ + PipelineName: pipeline.Name, + PipelineNamespace: pipeline.Namespace, + Environment: env, + Version: revision, + } + + _, err = strat.Promote(ctx, *pipeline.Spec.Promotion, prom) + + return err +} + +func checkAnyTargetHasRevision(env *v1alpha1.EnvironmentStatus, revision string) bool { + for _, target := range env.Targets { + if target.Revision == revision { + return true + } + } + + return false +} + +func checkAllTargetsRunRevision(env *v1alpha1.EnvironmentStatus, revision string) bool { + for _, target := range env.Targets { + if target.Revision != revision { + return false + } + } + + return true +} + +// checkAllTargetsHaveSameRevision returns a revision if all targets in the environment have the same, +// non-empty revision, and an empty string otherwise. +func checkAllTargetsHaveSameRevision(env *v1alpha1.EnvironmentStatus) string { + if len(env.Targets) == 0 { + return "" + } + + revision := env.Targets[0].Revision + for _, target := range env.Targets { + if target.Revision != revision { + return "" + } + } + + return revision +} + +func checkAllTargetsAreReady(env *v1alpha1.EnvironmentStatus) bool { + for _, target := range env.Targets { + if !target.Ready { + return false + } + } + + return true +} + // getClusterClient retrieves or creates a client for the cluster in question. A `nil` value for the argument indicates the local cluster. func (r *PipelineReconciler) getClusterClient(cluster *clusterctrlv1alpha1.GitopsCluster) (client.Client, error) { if cluster == nil { diff --git a/controllers/leveltriggered/controller_test.go b/controllers/leveltriggered/controller_test.go index 72b9393..c5a4816 100644 --- a/controllers/leveltriggered/controller_test.go +++ b/controllers/leveltriggered/controller_test.go @@ -11,6 +11,8 @@ import ( "github.com/fluxcd/pkg/runtime/conditions" . "github.com/onsi/gomega" clusterctrlv1alpha1 "github.com/weaveworks/cluster-controller/api/v1alpha1" + pipelineconditions "github.com/weaveworks/pipeline-controller/pkg/conditions" + "go.uber.org/mock/gomock" apimeta "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -19,6 +21,7 @@ import ( "github.com/weaveworks/pipeline-controller/api/v1alpha1" "github.com/weaveworks/pipeline-controller/internal/testingutils" + "github.com/weaveworks/pipeline-controller/server/strategy" ) const ( @@ -42,7 +45,7 @@ func TestReconcile(t *testing.T) { }, }}) - checkReadyCondition(ctx, g, client.ObjectKeyFromObject(pipeline), metav1.ConditionFalse, v1alpha1.TargetNotReadableReason) + checkCondition(ctx, g, client.ObjectKeyFromObject(pipeline), meta.ReadyCondition, metav1.ConditionFalse, v1alpha1.TargetNotReadableReason) g.Eventually(fetchEventsFor(ns.Name, name), time.Second, time.Millisecond*100).Should(Not(BeEmpty())) events := fetchEventsFor(ns.Name, name)() g.Expect(events).ToNot(BeEmpty()) @@ -51,7 +54,7 @@ func TestReconcile(t *testing.T) { // make an unready cluster and see if it notices testingutils.NewGitopsCluster(ctx, g, k8sClient, clusterName, ns.Name, kubeConfig) - checkReadyCondition(ctx, g, client.ObjectKeyFromObject(pipeline), metav1.ConditionFalse, v1alpha1.TargetNotReadableReason) + checkCondition(ctx, g, client.ObjectKeyFromObject(pipeline), meta.ReadyCondition, metav1.ConditionFalse, v1alpha1.TargetNotReadableReason) }) t.Run("sets reconciliation succeeded condition for remote cluster", func(t *testing.T) { @@ -66,7 +69,7 @@ func TestReconcile(t *testing.T) { pipeline := newPipeline(ctx, g, name, ns.Name, []*clusterctrlv1alpha1.GitopsCluster{gc}) - checkReadyCondition(ctx, g, client.ObjectKeyFromObject(pipeline), metav1.ConditionTrue, v1alpha1.ReconciliationSucceededReason) + checkCondition(ctx, g, client.ObjectKeyFromObject(pipeline), meta.ReadyCondition, metav1.ConditionTrue, v1alpha1.ReconciliationSucceededReason) g.Eventually(fetchEventsFor(ns.Name, name), time.Second, time.Millisecond*100).Should(Not(BeEmpty())) @@ -84,7 +87,7 @@ func TestReconcile(t *testing.T) { pipeline := newPipeline(ctx, g, name, ns.Name, nil) - checkReadyCondition(ctx, g, client.ObjectKeyFromObject(pipeline), metav1.ConditionTrue, v1alpha1.ReconciliationSucceededReason) + checkCondition(ctx, g, client.ObjectKeyFromObject(pipeline), meta.ReadyCondition, metav1.ConditionTrue, v1alpha1.ReconciliationSucceededReason) g.Eventually(fetchEventsFor(ns.Name, name), time.Second, time.Millisecond*100).Should(Not(BeEmpty())) @@ -98,10 +101,10 @@ func TestReconcile(t *testing.T) { name := "pipeline-" + rand.String(5) ns := testingutils.NewNamespace(ctx, g, k8sClient) pipeline := newPipeline(ctx, g, name, ns.Name, nil) - checkReadyCondition(ctx, g, client.ObjectKeyFromObject(pipeline), metav1.ConditionFalse, v1alpha1.TargetNotReadableReason) + checkCondition(ctx, g, client.ObjectKeyFromObject(pipeline), meta.ReadyCondition, metav1.ConditionFalse, v1alpha1.TargetNotReadableReason) hr := newApp(ctx, g, name, ns.Name) // the name of the pipeline is also used as the name in the appRef, in newPipeline(...) - checkReadyCondition(ctx, g, client.ObjectKeyFromObject(pipeline), metav1.ConditionTrue, v1alpha1.ReconciliationSucceededReason) + checkCondition(ctx, g, client.ObjectKeyFromObject(pipeline), meta.ReadyCondition, metav1.ConditionTrue, v1alpha1.ReconciliationSucceededReason) // we didn't set the app to be ready, so we should expect the target to be reported as unready p := getPipeline(ctx, g, client.ObjectKeyFromObject(pipeline)) @@ -121,6 +124,215 @@ func TestReconcile(t *testing.T) { }, "5s", "0.2s").Should(BeTrue()) g.Expect(targetStatus.Revision).To(Equal(appRevision)) }) + + t.Run("promotes revision to all environments", func(t *testing.T) { + mockStrategy := setStrategyRegistry(t, pipelineReconciler) + mockStrategy.EXPECT().Handles(gomock.Any()).Return(true).AnyTimes() + + name := "pipeline-" + rand.String(5) + + managementNs := testingutils.NewNamespace(ctx, g, k8sClient) + devNs := testingutils.NewNamespace(ctx, g, k8sClient) + stagingNs := testingutils.NewNamespace(ctx, g, k8sClient) + prodNs := testingutils.NewNamespace(ctx, g, k8sClient) + + pipeline := &v1alpha1.Pipeline{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: managementNs.Name, + }, + Spec: v1alpha1.PipelineSpec{ + AppRef: v1alpha1.LocalAppReference{ + APIVersion: "helm.toolkit.fluxcd.io/v2beta1", + Kind: "HelmRelease", + Name: name, + }, + Environments: []v1alpha1.Environment{ + { + Name: "dev", + Targets: []v1alpha1.Target{ + {Namespace: devNs.Name}, + }, + }, + { + Name: "staging", + Targets: []v1alpha1.Target{ + {Namespace: stagingNs.Name}, + }, + }, + { + Name: "prod", + Targets: []v1alpha1.Target{ + {Namespace: prodNs.Name}, + }, + }, + }, + Promotion: &v1alpha1.Promotion{ + Strategy: v1alpha1.Strategy{ + Notification: &v1alpha1.NotificationPromotion{}, + }, + }, + }, + } + + devApp := newApp(ctx, g, name, devNs.Name) + setAppRevisionAndReadyStatus(ctx, g, devApp, "v1.0.0") + + stagingApp := newApp(ctx, g, name, stagingNs.Name) + setAppRevisionAndReadyStatus(ctx, g, stagingApp, "v1.0.0") + + prodApp := newApp(ctx, g, name, prodNs.Name) + setAppRevisionAndReadyStatus(ctx, g, prodApp, "v1.0.0") + + g.Expect(k8sClient.Create(ctx, pipeline)).To(Succeed()) + checkCondition(ctx, g, client.ObjectKeyFromObject(pipeline), meta.ReadyCondition, metav1.ConditionTrue, v1alpha1.ReconciliationSucceededReason) + + versionToPromote := "v1.0.1" + + mockStrategy.EXPECT(). + Promote(gomock.Any(), *pipeline.Spec.Promotion, gomock.Any()). + AnyTimes(). + Do(func(ctx context.Context, p v1alpha1.Promotion, prom strategy.Promotion) { + switch prom.Environment.Name { + case "staging": + setAppRevision(ctx, g, stagingApp, prom.Version) + case "prod": + setAppRevision(ctx, g, prodApp, prom.Version) + default: + panic("Unexpected environment. Make sure to setup the pipeline properly in the test.") + } + }) + + // Bumping dev revision to trigger the promotion + setAppRevisionAndReadyStatus(ctx, g, devApp, versionToPromote) + + // checks if the revision of all target status is v1.0.1 + g.Eventually(func() bool { + p := getPipeline(ctx, g, client.ObjectKeyFromObject(pipeline)) + + for _, env := range p.Spec.Environments { + if !checkAllTargetsRunRevision(p.Status.Environments[env.Name], versionToPromote) { + return false + } + + if !checkAllTargetsAreReady(p.Status.Environments[env.Name]) { + return false + } + } + + return true + }, "5s", "0.2s").Should(BeTrue()) + + t.Run("triggers another promotion if the app is updated again", func(t *testing.T) { + // Bumping dev revision to trigger the promotion + setAppRevisionAndReadyStatus(ctx, g, devApp, "v1.0.2") + + // checks if the revision of all target status is v1.0.2 + g.Eventually(func() bool { + p := getPipeline(ctx, g, client.ObjectKeyFromObject(pipeline)) + + for _, env := range p.Spec.Environments { + if !checkAllTargetsRunRevision(p.Status.Environments[env.Name], "v1.0.2") { + return false + } + } + + return true + }, "5s", "0.2s").Should(BeTrue()) + }) + }) + + t.Run("sets PipelinePending condition", func(t *testing.T) { + mockStrategy := setStrategyRegistry(t, pipelineReconciler) + mockStrategy.EXPECT().Handles(gomock.Any()).Return(true).AnyTimes() + + name := "pipeline-" + rand.String(5) + + managementNs := testingutils.NewNamespace(ctx, g, k8sClient) + devNs := testingutils.NewNamespace(ctx, g, k8sClient) + devNs2 := testingutils.NewNamespace(ctx, g, k8sClient) + stagingNs := testingutils.NewNamespace(ctx, g, k8sClient) + + pipeline := &v1alpha1.Pipeline{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: managementNs.Name, + }, + Spec: v1alpha1.PipelineSpec{ + AppRef: v1alpha1.LocalAppReference{ + APIVersion: "helm.toolkit.fluxcd.io/v2beta1", + Kind: "HelmRelease", + Name: name, + }, + Environments: []v1alpha1.Environment{ + { + Name: "dev", + Targets: []v1alpha1.Target{ + {Namespace: devNs.Name}, + {Namespace: devNs2.Name}, + }, + }, + { + Name: "staging", + Targets: []v1alpha1.Target{ + {Namespace: stagingNs.Name}, + }, + }, + }, + Promotion: &v1alpha1.Promotion{ + Strategy: v1alpha1.Strategy{ + Notification: &v1alpha1.NotificationPromotion{}, + }, + }, + }, + } + + devApp := newApp(ctx, g, name, devNs.Name) + setAppRevisionAndReadyStatus(ctx, g, devApp, "v1.0.0") + + devApp2 := newApp(ctx, g, name, devNs2.Name) + + stagingApp := newApp(ctx, g, name, stagingNs.Name) + setAppRevisionAndReadyStatus(ctx, g, stagingApp, "v1.0.0") + + g.Expect(k8sClient.Create(ctx, pipeline)).To(Succeed()) + checkCondition(ctx, g, client.ObjectKeyFromObject(pipeline), meta.ReadyCondition, metav1.ConditionTrue, v1alpha1.ReconciliationSucceededReason) + checkCondition(ctx, g, client.ObjectKeyFromObject(pipeline), pipelineconditions.PromotionPendingCondition, metav1.ConditionTrue, v1alpha1.EnvironmentNotReadyReason) + + setAppRevision(ctx, g, devApp2, "v1.0.0") + checkCondition(ctx, g, client.ObjectKeyFromObject(pipeline), pipelineconditions.PromotionPendingCondition, metav1.ConditionTrue, v1alpha1.EnvironmentNotReadyReason) + + setAppStatusReadyCondition(ctx, g, devApp2) + + g.Eventually(func() bool { + p := getPipeline(ctx, g, client.ObjectKeyFromObject(pipeline)) + return apimeta.FindStatusCondition(p.Status.Conditions, pipelineconditions.PromotionPendingCondition) == nil + }).Should(BeTrue()) + }) +} + +func setAppRevisionAndReadyStatus(ctx context.Context, g Gomega, hr *helmv2.HelmRelease, revision string) { + setAppRevision(ctx, g, hr, revision) + setAppStatusReadyCondition(ctx, g, hr) +} + +func setAppRevision(ctx context.Context, g Gomega, hr *helmv2.HelmRelease, revision string) { + hr.Status.LastAppliedRevision = revision + g.Expect(k8sClient.Status().Update(ctx, hr)).To(Succeed()) +} + +func setAppStatusReadyCondition(ctx context.Context, g Gomega, hr *helmv2.HelmRelease) { + apimeta.SetStatusCondition(&hr.Status.Conditions, metav1.Condition{Type: "Ready", Status: metav1.ConditionTrue, Reason: "test"}) + g.Expect(k8sClient.Status().Update(ctx, hr)).To(Succeed()) +} + +func setStrategyRegistry(t *testing.T, r *PipelineReconciler) *strategy.MockStrategy { + mockCtrl := gomock.NewController(t) + mockStrategy := strategy.NewMockStrategy(mockCtrl) + + r.stratReg.Register(mockStrategy) + + return mockStrategy } func getPipeline(ctx context.Context, g Gomega, key client.ObjectKey) *v1alpha1.Pipeline { @@ -137,23 +349,29 @@ func getTargetStatus(g Gomega, pipeline *v1alpha1.Pipeline, envName string, targ return env.Targets[target] } -func checkReadyCondition(ctx context.Context, g Gomega, n types.NamespacedName, status metav1.ConditionStatus, reason string) { +func checkCondition(ctx context.Context, g Gomega, n types.NamespacedName, conditionType string, status metav1.ConditionStatus, reason string) { pipeline := &v1alpha1.Pipeline{} - assrt := g.Eventually(func() []metav1.Condition { + assrt := g.Eventually(func() metav1.Condition { err := k8sClient.Get(ctx, n, pipeline) if err != nil { - return nil + return metav1.Condition{} + } + + cond := apimeta.FindStatusCondition(pipeline.Status.Conditions, conditionType) + if cond == nil { + return metav1.Condition{} } - return pipeline.Status.Conditions + + return *cond }, defaultTimeout, defaultInterval) cond := metav1.Condition{ - Type: meta.ReadyCondition, + Type: conditionType, Status: status, Reason: reason, } - assrt.Should(conditions.MatchConditions([]metav1.Condition{cond})) + assrt.Should(conditions.MatchCondition(cond)) } func newPipeline(ctx context.Context, g Gomega, name string, ns string, clusters []*clusterctrlv1alpha1.GitopsCluster) *v1alpha1.Pipeline { diff --git a/controllers/leveltriggered/suite_test.go b/controllers/leveltriggered/suite_test.go index 98db0b2..bbccc60 100644 --- a/controllers/leveltriggered/suite_test.go +++ b/controllers/leveltriggered/suite_test.go @@ -19,6 +19,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/envtest" "github.com/weaveworks/pipeline-controller/api/v1alpha1" + "github.com/weaveworks/pipeline-controller/server/strategy" ) var k8sManager ctrl.Manager @@ -26,6 +27,7 @@ var k8sClient client.Client var testEnv *envtest.Environment var kubeConfig []byte var eventRecorder *testEventRecorder +var pipelineReconciler *PipelineReconciler type testEvent struct { object runtime.Object @@ -131,12 +133,14 @@ func TestMain(m *testing.M) { eventRecorder = &testEventRecorder{events: map[string][]testEvent{}} - err = (&PipelineReconciler{ + pipelineReconciler = &PipelineReconciler{ Client: k8sManager.GetClient(), Scheme: scheme.Scheme, targetScheme: scheme.Scheme, recorder: eventRecorder, - }).SetupWithManager(k8sManager) + stratReg: strategy.StrategyRegistry{}, + } + err = pipelineReconciler.SetupWithManager(k8sManager) if err != nil { log.Fatalf("setup pipeline controller failed: %s", err) } diff --git a/go.mod b/go.mod index c291c71..cedc637 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,6 @@ require ( github.com/go-git/go-git/v5 v5.6.1 github.com/go-logr/logr v1.2.3 github.com/go-logr/stdr v1.2.2 - github.com/golang/mock v1.6.0 github.com/google/go-github/v32 v32.1.0 github.com/google/go-github/v49 v49.1.0 github.com/hashicorp/go-uuid v1.0.3 @@ -35,6 +34,7 @@ require ( github.com/weaveworks/cluster-controller v1.3.0 github.com/weaveworks/pipeline-controller/api v0.0.0 github.com/xanzy/go-gitlab v0.78.0 + go.uber.org/mock v0.3.0 golang.org/x/oauth2 v0.6.0 k8s.io/api v0.25.2 k8s.io/apimachinery v0.27.0-beta.0 diff --git a/go.sum b/go.sum index e4e0652..afa6dd4 100644 --- a/go.sum +++ b/go.sum @@ -228,8 +228,6 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -495,7 +493,6 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= @@ -509,6 +506,8 @@ go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= +go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo= +go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= @@ -565,7 +564,6 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -637,7 +635,6 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -775,7 +772,6 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= diff --git a/go.work.sum b/go.work.sum index 96d5337..d3516da 100644 --- a/go.work.sum +++ b/go.work.sum @@ -455,6 +455,8 @@ github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keL github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -703,6 +705,7 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= @@ -775,12 +778,14 @@ golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDA golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -806,6 +811,7 @@ golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 h1:lxqLZaMad/dJHMFZH0NiNpiEZI/nhgWhe4wgzpE+MuA= golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -846,6 +852,7 @@ golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= diff --git a/main.go b/main.go index 3e2be93..bbe2919 100644 --- a/main.go +++ b/main.go @@ -111,12 +111,33 @@ func main() { os.Exit(1) } + pullRequestStrategy, err := pullrequest.New( + mgr.GetClient(), + log.WithValues("strategy", "pullrequest"), + ) + if err != nil { + setupLog.Error(err, "unable to create GitHub promotion strategy") + os.Exit(1) + } + + var eventRecorder *events.Recorder + if eventRecorder, err = events.NewRecorder(mgr, ctrl.Log, eventsAddr, controllerName); err != nil { + setupLog.Error(err, "unable to create event recorder") + os.Exit(1) + } + notificationStrat, _ := notification.NewNotification(mgr.GetClient(), eventRecorder) + + var stratReg strategy.StrategyRegistry + stratReg.Register(pullRequestStrategy) + stratReg.Register(notificationStrat) + var startErr error if useLevelTriggeredController { startErr = leveltriggered.NewPipelineReconciler( mgr.GetClient(), mgr.GetScheme(), controllerName, + stratReg, ).SetupWithManager(mgr) } else { startErr = controllers.NewPipelineReconciler( @@ -142,26 +163,6 @@ func main() { ctx := ctrl.SetupSignalHandler() - pullRequestStrategy, err := pullrequest.New( - mgr.GetClient(), - log.WithValues("strategy", "pullrequest"), - ) - if err != nil { - setupLog.Error(err, "unable to create GitHub promotion strategy") - os.Exit(1) - } - - var eventRecorder *events.Recorder - if eventRecorder, err = events.NewRecorder(mgr, ctrl.Log, eventsAddr, controllerName); err != nil { - setupLog.Error(err, "unable to create event recorder") - os.Exit(1) - } - notificationStrat, _ := notification.NewNotification(mgr.GetClient(), eventRecorder) - - var stratReg strategy.StrategyRegistry - stratReg.Register(pullRequestStrategy) - stratReg.Register(notificationStrat) - promServer, err := server.NewPromotionServer( mgr.GetClient(), server.WithRateLimit(promotionRateLimit, time.Duration(promotionRateLimitIntervalSeconds)*time.Second), diff --git a/pkg/conditions/util.go b/pkg/conditions/util.go index 3a7941b..9a10ec8 100644 --- a/pkg/conditions/util.go +++ b/pkg/conditions/util.go @@ -2,7 +2,10 @@ package conditions import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -const ReadyCondition = "Ready" +const ( + ReadyCondition = "Ready" + PromotionPendingCondition = "PromotionPending" +) func IsReady(cs []metav1.Condition) bool { for _, c := range cs { diff --git a/server/strategy/mock_strategy.go b/server/strategy/mock_strategy.go new file mode 100644 index 0000000..6cc7c37 --- /dev/null +++ b/server/strategy/mock_strategy.go @@ -0,0 +1,69 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/weaveworks/pipeline-controller/server/strategy (interfaces: Strategy) +// +// Generated by this command: +// +// mockgen -destination mock_strategy_test.go -package strategy github.com/weaveworks/pipeline-controller/server/strategy Strategy +// +// Package strategy is a generated GoMock package. +package strategy + +import ( + context "context" + reflect "reflect" + + v1alpha1 "github.com/weaveworks/pipeline-controller/api/v1alpha1" + gomock "go.uber.org/mock/gomock" +) + +// MockStrategy is a mock of Strategy interface. +type MockStrategy struct { + ctrl *gomock.Controller + recorder *MockStrategyMockRecorder +} + +// MockStrategyMockRecorder is the mock recorder for MockStrategy. +type MockStrategyMockRecorder struct { + mock *MockStrategy +} + +// NewMockStrategy creates a new mock instance. +func NewMockStrategy(ctrl *gomock.Controller) *MockStrategy { + mock := &MockStrategy{ctrl: ctrl} + mock.recorder = &MockStrategyMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockStrategy) EXPECT() *MockStrategyMockRecorder { + return m.recorder +} + +// Handles mocks base method. +func (m *MockStrategy) Handles(arg0 v1alpha1.Promotion) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Handles", arg0) + ret0, _ := ret[0].(bool) + return ret0 +} + +// Handles indicates an expected call of Handles. +func (mr *MockStrategyMockRecorder) Handles(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Handles", reflect.TypeOf((*MockStrategy)(nil).Handles), arg0) +} + +// Promote mocks base method. +func (m *MockStrategy) Promote(arg0 context.Context, arg1 v1alpha1.Promotion, arg2 Promotion) (*PromotionResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Promote", arg0, arg1, arg2) + ret0, _ := ret[0].(*PromotionResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Promote indicates an expected call of Promote. +func (mr *MockStrategyMockRecorder) Promote(arg0, arg1, arg2 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Promote", reflect.TypeOf((*MockStrategy)(nil).Promote), arg0, arg1, arg2) +} diff --git a/server/strategy/pullrequest/mock_gitprovider_test.go b/server/strategy/pullrequest/mock_gitprovider_test.go index 5ad1b5b..10048f0 100644 --- a/server/strategy/pullrequest/mock_gitprovider_test.go +++ b/server/strategy/pullrequest/mock_gitprovider_test.go @@ -1,6 +1,10 @@ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/fluxcd/go-git-providers/gitprovider (interfaces: Client,UserRepositoriesClient,UserRepository,PullRequestClient,PullRequest,OrgRepositoriesClient,OrgRepository) - +// +// Generated by this command: +// +// mockgen -destination mock_gitprovider_test.go -package pullrequest_test github.com/fluxcd/go-git-providers/gitprovider Client,UserRepositoriesClient,UserRepository,PullRequestClient,PullRequest,OrgRepositoriesClient,OrgRepository +// // Package pullrequest_test is a generated GoMock package. package pullrequest_test @@ -9,7 +13,7 @@ import ( reflect "reflect" gitprovider "github.com/fluxcd/go-git-providers/gitprovider" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockClient is a mock of Client interface. @@ -45,7 +49,7 @@ func (m *MockClient) HasTokenPermission(arg0 context.Context, arg1 gitprovider.T } // HasTokenPermission indicates an expected call of HasTokenPermission. -func (mr *MockClientMockRecorder) HasTokenPermission(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockClientMockRecorder) HasTokenPermission(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasTokenPermission", reflect.TypeOf((*MockClient)(nil).HasTokenPermission), arg0, arg1) } @@ -93,10 +97,10 @@ func (mr *MockClientMockRecorder) ProviderID() *gomock.Call { } // Raw mocks base method. -func (m *MockClient) Raw() interface{} { +func (m *MockClient) Raw() any { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Raw") - ret0, _ := ret[0].(interface{}) + ret0, _ := ret[0].(any) return ret0 } @@ -160,7 +164,7 @@ func (m *MockUserRepositoriesClient) EXPECT() *MockUserRepositoriesClientMockRec // Create mocks base method. func (m *MockUserRepositoriesClient) Create(arg0 context.Context, arg1 gitprovider.UserRepositoryRef, arg2 gitprovider.RepositoryInfo, arg3 ...gitprovider.RepositoryCreateOption) (gitprovider.UserRepository, error) { m.ctrl.T.Helper() - varargs := []interface{}{arg0, arg1, arg2} + varargs := []any{arg0, arg1, arg2} for _, a := range arg3 { varargs = append(varargs, a) } @@ -171,9 +175,9 @@ func (m *MockUserRepositoriesClient) Create(arg0 context.Context, arg1 gitprovid } // Create indicates an expected call of Create. -func (mr *MockUserRepositoriesClientMockRecorder) Create(arg0, arg1, arg2 interface{}, arg3 ...interface{}) *gomock.Call { +func (mr *MockUserRepositoriesClientMockRecorder) Create(arg0, arg1, arg2 any, arg3 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{arg0, arg1, arg2}, arg3...) + varargs := append([]any{arg0, arg1, arg2}, arg3...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockUserRepositoriesClient)(nil).Create), varargs...) } @@ -187,7 +191,7 @@ func (m *MockUserRepositoriesClient) Get(arg0 context.Context, arg1 gitprovider. } // Get indicates an expected call of Get. -func (mr *MockUserRepositoriesClientMockRecorder) Get(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockUserRepositoriesClientMockRecorder) Get(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockUserRepositoriesClient)(nil).Get), arg0, arg1) } @@ -202,7 +206,7 @@ func (m *MockUserRepositoriesClient) List(arg0 context.Context, arg1 gitprovider } // List indicates an expected call of List. -func (mr *MockUserRepositoriesClientMockRecorder) List(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockUserRepositoriesClientMockRecorder) List(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockUserRepositoriesClient)(nil).List), arg0, arg1) } @@ -210,7 +214,7 @@ func (mr *MockUserRepositoriesClientMockRecorder) List(arg0, arg1 interface{}) * // Reconcile mocks base method. func (m *MockUserRepositoriesClient) Reconcile(arg0 context.Context, arg1 gitprovider.UserRepositoryRef, arg2 gitprovider.RepositoryInfo, arg3 ...gitprovider.RepositoryReconcileOption) (gitprovider.UserRepository, bool, error) { m.ctrl.T.Helper() - varargs := []interface{}{arg0, arg1, arg2} + varargs := []any{arg0, arg1, arg2} for _, a := range arg3 { varargs = append(varargs, a) } @@ -222,9 +226,9 @@ func (m *MockUserRepositoriesClient) Reconcile(arg0 context.Context, arg1 gitpro } // Reconcile indicates an expected call of Reconcile. -func (mr *MockUserRepositoriesClientMockRecorder) Reconcile(arg0, arg1, arg2 interface{}, arg3 ...interface{}) *gomock.Call { +func (mr *MockUserRepositoriesClientMockRecorder) Reconcile(arg0, arg1, arg2 any, arg3 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{arg0, arg1, arg2}, arg3...) + varargs := append([]any{arg0, arg1, arg2}, arg3...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Reconcile", reflect.TypeOf((*MockUserRepositoriesClient)(nil).Reconcile), varargs...) } @@ -252,10 +256,10 @@ func (m *MockUserRepository) EXPECT() *MockUserRepositoryMockRecorder { } // APIObject mocks base method. -func (m *MockUserRepository) APIObject() interface{} { +func (m *MockUserRepository) APIObject() any { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "APIObject") - ret0, _ := ret[0].(interface{}) + ret0, _ := ret[0].(any) return ret0 } @@ -302,7 +306,7 @@ func (m *MockUserRepository) Delete(arg0 context.Context) error { } // Delete indicates an expected call of Delete. -func (mr *MockUserRepositoryMockRecorder) Delete(arg0 interface{}) *gomock.Call { +func (mr *MockUserRepositoryMockRecorder) Delete(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockUserRepository)(nil).Delete), arg0) } @@ -373,7 +377,7 @@ func (m *MockUserRepository) Reconcile(arg0 context.Context) (bool, error) { } // Reconcile indicates an expected call of Reconcile. -func (mr *MockUserRepositoryMockRecorder) Reconcile(arg0 interface{}) *gomock.Call { +func (mr *MockUserRepositoryMockRecorder) Reconcile(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Reconcile", reflect.TypeOf((*MockUserRepository)(nil).Reconcile), arg0) } @@ -401,7 +405,7 @@ func (m *MockUserRepository) Set(arg0 gitprovider.RepositoryInfo) error { } // Set indicates an expected call of Set. -func (mr *MockUserRepositoryMockRecorder) Set(arg0 interface{}) *gomock.Call { +func (mr *MockUserRepositoryMockRecorder) Set(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Set", reflect.TypeOf((*MockUserRepository)(nil).Set), arg0) } @@ -429,7 +433,7 @@ func (m *MockUserRepository) Update(arg0 context.Context) error { } // Update indicates an expected call of Update. -func (mr *MockUserRepositoryMockRecorder) Update(arg0 interface{}) *gomock.Call { +func (mr *MockUserRepositoryMockRecorder) Update(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockUserRepository)(nil).Update), arg0) } @@ -467,7 +471,7 @@ func (m *MockPullRequestClient) Create(arg0 context.Context, arg1, arg2, arg3, a } // Create indicates an expected call of Create. -func (mr *MockPullRequestClientMockRecorder) Create(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { +func (mr *MockPullRequestClientMockRecorder) Create(arg0, arg1, arg2, arg3, arg4 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockPullRequestClient)(nil).Create), arg0, arg1, arg2, arg3, arg4) } @@ -482,7 +486,7 @@ func (m *MockPullRequestClient) Edit(arg0 context.Context, arg1 int, arg2 gitpro } // Edit indicates an expected call of Edit. -func (mr *MockPullRequestClientMockRecorder) Edit(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockPullRequestClientMockRecorder) Edit(arg0, arg1, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Edit", reflect.TypeOf((*MockPullRequestClient)(nil).Edit), arg0, arg1, arg2) } @@ -497,7 +501,7 @@ func (m *MockPullRequestClient) Get(arg0 context.Context, arg1 int) (gitprovider } // Get indicates an expected call of Get. -func (mr *MockPullRequestClientMockRecorder) Get(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockPullRequestClientMockRecorder) Get(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockPullRequestClient)(nil).Get), arg0, arg1) } @@ -512,7 +516,7 @@ func (m *MockPullRequestClient) List(arg0 context.Context) ([]gitprovider.PullRe } // List indicates an expected call of List. -func (mr *MockPullRequestClientMockRecorder) List(arg0 interface{}) *gomock.Call { +func (mr *MockPullRequestClientMockRecorder) List(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockPullRequestClient)(nil).List), arg0) } @@ -526,7 +530,7 @@ func (m *MockPullRequestClient) Merge(arg0 context.Context, arg1 int, arg2 gitpr } // Merge indicates an expected call of Merge. -func (mr *MockPullRequestClientMockRecorder) Merge(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { +func (mr *MockPullRequestClientMockRecorder) Merge(arg0, arg1, arg2, arg3 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Merge", reflect.TypeOf((*MockPullRequestClient)(nil).Merge), arg0, arg1, arg2, arg3) } @@ -555,10 +559,10 @@ func (m *MockPullRequest) EXPECT() *MockPullRequestMockRecorder { } // APIObject mocks base method. -func (m *MockPullRequest) APIObject() interface{} { +func (m *MockPullRequest) APIObject() any { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "APIObject") - ret0, _ := ret[0].(interface{}) + ret0, _ := ret[0].(any) return ret0 } @@ -608,7 +612,7 @@ func (m *MockOrgRepositoriesClient) EXPECT() *MockOrgRepositoriesClientMockRecor // Create mocks base method. func (m *MockOrgRepositoriesClient) Create(arg0 context.Context, arg1 gitprovider.OrgRepositoryRef, arg2 gitprovider.RepositoryInfo, arg3 ...gitprovider.RepositoryCreateOption) (gitprovider.OrgRepository, error) { m.ctrl.T.Helper() - varargs := []interface{}{arg0, arg1, arg2} + varargs := []any{arg0, arg1, arg2} for _, a := range arg3 { varargs = append(varargs, a) } @@ -619,9 +623,9 @@ func (m *MockOrgRepositoriesClient) Create(arg0 context.Context, arg1 gitprovide } // Create indicates an expected call of Create. -func (mr *MockOrgRepositoriesClientMockRecorder) Create(arg0, arg1, arg2 interface{}, arg3 ...interface{}) *gomock.Call { +func (mr *MockOrgRepositoriesClientMockRecorder) Create(arg0, arg1, arg2 any, arg3 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{arg0, arg1, arg2}, arg3...) + varargs := append([]any{arg0, arg1, arg2}, arg3...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockOrgRepositoriesClient)(nil).Create), varargs...) } @@ -635,7 +639,7 @@ func (m *MockOrgRepositoriesClient) Get(arg0 context.Context, arg1 gitprovider.O } // Get indicates an expected call of Get. -func (mr *MockOrgRepositoriesClientMockRecorder) Get(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockOrgRepositoriesClientMockRecorder) Get(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockOrgRepositoriesClient)(nil).Get), arg0, arg1) } @@ -650,7 +654,7 @@ func (m *MockOrgRepositoriesClient) List(arg0 context.Context, arg1 gitprovider. } // List indicates an expected call of List. -func (mr *MockOrgRepositoriesClientMockRecorder) List(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockOrgRepositoriesClientMockRecorder) List(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockOrgRepositoriesClient)(nil).List), arg0, arg1) } @@ -658,7 +662,7 @@ func (mr *MockOrgRepositoriesClientMockRecorder) List(arg0, arg1 interface{}) *g // Reconcile mocks base method. func (m *MockOrgRepositoriesClient) Reconcile(arg0 context.Context, arg1 gitprovider.OrgRepositoryRef, arg2 gitprovider.RepositoryInfo, arg3 ...gitprovider.RepositoryReconcileOption) (gitprovider.OrgRepository, bool, error) { m.ctrl.T.Helper() - varargs := []interface{}{arg0, arg1, arg2} + varargs := []any{arg0, arg1, arg2} for _, a := range arg3 { varargs = append(varargs, a) } @@ -670,9 +674,9 @@ func (m *MockOrgRepositoriesClient) Reconcile(arg0 context.Context, arg1 gitprov } // Reconcile indicates an expected call of Reconcile. -func (mr *MockOrgRepositoriesClientMockRecorder) Reconcile(arg0, arg1, arg2 interface{}, arg3 ...interface{}) *gomock.Call { +func (mr *MockOrgRepositoriesClientMockRecorder) Reconcile(arg0, arg1, arg2 any, arg3 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{arg0, arg1, arg2}, arg3...) + varargs := append([]any{arg0, arg1, arg2}, arg3...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Reconcile", reflect.TypeOf((*MockOrgRepositoriesClient)(nil).Reconcile), varargs...) } @@ -700,10 +704,10 @@ func (m *MockOrgRepository) EXPECT() *MockOrgRepositoryMockRecorder { } // APIObject mocks base method. -func (m *MockOrgRepository) APIObject() interface{} { +func (m *MockOrgRepository) APIObject() any { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "APIObject") - ret0, _ := ret[0].(interface{}) + ret0, _ := ret[0].(any) return ret0 } @@ -750,7 +754,7 @@ func (m *MockOrgRepository) Delete(arg0 context.Context) error { } // Delete indicates an expected call of Delete. -func (mr *MockOrgRepositoryMockRecorder) Delete(arg0 interface{}) *gomock.Call { +func (mr *MockOrgRepositoryMockRecorder) Delete(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockOrgRepository)(nil).Delete), arg0) } @@ -821,7 +825,7 @@ func (m *MockOrgRepository) Reconcile(arg0 context.Context) (bool, error) { } // Reconcile indicates an expected call of Reconcile. -func (mr *MockOrgRepositoryMockRecorder) Reconcile(arg0 interface{}) *gomock.Call { +func (mr *MockOrgRepositoryMockRecorder) Reconcile(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Reconcile", reflect.TypeOf((*MockOrgRepository)(nil).Reconcile), arg0) } @@ -849,7 +853,7 @@ func (m *MockOrgRepository) Set(arg0 gitprovider.RepositoryInfo) error { } // Set indicates an expected call of Set. -func (mr *MockOrgRepositoryMockRecorder) Set(arg0 interface{}) *gomock.Call { +func (mr *MockOrgRepositoryMockRecorder) Set(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Set", reflect.TypeOf((*MockOrgRepository)(nil).Set), arg0) } @@ -891,7 +895,7 @@ func (m *MockOrgRepository) Update(arg0 context.Context) error { } // Update indicates an expected call of Update. -func (mr *MockOrgRepositoryMockRecorder) Update(arg0 interface{}) *gomock.Call { +func (mr *MockOrgRepositoryMockRecorder) Update(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockOrgRepository)(nil).Update), arg0) } diff --git a/server/strategy/pullrequest/pullrequest_test.go b/server/strategy/pullrequest/pullrequest_test.go index bfa278b..8e4d6ce 100644 --- a/server/strategy/pullrequest/pullrequest_test.go +++ b/server/strategy/pullrequest/pullrequest_test.go @@ -25,8 +25,8 @@ import ( "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/object" "github.com/go-git/go-git/v5/storage/memory" - "github.com/golang/mock/gomock" . "github.com/onsi/gomega" + "go.uber.org/mock/gomock" corev1 "k8s.io/api/core/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" diff --git a/server/strategy/strategy.go b/server/strategy/strategy.go index 0474a41..eabb1a4 100644 --- a/server/strategy/strategy.go +++ b/server/strategy/strategy.go @@ -7,6 +7,8 @@ import ( pipelinev1alpha1 "github.com/weaveworks/pipeline-controller/api/v1alpha1" ) +//go:generate mockgen -destination mock_strategy_test.go -package strategy github.com/weaveworks/pipeline-controller/server/strategy Strategy + // Strategy is the interface that all types need to implement that intend to handle at least one of the strategies requested in a Pipeline's // `.spec.promotion` field. type Strategy interface {