github.com/operator-framework/operator-lifecycle-manager@v0.30.0/pkg/controller/operators/olm/operatorgroup_test.go (about)

     1  package olm
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  
     7  	"github.com/google/go-cmp/cmp"
     8  	"github.com/sirupsen/logrus/hooks/test"
     9  	"github.com/stretchr/testify/assert"
    10  	"github.com/stretchr/testify/require"
    11  	"k8s.io/client-go/metadata/metadatalister"
    12  
    13  	"k8s.io/apimachinery/pkg/api/errors"
    14  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    15  	"k8s.io/apimachinery/pkg/labels"
    16  	ktesting "k8s.io/client-go/testing"
    17  
    18  	"github.com/operator-framework/api/pkg/operators/v1alpha1"
    19  	"github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned/fake"
    20  )
    21  
    22  func TestCopyToNamespace(t *testing.T) {
    23  	gvr := v1alpha1.SchemeGroupVersion.WithResource("clusterserviceversions")
    24  
    25  	for _, tc := range []struct {
    26  		Name            string
    27  		FromNamespace   string
    28  		ToNamespace     string
    29  		Hash            string
    30  		StatusHash      string
    31  		Prototype       v1alpha1.ClusterServiceVersion
    32  		ExistingCopy    *metav1.PartialObjectMetadata
    33  		ExpectedResult  *v1alpha1.ClusterServiceVersion
    34  		ExpectedError   error
    35  		ExpectedActions []ktesting.Action
    36  	}{
    37  		{
    38  			Name:          "copy to original namespace returns error",
    39  			FromNamespace: "samesies",
    40  			ToNamespace:   "samesies",
    41  			ExpectedError: fmt.Errorf("bug: can not copy to active namespace samesies"),
    42  		},
    43  		{
    44  			Name:          "copy created if does not exist",
    45  			FromNamespace: "from",
    46  			ToNamespace:   "to",
    47  			Prototype: v1alpha1.ClusterServiceVersion{
    48  				ObjectMeta: metav1.ObjectMeta{
    49  					Name: "name",
    50  				},
    51  				Spec: v1alpha1.ClusterServiceVersionSpec{
    52  					Replaces: "replacee",
    53  				},
    54  				Status: v1alpha1.ClusterServiceVersionStatus{
    55  					Phase: "waxing gibbous",
    56  				},
    57  			},
    58  			ExpectedActions: []ktesting.Action{
    59  				ktesting.NewCreateAction(gvr, "to", &v1alpha1.ClusterServiceVersion{
    60  					ObjectMeta: metav1.ObjectMeta{
    61  						Name:      "name",
    62  						Namespace: "to",
    63  					},
    64  					Spec: v1alpha1.ClusterServiceVersionSpec{
    65  						Replaces: "replacee",
    66  					},
    67  					Status: v1alpha1.ClusterServiceVersionStatus{
    68  						Phase: "waxing gibbous",
    69  					},
    70  				}),
    71  				ktesting.NewUpdateSubresourceAction(gvr, "status", "to", &v1alpha1.ClusterServiceVersion{
    72  					ObjectMeta: metav1.ObjectMeta{
    73  						Name:      "name",
    74  						Namespace: "to",
    75  					},
    76  					Spec: v1alpha1.ClusterServiceVersionSpec{
    77  						Replaces: "replacee",
    78  					},
    79  					Status: v1alpha1.ClusterServiceVersionStatus{
    80  						Phase: "waxing gibbous",
    81  					},
    82  				}),
    83  			},
    84  			ExpectedResult: &v1alpha1.ClusterServiceVersion{
    85  				ObjectMeta: metav1.ObjectMeta{
    86  					Name:      "name",
    87  					Namespace: "to",
    88  				},
    89  			},
    90  		},
    91  		{
    92  			Name:          "copy updated if hash differs",
    93  			FromNamespace: "from",
    94  			ToNamespace:   "to",
    95  			Hash:          "hn-1",
    96  			StatusHash:    "hs",
    97  			Prototype: v1alpha1.ClusterServiceVersion{
    98  				ObjectMeta: metav1.ObjectMeta{
    99  					Name: "name",
   100  				},
   101  				Spec: v1alpha1.ClusterServiceVersionSpec{
   102  					Replaces: "replacee",
   103  				},
   104  				Status: v1alpha1.ClusterServiceVersionStatus{
   105  					Phase: "waxing gibbous",
   106  				},
   107  			},
   108  			ExistingCopy: &metav1.PartialObjectMetadata{
   109  				ObjectMeta: metav1.ObjectMeta{
   110  					Name:            "name",
   111  					Namespace:       "to",
   112  					UID:             "uid",
   113  					ResourceVersion: "42",
   114  					Annotations: map[string]string{
   115  						"$copyhash-nonstatus": "hn-2",
   116  						"$copyhash-status":    "hs",
   117  					},
   118  				},
   119  			},
   120  			ExpectedResult: &v1alpha1.ClusterServiceVersion{
   121  				ObjectMeta: metav1.ObjectMeta{
   122  					Name:      "name",
   123  					Namespace: "to",
   124  					UID:       "uid",
   125  				},
   126  			},
   127  			ExpectedActions: []ktesting.Action{
   128  				ktesting.NewUpdateAction(gvr, "to", &v1alpha1.ClusterServiceVersion{
   129  					ObjectMeta: metav1.ObjectMeta{
   130  						Name:            "name",
   131  						Namespace:       "to",
   132  						UID:             "uid",
   133  						ResourceVersion: "42",
   134  					},
   135  					Spec: v1alpha1.ClusterServiceVersionSpec{
   136  						Replaces: "replacee",
   137  					},
   138  					Status: v1alpha1.ClusterServiceVersionStatus{
   139  						Phase: "waxing gibbous",
   140  					},
   141  				}),
   142  			},
   143  		},
   144  		{
   145  			Name:          "copy status updated if status hash differs",
   146  			FromNamespace: "from",
   147  			ToNamespace:   "to",
   148  			Hash:          "hn",
   149  			StatusHash:    "hs-1",
   150  			Prototype: v1alpha1.ClusterServiceVersion{
   151  				ObjectMeta: metav1.ObjectMeta{
   152  					Name: "name",
   153  				},
   154  				Spec: v1alpha1.ClusterServiceVersionSpec{
   155  					Replaces: "replacee",
   156  				},
   157  				Status: v1alpha1.ClusterServiceVersionStatus{
   158  					Phase: "waxing gibbous",
   159  				},
   160  			},
   161  			ExistingCopy: &metav1.PartialObjectMetadata{
   162  				ObjectMeta: metav1.ObjectMeta{
   163  					Name:            "name",
   164  					Namespace:       "to",
   165  					UID:             "uid",
   166  					ResourceVersion: "42",
   167  					Annotations: map[string]string{
   168  						"$copyhash-nonstatus": "hn",
   169  						"$copyhash-status":    "hs-2",
   170  					},
   171  				},
   172  			},
   173  			ExpectedResult: &v1alpha1.ClusterServiceVersion{
   174  				ObjectMeta: metav1.ObjectMeta{
   175  					Name:      "name",
   176  					Namespace: "to",
   177  					UID:       "uid",
   178  				},
   179  			},
   180  			ExpectedActions: []ktesting.Action{
   181  				ktesting.NewUpdateSubresourceAction(gvr, "status", "to", &v1alpha1.ClusterServiceVersion{
   182  					ObjectMeta: metav1.ObjectMeta{
   183  						Name:            "name",
   184  						Namespace:       "to",
   185  						UID:             "uid",
   186  						ResourceVersion: "42",
   187  					},
   188  					Spec: v1alpha1.ClusterServiceVersionSpec{
   189  						Replaces: "replacee",
   190  					},
   191  					Status: v1alpha1.ClusterServiceVersionStatus{
   192  						Phase: "waxing gibbous",
   193  					},
   194  				}),
   195  			},
   196  		},
   197  		{
   198  			Name:          "copy and copy status updated if both hashes differ",
   199  			FromNamespace: "from",
   200  			ToNamespace:   "to",
   201  			Hash:          "hn-1",
   202  			StatusHash:    "hs-1",
   203  			Prototype: v1alpha1.ClusterServiceVersion{
   204  				ObjectMeta: metav1.ObjectMeta{
   205  					Name: "name",
   206  				},
   207  				Spec: v1alpha1.ClusterServiceVersionSpec{
   208  					Replaces: "replacee",
   209  				},
   210  				Status: v1alpha1.ClusterServiceVersionStatus{
   211  					Phase: "waxing gibbous",
   212  				},
   213  			},
   214  			ExistingCopy: &metav1.PartialObjectMetadata{
   215  				ObjectMeta: metav1.ObjectMeta{
   216  					Name:            "name",
   217  					Namespace:       "to",
   218  					UID:             "uid",
   219  					ResourceVersion: "42",
   220  					Annotations: map[string]string{
   221  						"$copyhash-nonstatus": "hn-2",
   222  						"$copyhash-status":    "hs-2",
   223  					},
   224  				},
   225  			},
   226  			ExpectedResult: &v1alpha1.ClusterServiceVersion{
   227  				ObjectMeta: metav1.ObjectMeta{
   228  					Name:      "name",
   229  					Namespace: "to",
   230  					UID:       "uid",
   231  				},
   232  			},
   233  			ExpectedActions: []ktesting.Action{
   234  				ktesting.NewUpdateAction(gvr, "to", &v1alpha1.ClusterServiceVersion{
   235  					ObjectMeta: metav1.ObjectMeta{
   236  						Name:            "name",
   237  						Namespace:       "to",
   238  						UID:             "uid",
   239  						ResourceVersion: "42",
   240  					},
   241  					Spec: v1alpha1.ClusterServiceVersionSpec{
   242  						Replaces: "replacee",
   243  					},
   244  					Status: v1alpha1.ClusterServiceVersionStatus{
   245  						Phase: "waxing gibbous",
   246  					},
   247  				}),
   248  				ktesting.NewUpdateSubresourceAction(gvr, "status", "to", &v1alpha1.ClusterServiceVersion{
   249  					ObjectMeta: metav1.ObjectMeta{
   250  						Name:            "name",
   251  						Namespace:       "to",
   252  						UID:             "uid",
   253  						ResourceVersion: "42",
   254  					},
   255  					Spec: v1alpha1.ClusterServiceVersionSpec{
   256  						Replaces: "replacee",
   257  					},
   258  					Status: v1alpha1.ClusterServiceVersionStatus{
   259  						Phase: "waxing gibbous",
   260  					},
   261  				}),
   262  			},
   263  		},
   264  		{
   265  			Name:          "no action taken if neither hash differs",
   266  			FromNamespace: "from",
   267  			ToNamespace:   "to",
   268  			Hash:          "hn",
   269  			StatusHash:    "hs",
   270  			Prototype: v1alpha1.ClusterServiceVersion{
   271  				ObjectMeta: metav1.ObjectMeta{
   272  					Name: "name",
   273  				},
   274  			},
   275  			ExistingCopy: &metav1.PartialObjectMetadata{
   276  				ObjectMeta: metav1.ObjectMeta{
   277  					Name:      "name",
   278  					Namespace: "to",
   279  					UID:       "uid",
   280  					Annotations: map[string]string{
   281  						"$copyhash-nonstatus": "hn",
   282  						"$copyhash-status":    "hs",
   283  					},
   284  				},
   285  			},
   286  			ExpectedResult: &v1alpha1.ClusterServiceVersion{
   287  				ObjectMeta: metav1.ObjectMeta{
   288  					Name:      "name",
   289  					Namespace: "to",
   290  					UID:       "uid",
   291  				},
   292  			},
   293  		},
   294  	} {
   295  		t.Run(tc.Name, func(t *testing.T) {
   296  			client := fake.NewSimpleClientset()
   297  			var lister metadatalister.Lister
   298  			if tc.ExistingCopy != nil {
   299  				client = fake.NewSimpleClientset(&v1alpha1.ClusterServiceVersion{
   300  					ObjectMeta: tc.ExistingCopy.ObjectMeta,
   301  				})
   302  				lister = FakeClusterServiceVersionLister{tc.ExistingCopy}
   303  			} else {
   304  				lister = FakeClusterServiceVersionLister{{}}
   305  			}
   306  
   307  			logger, _ := test.NewNullLogger()
   308  			o := &Operator{
   309  				copiedCSVLister: lister,
   310  				client:          client,
   311  				logger:          logger,
   312  			}
   313  
   314  			result, err := o.copyToNamespace(tc.Prototype.DeepCopy(), tc.FromNamespace, tc.ToNamespace, tc.Hash, tc.StatusHash)
   315  
   316  			if tc.ExpectedError == nil {
   317  				require.NoError(t, err)
   318  			} else {
   319  				require.EqualError(t, err, tc.ExpectedError.Error())
   320  			}
   321  			if diff := cmp.Diff(tc.ExpectedResult, result); diff != "" {
   322  				t.Errorf("incorrect result: %v", diff)
   323  			}
   324  
   325  			actions := client.Actions()
   326  			if len(actions) == 0 {
   327  				actions = nil
   328  			}
   329  			if diff := cmp.Diff(tc.ExpectedActions, actions); diff != "" {
   330  				t.Errorf("incorrect actions: %v", diff)
   331  			}
   332  		})
   333  	}
   334  }
   335  
   336  type FakeClusterServiceVersionLister []*metav1.PartialObjectMetadata
   337  
   338  func (l FakeClusterServiceVersionLister) List(selector labels.Selector) ([]*metav1.PartialObjectMetadata, error) {
   339  	var result []*metav1.PartialObjectMetadata
   340  	for _, csv := range l {
   341  		if !selector.Matches(labels.Set(csv.GetLabels())) {
   342  			continue
   343  		}
   344  		result = append(result, csv)
   345  	}
   346  	return result, nil
   347  }
   348  
   349  func (l FakeClusterServiceVersionLister) Namespace(namespace string) metadatalister.NamespaceLister {
   350  	var filtered []*metav1.PartialObjectMetadata
   351  	for _, csv := range l {
   352  		if csv.GetNamespace() != namespace {
   353  			continue
   354  		}
   355  		filtered = append(filtered, csv)
   356  	}
   357  	return FakeClusterServiceVersionLister(filtered)
   358  }
   359  
   360  func (l FakeClusterServiceVersionLister) Get(name string) (*metav1.PartialObjectMetadata, error) {
   361  	for _, csv := range l {
   362  		if csv.GetName() == name {
   363  			return csv, nil
   364  		}
   365  	}
   366  	return nil, errors.NewNotFound(v1alpha1.Resource("clusterserviceversion"), name)
   367  }
   368  
   369  var (
   370  	_ metadatalister.Lister          = FakeClusterServiceVersionLister{}
   371  	_ metadatalister.NamespaceLister = FakeClusterServiceVersionLister{}
   372  )
   373  
   374  func TestCSVCopyPrototype(t *testing.T) {
   375  	src := v1alpha1.ClusterServiceVersion{
   376  		ObjectMeta: metav1.ObjectMeta{
   377  			Name:      "name",
   378  			Namespace: "foo",
   379  			Annotations: map[string]string{
   380  				"olm.targetNamespaces":                             "a,b,c",
   381  				"kubectl.kubernetes.io/last-applied-configuration": "{}",
   382  				"preserved": "yes",
   383  			},
   384  			Labels: map[string]string{
   385  				"operators.coreos.com/foo": "",
   386  				"operators.coreos.com/bar": "",
   387  				"untouched":                "fine",
   388  			},
   389  		},
   390  	}
   391  	var dst v1alpha1.ClusterServiceVersion
   392  	csvCopyPrototype(&src, &dst)
   393  	assert.Equal(t, v1alpha1.ClusterServiceVersion{
   394  		ObjectMeta: metav1.ObjectMeta{
   395  			Name: "name",
   396  			Annotations: map[string]string{
   397  				"preserved": "yes",
   398  			},
   399  			Labels: map[string]string{
   400  				"untouched":      "fine",
   401  				"olm.copiedFrom": "foo",
   402  			},
   403  		},
   404  		Status: v1alpha1.ClusterServiceVersionStatus{
   405  			Message: "The operator is running in foo but is managing this namespace",
   406  			Reason:  v1alpha1.CSVReasonCopied,
   407  		},
   408  	}, dst)
   409  }