From 7485e652b4ee76f63f9fd55b52ff270bf3a1c7b4 Mon Sep 17 00:00:00 2001 From: Andrew Pantuso Date: Thu, 8 Sep 2022 16:13:40 -0400 Subject: [PATCH 1/2] feat: add dynamic network policy creation --- internal/controllers/helpers.go | 28 ++ internal/controllers/options.go | 15 +- internal/controllers/parameter_getter.go | 39 +- internal/controllers/parameter_getter_test.go | 10 +- internal/controllers/phase/phase.go | 57 ++- .../phase_apply_network_policies.go | 160 ++++++++ .../phase_apply_network_policies_test.go | 358 ++++++++++++++++++ .../phase_simulate_reconciliation.go | 6 +- internal/controllers/phase_uninstall.go | 11 - .../controllers/reference_addon_controller.go | 34 +- 10 files changed, 692 insertions(+), 26 deletions(-) create mode 100644 internal/controllers/helpers.go create mode 100644 internal/controllers/phase_apply_network_policies.go create mode 100644 internal/controllers/phase_apply_network_policies_test.go diff --git a/internal/controllers/helpers.go b/internal/controllers/helpers.go new file mode 100644 index 00000000..916544ab --- /dev/null +++ b/internal/controllers/helpers.go @@ -0,0 +1,28 @@ +package controllers + +import ( + "errors" + "fmt" +) + +var ErrEmptyOptionValue = errors.New("empty option value") + +func validateOptionValue(val string) error { + if val != "" { + return nil + } + + return ErrEmptyOptionValue +} + +func generateIngressPolicyName(prefix string) string { + return fmt.Sprintf("%s-ingress", prefix) +} + +func boolPtr(b bool) *bool { + return &b +} + +func stringPtr(s string) *string { + return &s +} diff --git a/internal/controllers/options.go b/internal/controllers/options.go index 34b74ac4..591b614b 100644 --- a/internal/controllers/options.go +++ b/internal/controllers/options.go @@ -1,6 +1,9 @@ package controllers -import "github.com/go-logr/logr" +import ( + "github.com/go-logr/logr" + netv1 "k8s.io/api/networking/v1" +) type WithLog struct{ Log logr.Logger } @@ -8,6 +11,10 @@ func (w WithLog) ConfigureReferenceAddonReconciler(c *ReferenceAddonReconcilerCo c.Log = w.Log } +func (w WithLog) ConfigurePhaseApplyNetworkPolicies(c *PhaseApplyNetworkPoliciesConfig) { + c.Log = w.Log +} + func (w WithLog) ConfigurePhaseSimulateReconciliation(c *PhaseSimulateReconciliationConfig) { c.Log = w.Log } @@ -80,6 +87,12 @@ func (w WithNamespace) ConfigureListCSVs(c *ListCSVsConfig) { c.Namespace = string(w) } +type WithPolicies []netv1.NetworkPolicy + +func (w WithPolicies) ConfigurePhaseApplyNetworkPolicies(c *PhaseApplyNetworkPoliciesConfig) { + c.Policies = []netv1.NetworkPolicy(w) +} + type WithPrefix string func (w WithPrefix) ConfigureListCSVs(c *ListCSVsConfig) { diff --git a/internal/controllers/parameter_getter.go b/internal/controllers/parameter_getter.go index dad5d7c0..144c28ba 100644 --- a/internal/controllers/parameter_getter.go +++ b/internal/controllers/parameter_getter.go @@ -2,7 +2,9 @@ package controllers import ( "context" + "errors" "fmt" + "strings" "github.com/openshift/reference-addon/internal/controllers/phase" corev1 "k8s.io/api/core/v1" @@ -32,7 +34,8 @@ type SecretParameterGetter struct { } const ( - sizeParameterID = "size" + applyNetworkPoliciesID = "applynetworkpolicies" + sizeParameterID = "size" ) func (s *SecretParameterGetter) GetParameters(ctx context.Context) (phase.RequestParameters, error) { @@ -44,19 +47,30 @@ func (s *SecretParameterGetter) GetParameters(ctx context.Context) (phase.Reques Name: s.cfg.Name, } - var params phase.RequestParameters - var secret corev1.Secret if err := s.client.Get(ctx, key, &secret); err != nil { - return params, fmt.Errorf("retrieving addon parameters secret: %w", err) + return phase.NewRequestParameters(), fmt.Errorf("retrieving addon parameters secret: %w", err) + } + + var opts []phase.RequestParametersOption + + if val, ok := secret.Data[applyNetworkPoliciesID]; ok { + b, err := parseBool(string(val)) + if err != nil { + return phase.NewRequestParameters(), fmt.Errorf("parsing 'ApplyNetworkPolicies' value: %w", err) + } + + opts = append(opts, phase.WithApplyNetworkPolicies{Value: &b}) } if val, ok := secret.Data[sizeParameterID]; ok { - params.Size = string(val) + s := string(val) + + opts = append(opts, phase.WithSize{Value: &s}) } - return params, nil + return phase.NewRequestParameters(opts...), nil } type SecretParameterGetterConfig struct { @@ -73,3 +87,16 @@ func (c *SecretParameterGetterConfig) Option(opts ...SecretParameteterGetterOpti type SecretParameteterGetterOption interface { ConfigureSecretParameterGetter(*SecretParameterGetterConfig) } + +var ErrInvalidBoolValue = errors.New("invalid bool value") + +func parseBool(maybeBool string) (bool, error) { + switch strings.ToLower(maybeBool) { + case "true": + return true, nil + case "false": + return false, nil + default: + return false, ErrInvalidBoolValue + } +} diff --git a/internal/controllers/parameter_getter_test.go b/internal/controllers/parameter_getter_test.go index 22781fcc..212d6d44 100644 --- a/internal/controllers/parameter_getter_test.go +++ b/internal/controllers/parameter_getter_test.go @@ -34,14 +34,16 @@ func TestSecretParameterGetter(t *testing.T) { Namespace: "test-namespace", }, Data: map[string][]byte{ - "size": []byte("1"), + "applynetworkpolicies": []byte("true"), + "size": []byte("1"), }, }, Namespace: "test-namespace", Name: "test", - ExpectedParams: phase.RequestParameters{ - Size: "1", - }, + ExpectedParams: phase.NewRequestParameters( + phase.WithApplyNetworkPolicies{Value: boolPtr(true)}, + phase.WithSize{Value: stringPtr("1")}, + ), }, } { tc := tc diff --git a/internal/controllers/phase/phase.go b/internal/controllers/phase/phase.go index 1133271f..b25c44ea 100644 --- a/internal/controllers/phase/phase.go +++ b/internal/controllers/phase/phase.go @@ -15,8 +15,63 @@ type Request struct { Params RequestParameters } +func NewRequestParameters(opts ...RequestParametersOption) RequestParameters { + var cfg RequestParametersConfig + + cfg.Option(opts...) + + return RequestParameters{ + applyNetworkPolicies: cfg.ApplyNetworkPolicies, + size: cfg.Size, + } +} + type RequestParameters struct { - Size string + applyNetworkPolicies *bool + size *string +} + +func (p *RequestParameters) GetSize() (string, bool) { + if p.size == nil { + return "", false + } + + return *p.size, true +} + +func (p *RequestParameters) GetApplyNetworkPolicies() (bool, bool) { + if p.applyNetworkPolicies == nil { + return false, false + } + + return *p.applyNetworkPolicies, true +} + +type RequestParametersConfig struct { + ApplyNetworkPolicies *bool + Size *string +} + +func (c *RequestParametersConfig) Option(opts ...RequestParametersOption) { + for _, opt := range opts { + opt.ConfigureRequestParameters(c) + } +} + +type WithApplyNetworkPolicies struct{ Value *bool } + +func (w WithApplyNetworkPolicies) ConfigureRequestParameters(c *RequestParametersConfig) { + c.ApplyNetworkPolicies = w.Value +} + +type WithSize struct{ Value *string } + +func (w WithSize) ConfigureRequestParameters(c *RequestParametersConfig) { + c.Size = w.Value +} + +type RequestParametersOption interface { + ConfigureRequestParameters(*RequestParametersConfig) } func Success() Result { diff --git a/internal/controllers/phase_apply_network_policies.go b/internal/controllers/phase_apply_network_policies.go new file mode 100644 index 00000000..9c43b53c --- /dev/null +++ b/internal/controllers/phase_apply_network_policies.go @@ -0,0 +1,160 @@ +package controllers + +import ( + "context" + "fmt" + + "github.com/go-logr/logr" + "github.com/openshift/reference-addon/internal/controllers/phase" + "go.uber.org/multierr" + netv1 "k8s.io/api/networking/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func NewPhaseApplyNetworkPolicies(client NetworkPolicyClient, opts ...PhaseApplyNetworkPoliciesOption) *PhaseApplyNetworkPolicies { + var cfg PhaseApplyNetworkPoliciesConfig + + cfg.Option(opts...) + cfg.Default() + + return &PhaseApplyNetworkPolicies{ + cfg: cfg, + + client: client, + } +} + +type PhaseApplyNetworkPolicies struct { + cfg PhaseApplyNetworkPoliciesConfig + + client NetworkPolicyClient +} + +func (p *PhaseApplyNetworkPolicies) Execute(ctx context.Context, req phase.Request) phase.Result { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + applyNetworkPolicies, ok := req.Params.GetApplyNetworkPolicies() + if !ok { + return phase.Success() + } + + if !applyNetworkPolicies { + return p.ensureNetworkPoliciesRemoved(ctx) + } + + return p.ensureNetworkPoliciesApplied(ctx) +} + +func (p *PhaseApplyNetworkPolicies) ensureNetworkPoliciesRemoved(ctx context.Context) phase.Result { + p.cfg.Log.Info("removing NetworkPolicies", "count", len(p.cfg.Policies)) + + if err := p.client.RemoveNetworkPolicies(ctx, p.cfg.Policies...); err != nil { + return phase.Error(fmt.Errorf("deleting NetworkPolicies: %w", err)) + } + + p.cfg.Log.Info("successfully removed NetworkPolicies", "count", len(p.cfg.Policies)) + + return phase.Success() +} + +func (p *PhaseApplyNetworkPolicies) ensureNetworkPoliciesApplied(ctx context.Context) phase.Result { + p.cfg.Log.Info("applying NetworkPolicies", "count", len(p.cfg.Policies)) + + if err := p.client.ApplyNetworkPolicies(ctx, p.cfg.Policies...); err != nil { + return phase.Error(fmt.Errorf("applying NetworkPolicies: %w", err)) + } + + p.cfg.Log.Info("successfully applied NetworkPolicies", "count", len(p.cfg.Policies)) + + return phase.Success() +} + +type PhaseApplyNetworkPoliciesConfig struct { + Log logr.Logger + + Policies []netv1.NetworkPolicy +} + +func (c *PhaseApplyNetworkPoliciesConfig) Option(opts ...PhaseApplyNetworkPoliciesOption) { + for _, opt := range opts { + opt.ConfigurePhaseApplyNetworkPolicies(c) + } +} + +func (c *PhaseApplyNetworkPoliciesConfig) Default() { + if c.Log == nil { + c.Log = logr.Discard() + } +} + +type PhaseApplyNetworkPoliciesOption interface { + ConfigurePhaseApplyNetworkPolicies(*PhaseApplyNetworkPoliciesConfig) +} + +type NetworkPolicyClient interface { + ApplyNetworkPolicies(ctx context.Context, policies ...netv1.NetworkPolicy) error + RemoveNetworkPolicies(ctx context.Context, policies ...netv1.NetworkPolicy) error +} + +func NewNetworkPolicyClientImpl(client client.Client) *NetworkPolicyClientImpl { + return &NetworkPolicyClientImpl{ + client: client, + } +} + +type NetworkPolicyClientImpl struct { + client client.Client +} + +func (c *NetworkPolicyClientImpl) ApplyNetworkPolicies(ctx context.Context, policies ...netv1.NetworkPolicy) error { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + var finalErr error + + for _, policy := range policies { + if err := c.createOrUpdatePolicy(ctx, policy); err != nil { + multierr.AppendInto(&finalErr, fmt.Errorf("creating/updating NetworkPolicy %q: %w", policy.Name, err)) + } + } + + return finalErr +} + +func (c *NetworkPolicyClientImpl) createOrUpdatePolicy(ctx context.Context, policy netv1.NetworkPolicy) error { + actualPolicy := &netv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: policy.Name, + Namespace: policy.Namespace, + }, + } + + _, err := ctrl.CreateOrUpdate(ctx, c.client, actualPolicy, func() error { + actualPolicy.Labels = labels.Merge(actualPolicy.Labels, policy.Labels) + actualPolicy.Spec = policy.Spec + + return nil + }) + + return err +} + +func (c *NetworkPolicyClientImpl) RemoveNetworkPolicies(ctx context.Context, policies ...netv1.NetworkPolicy) error { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + var finalErr error + + for _, policy := range policies { + if err := c.client.Delete(ctx, &policy); err != nil && !errors.IsNotFound(err) { + multierr.AppendInto(&finalErr, fmt.Errorf("deleting NetworkPolicy %q: %w", policy.Name, err)) + } + } + + return finalErr +} diff --git a/internal/controllers/phase_apply_network_policies_test.go b/internal/controllers/phase_apply_network_policies_test.go new file mode 100644 index 00000000..6f82d271 --- /dev/null +++ b/internal/controllers/phase_apply_network_policies_test.go @@ -0,0 +1,358 @@ +package controllers + +import ( + "context" + "testing" + + "github.com/openshift/reference-addon/internal/controllers/phase" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + netv1 "k8s.io/api/networking/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +func TestPhaseApplyNetworkPoliciesInterfaces(t *testing.T) { + t.Parallel() + + require.Implements(t, new(phase.Phase), new(PhaseApplyNetworkPolicies)) +} + +func TestPhaseApplyNetworkPolicies(t *testing.T) { + t.Parallel() + + for name, tc := range map[string]struct { + ApplyNetworkPolicy *bool + Policies []netv1.NetworkPolicy + }{ + "applyNetworkPolicies unset": { + ApplyNetworkPolicy: nil, + }, + "applyNetworkPolicies false/no NetworkPolicies": { + ApplyNetworkPolicy: boolPtr(false), + }, + "applyNetworkPolicies false/with NetworkPolicies": { + ApplyNetworkPolicy: boolPtr(false), + Policies: []netv1.NetworkPolicy{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace", + }, + }, + }, + }, + "applyNetworkPolicies true/no NetworkPolicies": { + ApplyNetworkPolicy: boolPtr(false), + }, + "applyNetworkPolicies true/with NetworkPolicies": { + ApplyNetworkPolicy: boolPtr(true), + Policies: []netv1.NetworkPolicy{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace", + }, + }, + }, + }, + } { + tc := tc + + t.Run(name, func(t *testing.T) { + t.Parallel() + + var m NetworkPolicyClientMock + + argList := make([]interface{}, 0, 1+len(tc.Policies)) + + argList = append(argList, mock.Anything) + + for _, p := range tc.Policies { + argList = append(argList, p) + } + + switch val := tc.ApplyNetworkPolicy; { + case val == nil: + case *val == true: + m. + On("ApplyNetworkPolicies", argList...). + Return(nil) + case *val == false: + m. + On("RemoveNetworkPolicies", argList...). + Return(nil) + } + + p := NewPhaseApplyNetworkPolicies( + &m, + WithPolicies(tc.Policies), + ) + + res := p.Execute(context.Background(), phase.Request{ + Params: phase.NewRequestParameters( + phase.WithApplyNetworkPolicies{Value: tc.ApplyNetworkPolicy}, + ), + }) + require.NoError(t, res.Error()) + + assert.True(t, res.IsSuccess()) + + m.AssertExpectations(t) + }) + } +} + +type NetworkPolicyClientMock struct { + mock.Mock +} + +func (m *NetworkPolicyClientMock) ApplyNetworkPolicies(ctx context.Context, policies ...netv1.NetworkPolicy) error { + argList := make([]interface{}, 0, 1+len(policies)) + + argList = append(argList, ctx) + + for _, p := range policies { + argList = append(argList, p) + } + + args := m.Called(argList...) + + return args.Error(0) +} + +func (m *NetworkPolicyClientMock) RemoveNetworkPolicies(ctx context.Context, policies ...netv1.NetworkPolicy) error { + argList := make([]interface{}, 0, 1+len(policies)) + + argList = append(argList, ctx) + + for _, p := range policies { + argList = append(argList, p) + } + + args := m.Called(argList...) + + return args.Error(0) +} + +func TestNetworkPolicyClientImplInterfaces(t *testing.T) { + t.Parallel() + + require.Implements(t, new(NetworkPolicyClient), new(NetworkPolicyClientImpl)) +} + +func TestNetworkPolicyClientImpl_ApplyNetworkPolicies(t *testing.T) { + t.Parallel() + + for name, tc := range map[string]struct { + ActualPolicies []netv1.NetworkPolicy + DesiredPolicies []netv1.NetworkPolicy + ExpectedPolicies []netv1.NetworkPolicy + }{ + "no existing policies/no new policies": { + ActualPolicies: []netv1.NetworkPolicy{}, + DesiredPolicies: []netv1.NetworkPolicy{}, + ExpectedPolicies: []netv1.NetworkPolicy{}, + }, + "existing policies/no new policies": { + ActualPolicies: []netv1.NetworkPolicy{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace", + }, + }, + }, + DesiredPolicies: []netv1.NetworkPolicy{}, + ExpectedPolicies: []netv1.NetworkPolicy{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace", + }, + }, + }, + }, + "no existing policies/one new policy": { + ActualPolicies: []netv1.NetworkPolicy{}, + DesiredPolicies: []netv1.NetworkPolicy{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace", + }, + }, + }, + ExpectedPolicies: []netv1.NetworkPolicy{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace", + }, + }, + }, + }, + "existing policy/one desired policy": { + ActualPolicies: []netv1.NetworkPolicy{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace", + }, + }, + }, + DesiredPolicies: []netv1.NetworkPolicy{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace", + }, + }, + }, + ExpectedPolicies: []netv1.NetworkPolicy{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace", + }, + }, + }, + }, + } { + tc := tc + + t.Run(name, func(t *testing.T) { + t.Parallel() + + objs := make([]client.Object, 0, len(tc.ActualPolicies)) + + for _, p := range tc.ActualPolicies { + objs = append(objs, &p) + } + + c := fake. + NewClientBuilder(). + WithObjects(objs...). + Build() + + npClient := NewNetworkPolicyClientImpl(c) + + require.NoError(t, npClient.ApplyNetworkPolicies( + context.Background(), + tc.DesiredPolicies..., + ), + ) + + for _, expected := range tc.ExpectedPolicies { + assert.NoError(t, c.Get( + context.Background(), + client.ObjectKeyFromObject(&expected), + new(netv1.NetworkPolicy), + ), + ) + } + }) + } +} + +func TestNetworkPolicyClientImpl_RemoveNetworkPolicies(t *testing.T) { + t.Parallel() + + for name, tc := range map[string]struct { + ActualPolicies []netv1.NetworkPolicy + DesiredPolicies []netv1.NetworkPolicy + ExpectedPolicies []netv1.NetworkPolicy + }{ + "no existing policies/no new policies": { + ActualPolicies: []netv1.NetworkPolicy{}, + DesiredPolicies: []netv1.NetworkPolicy{}, + ExpectedPolicies: []netv1.NetworkPolicy{}, + }, + "existing policies/no new policies": { + ActualPolicies: []netv1.NetworkPolicy{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace", + }, + }, + }, + DesiredPolicies: []netv1.NetworkPolicy{}, + ExpectedPolicies: []netv1.NetworkPolicy{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace", + }, + }, + }, + }, + "no existing policies/one new policy": { + ActualPolicies: []netv1.NetworkPolicy{}, + DesiredPolicies: []netv1.NetworkPolicy{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace", + }, + }, + }, + ExpectedPolicies: []netv1.NetworkPolicy{}, + }, + "existing policy/one desired policy": { + ActualPolicies: []netv1.NetworkPolicy{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace", + }, + }, + }, + DesiredPolicies: []netv1.NetworkPolicy{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-namespace", + }, + }, + }, + ExpectedPolicies: []netv1.NetworkPolicy{}, + }, + } { + tc := tc + + t.Run(name, func(t *testing.T) { + t.Parallel() + + objs := make([]client.Object, 0, len(tc.ActualPolicies)) + + for _, p := range tc.ActualPolicies { + objs = append(objs, &p) + } + + c := fake. + NewClientBuilder(). + WithObjects(objs...). + Build() + + npClient := NewNetworkPolicyClientImpl(c) + + require.NoError(t, npClient.RemoveNetworkPolicies( + context.Background(), + tc.DesiredPolicies..., + ), + ) + + for _, expected := range tc.ExpectedPolicies { + assert.NoError(t, c.Get( + context.Background(), + client.ObjectKeyFromObject(&expected), + new(netv1.NetworkPolicy), + ), + ) + } + }) + } +} diff --git a/internal/controllers/phase_simulate_reconciliation.go b/internal/controllers/phase_simulate_reconciliation.go index a61b8636..7b69113f 100644 --- a/internal/controllers/phase_simulate_reconciliation.go +++ b/internal/controllers/phase_simulate_reconciliation.go @@ -26,9 +26,13 @@ type PhaseSimulateReconciliation struct { func (p *PhaseSimulateReconciliation) Execute(ctx context.Context, req phase.Request) phase.Result { log := p.cfg.Log.WithValues("addon", req.Object.String()) + applyNetworkPolicies, _ := req.Params.GetApplyNetworkPolicies() + size, _ := req.Params.GetSize() + log.Info( "reconciling with addon parameters", - "Size", req.Params.Size, + "ApplyNetworkPolicies", applyNetworkPolicies, + "Size", size, ) // dummy code to indicate reconciliation of the reference-addon object diff --git a/internal/controllers/phase_uninstall.go b/internal/controllers/phase_uninstall.go index 68e3862e..54ed7459 100644 --- a/internal/controllers/phase_uninstall.go +++ b/internal/controllers/phase_uninstall.go @@ -2,7 +2,6 @@ package controllers import ( "context" - "errors" "fmt" "strings" @@ -279,16 +278,6 @@ func (c *ConfigMapUninstallSignalerConfig) Validate() error { return finalErr } -var ErrEmptyOptionValue = errors.New("empty option value") - -func validateOptionValue(val string) error { - if val != "" { - return nil - } - - return ErrEmptyOptionValue -} - type ConfigMapUninstallSignalerOption interface { ConfigureConfigMapUninstallSignaler(*ConfigMapUninstallSignalerConfig) } diff --git a/internal/controllers/reference_addon_controller.go b/internal/controllers/reference_addon_controller.go index 770640d0..0ae9ab91 100644 --- a/internal/controllers/reference_addon_controller.go +++ b/internal/controllers/reference_addon_controller.go @@ -18,6 +18,8 @@ import ( "github.com/openshift/reference-addon/internal/metrics" opsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" corev1 "k8s.io/api/core/v1" + netv1 "k8s.io/api/networking/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func NewReferenceAddonReconciler(client client.Client, getter ParameterGetter, opts ...ReferenceAddonReconcilerOption) (*ReferenceAddonReconciler, error) { @@ -36,7 +38,12 @@ func NewReferenceAddonReconciler(client client.Client, getter ParameterGetter, o return nil, fmt.Errorf("initializing uninstall signaler: %w", err) } - phaseUninstallLog := cfg.Log.WithName("phase").WithName("uninstall") + var ( + phaseLog = cfg.Log.WithName("phase") + phaseApplyNetworkPoliciesLog = phaseLog.WithName("applyNetworkPolicies") + phaseSimulateReconciliationLog = phaseLog.WithName("simulateReconciliation") + phaseUninstallLog = phaseLog.WithName("uninstall") + ) return &ReferenceAddonReconciler{ cfg: cfg, @@ -54,12 +61,30 @@ func NewReferenceAddonReconciler(client client.Client, getter ParameterGetter, o WithOperatorName(cfg.OperatorName), ), NewPhaseSimulateReconciliation( - WithLog{Log: cfg.Log.WithName("phase").WithName("simulate-reconciliation")}, + WithLog{Log: phaseSimulateReconciliationLog}, ), NewPhaseSendDummyMetrics( metrics.NewResponseSamplerImpl(), WithSampleURLs{"https://httpstat.us/503", "https://httpstat.us/200"}, ), + NewPhaseApplyNetworkPolicies( + NewNetworkPolicyClientImpl(client), + WithLog{Log: phaseApplyNetworkPoliciesLog}, + WithPolicies{ + netv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: generateIngressPolicyName(cfg.OperatorName), + Namespace: cfg.AddonNamespace, + }, + Spec: netv1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{}, + PolicyTypes: []netv1.PolicyType{ + netv1.PolicyTypeIngress, + }, + }, + }, + }, + ), }, }, nil } @@ -116,6 +141,11 @@ func (r *ReferenceAddonReconciler) SetupWithManager(mgr ctrl.Manager) error { &handler.EnqueueRequestForObject{}, builder.WithPredicates(hasName(r.cfg.AddonParameterSecretname)), ). + Watches( + &source.Kind{Type: &netv1.NetworkPolicy{}}, + &handler.EnqueueRequestForObject{}, + builder.WithPredicates(hasName(generateIngressPolicyName(r.cfg.OperatorName))), + ). Complete(r) } From a90e0ab57e3ceae47124d6360fd41c44bba958b4 Mon Sep 17 00:00:00 2001 From: Andrew Pantuso Date: Fri, 16 Sep 2022 15:29:17 -0400 Subject: [PATCH 2/2] ci: add integration tests --- Makefile | 34 ++- go.mod | 2 + go.sum | 51 ++++- .../apply_network_policies_test.go | 162 ++++++++++++++ integration/controllers/suite_test.go | 85 ++++++++ integration/controllers/uninstall_test.go | 205 ++++++++++++++++++ 6 files changed, 523 insertions(+), 16 deletions(-) create mode 100644 integration/controllers/apply_network_policies_test.go create mode 100644 integration/controllers/suite_test.go create mode 100644 integration/controllers/uninstall_test.go diff --git a/Makefile b/Makefile index 9b4774ee..e951df26 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,7 @@ GOIMPORTS_VERSION:=v0.1.0 GOLANGCI_LINT_VERSION:=v1.39.0 OLM_VERSION:=v0.17.0 OPM_VERSION:=v1.17.2 +SETUP_ENVTEST_VERSION:=latest # Build Flags export CGO_ENABLED:=0 @@ -77,7 +78,7 @@ $(KIND): $(eval KIND_TMP := $(shell mktemp -d)) @(cd "$(KIND_TMP)" \ && go mod init tmp \ - && go get "sigs.k8s.io/kind@$(KIND_VERSION)" \ + && go install "sigs.k8s.io/kind@$(KIND_VERSION)" \ ) 2>&1 | sed 's/^/ /' @rm -rf "$(KIND_TMP)" "$(dir $(KIND))" \ && mkdir -p "$(dir $(KIND))" \ @@ -91,7 +92,7 @@ $(CONTROLLER_GEN): $(eval CONTROLLER_GEN_TMP := $(shell mktemp -d)) @(cd "$(CONTROLLER_GEN_TMP)" \ && go mod init tmp \ - && go get "sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_GEN_VERSION)" \ + && go install "sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_GEN_VERSION)" \ ) 2>&1 | sed 's/^/ /' @rm -rf "$(CONTROLLER_GEN_TMP)" "$(dir $(CONTROLLER_GEN))" \ && mkdir -p "$(dir $(CONTROLLER_GEN))" \ @@ -105,7 +106,7 @@ $(YQ): $(eval YQ_TMP := $(shell mktemp -d)) @(cd "$(YQ_TMP)" \ && go mod init tmp \ - && go get "github.com/mikefarah/yq/$(YQ_VERSION)" \ + && go install "github.com/mikefarah/yq/$(YQ_VERSION)" \ ) 2>&1 | sed 's/^/ /' @rm -rf "$(YQ_TMP)" "$(dir $(YQ))" \ && mkdir -p "$(dir $(YQ))" \ @@ -119,7 +120,7 @@ $(GOIMPORTS): $(eval GOIMPORTS_TMP := $(shell mktemp -d)) @(cd "$(GOIMPORTS_TMP)" \ && go mod init tmp \ - && go get "golang.org/x/tools/cmd/goimports@$(GOIMPORTS_VERSION)" \ + && go install "golang.org/x/tools/cmd/goimports@$(GOIMPORTS_VERSION)" \ ) 2>&1 | sed 's/^/ /' @rm -rf "$(GOIMPORTS_TMP)" "$(dir $(GOIMPORTS))" \ && mkdir -p "$(dir $(GOIMPORTS))" \ @@ -135,7 +136,7 @@ $(GOLANGCI_LINT): $(eval GOLANGCI_LINT_TMP := $(shell mktemp -d)) @(cd "$(GOLANGCI_LINT_TMP)" \ && go mod init tmp \ - && go get "github.com/golangci/golangci-lint/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION)" \ + && go install "github.com/golangci/golangci-lint/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION)" \ ) 2>&1 | sed 's/^/ /' @rm -rf "$(GOLANGCI_LINT_TMP)" "$(dir $(GOLANGCI_LINT))" \ && mkdir -p "$(dir $(GOLANGCI_LINT))" \ @@ -157,13 +158,27 @@ $(OPM): && touch "$(OPM)" \ && echo +SETUP_ENVTEST:=$(DEPENDENCIES)/setup-envtest/$(SETUP_ENVTEST_VERSION) +$(SETUP_ENVTEST): + @echo "installing setup-envtest $(SETUP_ENVTEST_VERSION)..." + $(eval SETUP_ENVTEST_TMP := $(shell mktemp -d)) + @(cd "$(SETUP_ENVTEST_TMP)" \ + && go mod init tmp \ + && go install "sigs.k8s.io/controller-runtime/tools/setup-envtest@$(SETUP_ENVTEST_VERSION)" \ + ) 2>&1 | sed 's/^/ /' + @rm -rf "$(SETUP_ENVTEST_TMP)" "$(dir $(SETUP_ENVTEST))" \ + && mkdir -p "$(dir $(SETUP_ENVTEST))" \ + && touch "$(SETUP_ENVTEST)" \ + && echo + # installs all project dependencies dependencies: \ $(KIND) \ $(CONTROLLER_GEN) \ $(YQ) \ $(GOIMPORTS) \ - $(GOLANGCI_LINT) + $(GOLANGCI_LINT) \ + $(SETUP_ENVTEST) .PHONY: dependencies # ---------- @@ -209,6 +224,13 @@ test-unit: generate CGO_ENABLED=1 go test -race -v ./internal/... ./cmd/... .PHONY: test-unit +# Runs integration tests +test-integration: $(SETUP_ENVTEST) + $(eval ASSET_PATH := $(shell setup-envtest use -p path --bin-dir=$(GOBIN) 1.20.x!)) + + KUBEBUILDER_ASSETS=$(ASSET_PATH) go test -v ./integration/... +.PHONY: test-integration + # Runs the E2E testsuite against the currently selected cluster. # FORCE_FLAGS ensures that the tests will not be cached FORCE_FLAGS = -count=1 diff --git a/go.mod b/go.mod index c21499f9..d3655522 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,8 @@ go 1.16 require ( github.com/go-logr/logr v0.4.0 + github.com/onsi/ginkgo/v2 v2.1.6 + github.com/onsi/gomega v1.20.1 github.com/operator-framework/api v0.10.7 github.com/prometheus/client_golang v1.11.0 github.com/stretchr/testify v1.7.0 diff --git a/go.sum b/go.sum index 160c6e94..2a2b84e4 100644 --- a/go.sum +++ b/go.sum @@ -235,8 +235,9 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -254,6 +255,7 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -396,13 +398,20 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108 github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= +github.com/onsi/ginkgo/v2 v2.1.6 h1:Fx2POJZfKRQcM1pH49qSZiYeu319wji004qX+GDovrU= +github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.14.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= -github.com/onsi/gomega v1.15.0 h1:WjP/FQ/sk43MRmnEcT+MlDw2TFvkrXlprrPST/IudjU= github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q= +github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/operator-framework/api v0.10.7 h1:GlZJ6m+0WSVdSsSjTbhKKAvHXamWJXhwXHUhVwL8LBE= github.com/operator-framework/api v0.10.7/go.mod h1:PtQSNSuVrhSC6YE6JJJZv3nnZJc32osKX8FmFUZK05U= @@ -507,6 +516,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de 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.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= @@ -562,6 +573,7 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -599,6 +611,8 @@ golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hM 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.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -646,8 +660,12 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210520170846-37e1c6afe023 h1:ADo5wSpq2gqaCGQWzk7S5vd//0iyyLeAratkEoG5dLE= golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -672,6 +690,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ 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-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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= @@ -738,13 +757,21 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2 h1:c8PlLMqBbOHoqtjteWm5/kbe6rNY2pbRfbIMVnepueo= golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -752,8 +779,9 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -819,12 +847,13 @@ 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.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= +golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= @@ -935,8 +964,9 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -970,8 +1000,9 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/integration/controllers/apply_network_policies_test.go b/integration/controllers/apply_network_policies_test.go new file mode 100644 index 00000000..62856488 --- /dev/null +++ b/integration/controllers/apply_network_policies_test.go @@ -0,0 +1,162 @@ +package controllers + +import ( + "context" + "fmt" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/openshift/reference-addon/internal/controllers" + corev1 "k8s.io/api/core/v1" + netv1 "k8s.io/api/networking/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +var _ = Describe("Apply Network Policies Phase", Ordered, func() { + var ( + ctx context.Context + cancel context.CancelFunc + deleteLabel string + deleteLabelGen = nameGenerator("policies-test-label") + namespace string + namespaceGen = nameGenerator("policies-test-namespace") + operatorName string + operatorNameGen = nameGenerator("policies-test-operator") + parameterSecretName string + parameterSecretNameGen = nameGenerator("policies-test-secret") + ) + + BeforeEach(func() { + ctx, cancel = context.WithCancel(context.Background()) + + deleteLabel = deleteLabelGen() + namespace = namespaceGen() + operatorName = operatorNameGen() + parameterSecretName = parameterSecretNameGen() + + By("Starting manager with controllers") + + mgr, err := ctrl.NewManager(_cfg, ctrl.Options{ + Scheme: _scheme, + }) + Expect(err).ToNot(HaveOccurred()) + + r, err := controllers.NewReferenceAddonReconciler( + mgr.GetClient(), + controllers.NewSecretParameterGetter( + mgr.GetClient(), + controllers.WithNamespace(namespace), + controllers.WithName(parameterSecretName), + ), + controllers.WithAddonNamespace(namespace), + controllers.WithAddonParameterSecretName(parameterSecretName), + controllers.WithOperatorName(operatorName), + controllers.WithDeleteLabel(deleteLabel), + ) + Expect(err).ToNot(HaveOccurred()) + + Expect(r.SetupWithManager(mgr)).Should(Succeed()) + + go func() { + defer GinkgoRecover() + + Expect(mgr.Start(ctx)).Should(Succeed()) + }() + + By("Creating the addon namespace") + + ns := addonNamespace(namespace) + + Expect(_client.Create(ctx, &ns)).Should(Succeed()) + EventuallyObjectExists(ctx, &ns) + + DeferCleanup(cancel) + }) + + When("no parameter secret exists", func() { + It("should not create a NetworkPolicy", func() { + secret := addonParameterSecret(parameterSecretName, namespace) + EventuallyObjectDoesNotExist(ctx, &secret) + + np := addonNetworkPolicy(fmt.Sprintf("%s-ingress", operatorName), namespace) + EventuallyObjectDoesNotExist(ctx, &np) + }) + }) + + When("parameter secret exists", func() { + BeforeEach(func() { + By("Creating the parameter Secret") + + secret := addonParameterSecret(parameterSecretName, namespace) + Expect(_client.Create(ctx, &secret)).Should(Succeed()) + + EventuallyObjectExists(ctx, &secret) + + DeferCleanup(func() { + By("Deleting the parameter Secret") + + err := _client.Delete(ctx, &secret) + Expect(client.IgnoreNotFound(err)).ToNot(HaveOccurred()) + }) + }) + + Context("ApplyNetworkPolicies set to 'nil'", func() { + It("should not create a NetworkPolicy", func() { + np := addonNetworkPolicy(fmt.Sprintf("%s-ingress", operatorName), namespace) + EventuallyObjectDoesNotExist(ctx, &np) + }) + }) + + Context("ApplyNetworkPolicies set to 'false'", func() { + It("should not create a NetworkPolicy", func() { + secret := addonParameterSecret(parameterSecretName, namespace) + secret.Data = map[string][]byte{ + "applynetworkpolicies": []byte("false"), + } + Expect(_client.Update(ctx, &secret)).Should(Succeed()) + + np := addonNetworkPolicy(fmt.Sprintf("%s-ingress", operatorName), namespace) + EventuallyObjectDoesNotExist(ctx, &np) + }) + }) + + Context("ApplyNetworkPolicies set to 'true'", func() { + It("should create a NetworkPolicy", func() { + secret := addonParameterSecret(parameterSecretName, namespace) + secret.Data = map[string][]byte{ + "applynetworkpolicies": []byte("true"), + } + Expect(_client.Update(ctx, &secret)).Should(Succeed()) + + np := addonNetworkPolicy(fmt.Sprintf("%s-ingress", operatorName), namespace) + EventuallyObjectExists(ctx, &np) + }) + }) + }) +}) + +func addonParameterSecret(name, ns string) corev1.Secret { + return corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + }, + } +} + +func addonNetworkPolicy(name, ns string) netv1.NetworkPolicy { + return netv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + }, + Spec: netv1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{}, + PolicyTypes: []netv1.PolicyType{ + netv1.PolicyTypeIngress, + }, + }, + } +} diff --git a/integration/controllers/suite_test.go b/integration/controllers/suite_test.go new file mode 100644 index 00000000..714c8eb0 --- /dev/null +++ b/integration/controllers/suite_test.go @@ -0,0 +1,85 @@ +package controllers + +import ( + "context" + "testing" + + refapis "github.com/openshift/reference-addon/apis" + olmcrds "github.com/operator-framework/api/crds" + opsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" + v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apimachinery/pkg/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" +) + +var ( + _cfg *rest.Config + _client client.Client + _testEnv *envtest.Environment + _ctx context.Context + _cancel context.CancelFunc + _scheme = runtime.NewScheme() +) + +func TestSuite(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "controllers suite") +} + +var _ = BeforeSuite(func() { + _ctx, _cancel = context.WithCancel(context.Background()) + + By("Registering schemes") + + Expect(clientgoscheme.AddToScheme(_scheme)).Should(Succeed()) + Expect(opsv1alpha1.AddToScheme(_scheme)).Should(Succeed()) + Expect(refapis.AddToScheme(_scheme)).Should(Succeed()) + + By("Starting test environment") + + _testEnv = &envtest.Environment{ + Scheme: _scheme, + } + + var err error + + _cfg, err = _testEnv.Start() + Expect(err).ToNot(HaveOccurred()) + Expect(_cfg).ToNot(BeNil()) + + DeferCleanup(cleanup) + + By("Installing CRD's") + + _, err = envtest.InstallCRDs(_cfg, envtest.CRDInstallOptions{ + CRDs: []v1.CustomResourceDefinition{ + *olmcrds.ClusterServiceVersion(), + }, + Paths: []string{ + "../../config/deploy/reference.addons.managed.openshift.io_referenceaddons.yaml", + }, + Scheme: _scheme, + }) + Expect(err).ToNot(HaveOccurred()) + + By("Initializing k8s client") + + _client, err = client.New(_cfg, client.Options{ + Scheme: _scheme, + }) + Expect(err).ToNot(HaveOccurred()) +}) + +func cleanup() { + _cancel() + + By("Stopping the test environment") + + Expect(_testEnv.Stop()).Should(Succeed()) +} diff --git a/integration/controllers/uninstall_test.go b/integration/controllers/uninstall_test.go new file mode 100644 index 00000000..0c10a071 --- /dev/null +++ b/integration/controllers/uninstall_test.go @@ -0,0 +1,205 @@ +package controllers + +import ( + "context" + "fmt" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/openshift/reference-addon/internal/controllers" + opsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +var _ = Describe("Uninstall Phase", Ordered, func() { + var ( + ctx context.Context + cancel context.CancelFunc + deleteLabel string + deleteLabelGen = nameGenerator("uninstall-test-label") + namespace string + namespaceGen = nameGenerator("uninstall-test-namespace") + operatorName string + operatorNameGen = nameGenerator("uninstall-test-operator") + parameterSecretName string + parameterSecretNameGen = nameGenerator("uninstall-test-secret") + ) + + BeforeEach(func() { + ctx, cancel = context.WithCancel(context.Background()) + + deleteLabel = deleteLabelGen() + namespace = namespaceGen() + operatorName = operatorNameGen() + parameterSecretName = parameterSecretNameGen() + + By("Starting manager with controllers") + + mgr, err := ctrl.NewManager(_cfg, ctrl.Options{ + Scheme: _scheme, + }) + Expect(err).ToNot(HaveOccurred()) + + r, err := controllers.NewReferenceAddonReconciler( + mgr.GetClient(), + controllers.NewSecretParameterGetter( + mgr.GetClient(), + controllers.WithNamespace(namespace), + controllers.WithName(parameterSecretName), + ), + controllers.WithAddonNamespace(namespace), + controllers.WithAddonParameterSecretName(parameterSecretName), + controllers.WithOperatorName(operatorName), + controllers.WithDeleteLabel(deleteLabel), + ) + Expect(err).ToNot(HaveOccurred()) + + Expect(r.SetupWithManager(mgr)).Should(Succeed()) + + go func() { + defer GinkgoRecover() + + Expect(mgr.Start(ctx)).Should(Succeed()) + }() + + By("Creating the addon namespace") + + ns := addonNamespace(namespace) + + Expect(_client.Create(ctx, &ns)).Should(Succeed()) + EventuallyObjectExists(ctx, &ns) + + By("Ensuring the addon CSV exists") + + csv := addonCSV(operatorName, namespace) + + Expect(_client.Create(ctx, &csv)).Should(Succeed()) + + DeferCleanup(func() { + defer cancel() + + By("Ensuring the addon CSV does not exist") + + err := _client.Delete(ctx, &csv) + Expect(client.IgnoreNotFound(err)).ToNot(HaveOccurred()) + }) + }) + + When("no uninstall ConfigMap exists", func() { + It("should not remove the addon CSV", func() { + cm := deleteConfigMap(operatorName, namespace) + EventuallyObjectDoesNotExist(ctx, &cm) + + csv := addonCSV(operatorName, namespace) + EventuallyObjectExists(ctx, &csv) + }) + }) + + When("uninstall ConfigMap exists", func() { + BeforeEach(func() { + By("Creating the uninstall ConfigMap") + + cm := deleteConfigMap(operatorName, namespace) + Expect(_client.Create(ctx, &cm)).Should(Succeed()) + + EventuallyObjectExists(ctx, &cm) + + DeferCleanup(func() { + By("Deleting the uninstall ConfigMap") + + err := _client.Delete(ctx, &cm) + Expect(client.IgnoreNotFound(err)).ToNot(HaveOccurred()) + }) + }) + + Context("without a delete label", func() { + It("should not remove the addon CSV", func() { + csv := addonCSV(operatorName, namespace) + EventuallyObjectExists(ctx, &csv) + }) + }) + + Context("with a delete label", func() { + It("should remove the addon CSV", func() { + updatedCM := deleteConfigMapWithLabel(operatorName, namespace, deleteLabel) + Expect(_client.Update(ctx, &updatedCM)).Should(Succeed()) + + csv := addonCSV(operatorName, namespace) + EventuallyObjectDoesNotExist(ctx, &csv) + }) + }) + }) +}) + +func EventuallyObjectExists(ctx context.Context, obj client.Object) bool { + get := func() error { + return _client.Get(ctx, client.ObjectKeyFromObject(obj), obj) + } + + return Eventually(get).Should(Succeed()) +} + +func EventuallyObjectDoesNotExist(ctx context.Context, obj client.Object) bool { + get := func() error { + return _client.Get(ctx, client.ObjectKeyFromObject(obj), obj) + } + + return EventuallyWithOffset(1, get).ShouldNot(Succeed()) +} + +func addonNamespace(name string) corev1.Namespace { + return corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + } +} + +func addonCSV(name, ns string) opsv1alpha1.ClusterServiceVersion { + return opsv1alpha1.ClusterServiceVersion{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + }, + Spec: opsv1alpha1.ClusterServiceVersionSpec{ + InstallStrategy: opsv1alpha1.NamedInstallStrategy{ + StrategySpec: opsv1alpha1.StrategyDetailsDeployment{ + DeploymentSpecs: []opsv1alpha1.StrategyDeploymentSpec{}, + }, + }, + }, + } +} + +func deleteConfigMapWithLabel(name, ns, label string) corev1.ConfigMap { + cm := deleteConfigMap(name, ns) + cm.Labels = map[string]string{ + label: "true", + } + + return cm +} + +func deleteConfigMap(name, ns string) corev1.ConfigMap { + return corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + }, + } +} + +func nameGenerator(pfx string) func() string { + i := 0 + + return func() string { + name := fmt.Sprintf("%s-%d", pfx, i) + + i++ + + return name + } +}