@@ -65,6 +65,7 @@ import (
6565 "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/queueinformer"
6666 "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/scoped"
6767 opregistry "github.com/operator-framework/operator-registry/pkg/registry"
68+ clienttesting "k8s.io/client-go/testing"
6869)
6970
7071type TestStrategy struct {}
@@ -166,6 +167,7 @@ type fakeOperatorConfig struct {
166167 k8sObjs []runtime.Object
167168 extObjs []runtime.Object
168169 regObjs []runtime.Object
170+ actionLog * []clienttesting.Action
169171}
170172
171173// fakeOperatorOption applies an option to the given fake operator configuration.
@@ -229,6 +231,18 @@ func withRegObjs(regObjs ...runtime.Object) fakeOperatorOption {
229231 }
230232}
231233
234+ func withActionLog (log * []clienttesting.Action ) fakeOperatorOption {
235+ return func (config * fakeOperatorConfig ) {
236+ config .actionLog = log
237+ }
238+ }
239+
240+ func withLogger (logger * logrus.Logger ) fakeOperatorOption {
241+ return func (config * fakeOperatorConfig ) {
242+ config .logger = logger
243+ }
244+ }
245+
232246// NewFakeOperator creates and starts a new operator using fake clients.
233247func NewFakeOperator (ctx context.Context , options ... fakeOperatorOption ) (* Operator , error ) {
234248 // Apply options to default config
@@ -247,6 +261,7 @@ func NewFakeOperator(ctx context.Context, options ...fakeOperatorOption) (*Opera
247261 recorder : & record.FakeRecorder {},
248262 // default expected namespaces
249263 namespaces : []string {"default" , "kube-system" , "kube-public" },
264+ actionLog : & []clienttesting.Action {},
250265 }
251266 for _ , option := range options {
252267 option (config )
@@ -258,6 +273,10 @@ func NewFakeOperator(ctx context.Context, options ...fakeOperatorOption) (*Opera
258273 // For now, directly use a SimpleClientset instead.
259274 k8sClientFake := k8sfake .NewSimpleClientset (config .k8sObjs ... )
260275 k8sClientFake .Resources = apiResourcesForObjects (append (config .extObjs , config .regObjs ... ))
276+ k8sClientFake .PrependReactor ("*" , "*" , clienttesting .ReactionFunc (func (action clienttesting.Action ) (bool , runtime.Object , error ) {
277+ * config .actionLog = append (* config .actionLog , action )
278+ return false , nil , nil
279+ }))
261280 config .operatorClient = operatorclient .NewClient (k8sClientFake , apiextensionsfake .NewSimpleClientset (config .extObjs ... ), apiregistrationfake .NewSimpleClientset (config .regObjs ... ))
262281 config .configClient = configfake .NewSimpleClientset ()
263282
@@ -3936,6 +3955,171 @@ func TestUpdates(t *testing.T) {
39363955 }
39373956}
39383957
3958+ type tDotLogWriter struct {
3959+ * testing.T
3960+ }
3961+
3962+ func (w tDotLogWriter ) Write (p []byte ) (int , error ) {
3963+ w .T .Logf ("%s" , string (p ))
3964+ return len (p ), nil
3965+ }
3966+
3967+ func testLogrusLogger (t * testing.T ) * logrus.Logger {
3968+ l := logrus .New ()
3969+ l .SetOutput (tDotLogWriter {t })
3970+ return l
3971+ }
3972+
3973+ func TestSyncNamespace (t * testing.T ) {
3974+ namespace := func (name string , labels map [string ]string ) corev1.Namespace {
3975+ return corev1.Namespace {
3976+ ObjectMeta : metav1.ObjectMeta {
3977+ Name : name ,
3978+ Labels : labels ,
3979+ },
3980+ }
3981+ }
3982+
3983+ operatorgroup := func (name string , targets []string ) operatorsv1.OperatorGroup {
3984+ return operatorsv1.OperatorGroup {
3985+ ObjectMeta : metav1.ObjectMeta {
3986+ Name : name ,
3987+ UID : types .UID (fmt .Sprintf ("%s-uid" , name )),
3988+ },
3989+ Status : operatorsv1.OperatorGroupStatus {
3990+ Namespaces : targets ,
3991+ },
3992+ }
3993+ }
3994+
3995+ for _ , tc := range []struct {
3996+ name string
3997+ before corev1.Namespace
3998+ operatorgroups []operatorsv1.OperatorGroup
3999+ noop bool
4000+ expected []string
4001+ }{
4002+ {
4003+ name : "adds missing labels" ,
4004+ before : namespace ("test-namespace" , map [string ]string {"unrelated" : "" }),
4005+ operatorgroups : []operatorsv1.OperatorGroup {
4006+ operatorgroup ("test-group-1" , []string {"test-namespace" }),
4007+ operatorgroup ("test-group-2" , []string {"test-namespace" }),
4008+ },
4009+ expected : []string {
4010+ "olm.operatorgroup.uid/test-group-1-uid" ,
4011+ "olm.operatorgroup.uid/test-group-2-uid" ,
4012+ "unrelated" ,
4013+ },
4014+ },
4015+ {
4016+ name : "removes stale labels" ,
4017+ before : namespace ("test-namespace" , map [string ]string {
4018+ "olm.operatorgroup.uid/test-group-1-uid" : "" ,
4019+ "olm.operatorgroup.uid/test-group-2-uid" : "" ,
4020+ }),
4021+ operatorgroups : []operatorsv1.OperatorGroup {
4022+ operatorgroup ("test-group-2" , []string {"test-namespace" }),
4023+ },
4024+ expected : []string {
4025+ "olm.operatorgroup.uid/test-group-2-uid" ,
4026+ },
4027+ },
4028+ {
4029+ name : "does not add label if namespace is not a target namespace" ,
4030+ before : namespace ("test-namespace" , nil ),
4031+ operatorgroups : []operatorsv1.OperatorGroup {
4032+ operatorgroup ("test-group-1" , []string {"test-namespace" }),
4033+ operatorgroup ("test-group-2" , []string {"not-test-namespace" }),
4034+ },
4035+ expected : []string {
4036+ "olm.operatorgroup.uid/test-group-1-uid" ,
4037+ },
4038+ },
4039+ {
4040+ name : "no update if labels are in sync" ,
4041+ before : namespace ("test-namespace" , map [string ]string {
4042+ "olm.operatorgroup.uid/test-group-1-uid" : "" ,
4043+ "olm.operatorgroup.uid/test-group-2-uid" : "" ,
4044+ }),
4045+ operatorgroups : []operatorsv1.OperatorGroup {
4046+ operatorgroup ("test-group-1" , []string {"test-namespace" }),
4047+ operatorgroup ("test-group-2" , []string {"test-namespace" }),
4048+ },
4049+ noop : true ,
4050+ expected : []string {
4051+ "olm.operatorgroup.uid/test-group-1-uid" ,
4052+ "olm.operatorgroup.uid/test-group-2-uid" ,
4053+ },
4054+ },
4055+ } {
4056+ t .Run (tc .name , func (t * testing.T ) {
4057+ ctx , cancel := context .WithCancel (context .Background ())
4058+ defer cancel ()
4059+
4060+ var ogs []runtime.Object
4061+ for i := range tc .operatorgroups {
4062+ ogs = append (ogs , & tc .operatorgroups [i ])
4063+ }
4064+
4065+ var actions []clienttesting.Action
4066+
4067+ o , err := NewFakeOperator (
4068+ ctx ,
4069+ withClientObjs (ogs ... ),
4070+ withK8sObjs (& tc .before ),
4071+ withActionLog (& actions ),
4072+ withLogger (testLogrusLogger (t )),
4073+ )
4074+ if err != nil {
4075+ t .Fatalf ("setup failed: %v" , err )
4076+ }
4077+
4078+ actions = actions [:0 ]
4079+
4080+ err = o .syncNamespace (& tc .before )
4081+ if err != nil {
4082+ t .Fatalf ("unexpected error: %v" , err )
4083+ }
4084+
4085+ if tc .noop {
4086+ for _ , action := range actions {
4087+ if action .GetResource ().Resource != "namespaces" {
4088+ continue
4089+ }
4090+ if namer , ok := action .(interface { GetName () string }); ok {
4091+ if namer .GetName () != tc .before .Name {
4092+ continue
4093+ }
4094+ } else if objer , ok := action .(interface { GetObject () runtime.Object }); ok {
4095+ if namer , ok := objer .GetObject ().(interface { GetName () string }); ok {
4096+ if namer .GetName () != tc .before .Name {
4097+ continue
4098+ }
4099+ }
4100+ }
4101+ t .Errorf ("unexpected client operation: %v" , action )
4102+ }
4103+ }
4104+
4105+ after , err := o .opClient .KubernetesInterface ().CoreV1 ().Namespaces ().Get (ctx , tc .before .Name , metav1.GetOptions {})
4106+ if err != nil {
4107+ t .Fatalf ("unexpected error: %v" , err )
4108+ }
4109+
4110+ if len (after .Labels ) != len (tc .expected ) {
4111+ t .Errorf ("expected %d labels, got %d" , len (tc .expected ), len (after .Labels ))
4112+ }
4113+
4114+ for _ , l := range tc .expected {
4115+ if _ , ok := after .Labels [l ]; ! ok {
4116+ t .Errorf ("missing expected label %q" , l )
4117+ }
4118+ }
4119+ })
4120+ }
4121+ }
4122+
39394123func TestSyncOperatorGroups (t * testing.T ) {
39404124 logrus .SetLevel (logrus .WarnLevel )
39414125 clockFake := utilclocktesting .NewFakeClock (time .Date (2006 , time .January , 2 , 15 , 4 , 5 , 0 , time .FixedZone ("MST" , - 7 * 3600 )))
0 commit comments