sigs.k8s.io/cluster-api@v1.7.1/cmd/clusterctl/client/cluster/inventory_test.go (about)

     1  /*
     2  Copyright 2019 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package cluster
    18  
    19  import (
    20  	"context"
    21  	"testing"
    22  	"time"
    23  
    24  	. "github.com/onsi/gomega"
    25  	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	"k8s.io/apimachinery/pkg/util/wait"
    28  	"sigs.k8s.io/controller-runtime/pkg/client"
    29  
    30  	clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
    31  	"sigs.k8s.io/cluster-api/cmd/clusterctl/internal/test"
    32  )
    33  
    34  func fakePollImmediateWaiter(_ context.Context, _, _ time.Duration, _ wait.ConditionWithContextFunc) error {
    35  	return nil
    36  }
    37  
    38  func Test_inventoryClient_CheckInventoryCRDs(t *testing.T) {
    39  	type fields struct {
    40  		alreadyHasCRD bool
    41  	}
    42  	tests := []struct {
    43  		name    string
    44  		fields  fields
    45  		want    bool
    46  		wantErr bool
    47  	}{
    48  		{
    49  			name: "Has not CRD",
    50  			fields: fields{
    51  				alreadyHasCRD: false,
    52  			},
    53  			want:    false,
    54  			wantErr: false,
    55  		},
    56  		{
    57  			name: "Already has CRD",
    58  			fields: fields{
    59  				alreadyHasCRD: true,
    60  			},
    61  			want:    true,
    62  			wantErr: false,
    63  		},
    64  	}
    65  	for _, tt := range tests {
    66  		t.Run(tt.name, func(t *testing.T) {
    67  			g := NewWithT(t)
    68  
    69  			ctx := context.Background()
    70  
    71  			proxy := test.NewFakeProxy()
    72  			p := newInventoryClient(proxy, fakePollImmediateWaiter)
    73  			if tt.fields.alreadyHasCRD {
    74  				// forcing creation of metadata before test
    75  				g.Expect(p.EnsureCustomResourceDefinitions(ctx)).To(Succeed())
    76  			}
    77  
    78  			res, err := checkInventoryCRDs(ctx, proxy)
    79  			g.Expect(res).To(Equal(tt.want))
    80  			if tt.wantErr {
    81  				g.Expect(err).To(HaveOccurred())
    82  			} else {
    83  				g.Expect(err).ToNot(HaveOccurred())
    84  			}
    85  		})
    86  	}
    87  }
    88  
    89  var fooProvider = clusterctlv1.Provider{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "ns1", ResourceVersion: "999"}}
    90  var v1alpha4Contract = "v1alpha4"
    91  
    92  func Test_inventoryClient_List(t *testing.T) {
    93  	type fields struct {
    94  		initObjs []client.Object
    95  	}
    96  	tests := []struct {
    97  		name    string
    98  		fields  fields
    99  		want    []clusterctlv1.Provider
   100  		wantErr bool
   101  	}{
   102  		{
   103  			name: "Get list",
   104  			fields: fields{
   105  				initObjs: []client.Object{
   106  					&fooProvider,
   107  				},
   108  			},
   109  			want: []clusterctlv1.Provider{
   110  				fooProvider,
   111  			},
   112  			wantErr: false,
   113  		},
   114  	}
   115  	for _, tt := range tests {
   116  		t.Run(tt.name, func(t *testing.T) {
   117  			g := NewWithT(t)
   118  
   119  			p := newInventoryClient(test.NewFakeProxy().WithObjs(tt.fields.initObjs...), fakePollImmediateWaiter)
   120  			got, err := p.List(context.Background())
   121  			if tt.wantErr {
   122  				g.Expect(err).To(HaveOccurred())
   123  				return
   124  			}
   125  
   126  			g.Expect(err).ToNot(HaveOccurred())
   127  			g.Expect(got.Items).To(ConsistOf(tt.want))
   128  		})
   129  	}
   130  }
   131  
   132  func Test_inventoryClient_Create(t *testing.T) {
   133  	type fields struct {
   134  		proxy Proxy
   135  	}
   136  	type args struct {
   137  		m clusterctlv1.Provider
   138  	}
   139  	providerV2 := fakeProvider("infra", clusterctlv1.InfrastructureProviderType, "v0.2.0", "")
   140  	// since this test object is used in a Create request, wherein setting ResourceVersion should no be set
   141  	providerV2.ResourceVersion = ""
   142  	providerV3 := fakeProvider("infra", clusterctlv1.InfrastructureProviderType, "v0.3.0", "")
   143  
   144  	tests := []struct {
   145  		name          string
   146  		fields        fields
   147  		args          args
   148  		wantProviders []clusterctlv1.Provider
   149  		wantErr       bool
   150  	}{
   151  		{
   152  			name: "Creates a provider",
   153  			fields: fields{
   154  				proxy: test.NewFakeProxy(),
   155  			},
   156  			args: args{
   157  				m: providerV2,
   158  			},
   159  			wantProviders: []clusterctlv1.Provider{
   160  				providerV2,
   161  			},
   162  			wantErr: false,
   163  		},
   164  		{
   165  			name: "Patches a provider",
   166  			fields: fields{
   167  				proxy: test.NewFakeProxy().WithObjs(&providerV2),
   168  			},
   169  			args: args{
   170  				m: providerV3,
   171  			},
   172  			wantProviders: []clusterctlv1.Provider{
   173  				providerV3,
   174  			},
   175  			wantErr: false,
   176  		},
   177  	}
   178  	for _, tt := range tests {
   179  		t.Run(tt.name, func(t *testing.T) {
   180  			g := NewWithT(t)
   181  
   182  			ctx := context.Background()
   183  
   184  			p := &inventoryClient{
   185  				proxy: tt.fields.proxy,
   186  			}
   187  			err := p.Create(ctx, tt.args.m)
   188  			if tt.wantErr {
   189  				g.Expect(err).To(HaveOccurred())
   190  				return
   191  			}
   192  
   193  			g.Expect(err).ToNot(HaveOccurred())
   194  
   195  			got, err := p.List(ctx)
   196  			if tt.wantErr {
   197  				g.Expect(err).To(HaveOccurred())
   198  				return
   199  			}
   200  
   201  			g.Expect(err).ToNot(HaveOccurred())
   202  
   203  			for i := range got.Items {
   204  				tt.wantProviders[i].ResourceVersion = got.Items[i].ResourceVersion
   205  			}
   206  
   207  			g.Expect(got.Items).To(ConsistOf(tt.wantProviders))
   208  		})
   209  	}
   210  }
   211  
   212  func Test_CheckCAPIContract(t *testing.T) {
   213  	type args struct {
   214  		options []CheckCAPIContractOption
   215  	}
   216  	type fields struct {
   217  		proxy Proxy
   218  	}
   219  
   220  	tests := []struct {
   221  		name    string
   222  		fields  fields
   223  		args    args
   224  		wantErr bool
   225  	}{
   226  		{
   227  			name: "Fails if Cluster API is not installed",
   228  			fields: fields{
   229  				proxy: test.NewFakeProxy().WithObjs(),
   230  			},
   231  			args:    args{},
   232  			wantErr: true,
   233  		},
   234  		{
   235  			name: "Pass if Cluster API is not installed, but this is explicitly tolerated",
   236  			fields: fields{
   237  				proxy: test.NewFakeProxy().WithObjs(),
   238  			},
   239  			args: args{
   240  				options: []CheckCAPIContractOption{AllowCAPINotInstalled{}},
   241  			},
   242  			wantErr: false,
   243  		},
   244  		{
   245  			name: "Pass when Cluster API with current contract is installed",
   246  			fields: fields{
   247  				proxy: test.NewFakeProxy().WithObjs(&apiextensionsv1.CustomResourceDefinition{
   248  					ObjectMeta: metav1.ObjectMeta{Name: "clusters.cluster.x-k8s.io"},
   249  					Spec: apiextensionsv1.CustomResourceDefinitionSpec{
   250  						Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
   251  							{
   252  								Name: test.PreviousCAPIContractNotSupported,
   253  							},
   254  							{
   255  								Name:    test.CurrentCAPIContract,
   256  								Storage: true,
   257  							},
   258  						},
   259  					},
   260  				}),
   261  			},
   262  			args:    args{},
   263  			wantErr: false,
   264  		},
   265  		{
   266  			name: "Fails when Cluster API with previous contract is installed",
   267  			fields: fields{
   268  				proxy: test.NewFakeProxy().WithObjs(&apiextensionsv1.CustomResourceDefinition{
   269  					ObjectMeta: metav1.ObjectMeta{Name: "clusters.cluster.x-k8s.io"},
   270  					Spec: apiextensionsv1.CustomResourceDefinitionSpec{
   271  						Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
   272  							{
   273  								Name:    test.PreviousCAPIContractNotSupported,
   274  								Storage: true,
   275  							},
   276  							{
   277  								Name: test.CurrentCAPIContract,
   278  							},
   279  						},
   280  					},
   281  				}),
   282  			},
   283  			args:    args{},
   284  			wantErr: true,
   285  		},
   286  		{
   287  			name: "Pass when Cluster API with previous contract is installed, but this is explicitly tolerated",
   288  			fields: fields{
   289  				proxy: test.NewFakeProxy().WithObjs(&apiextensionsv1.CustomResourceDefinition{
   290  					ObjectMeta: metav1.ObjectMeta{Name: "clusters.cluster.x-k8s.io"},
   291  					Spec: apiextensionsv1.CustomResourceDefinitionSpec{
   292  						Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
   293  							{
   294  								Name:    test.PreviousCAPIContractNotSupported,
   295  								Storage: true,
   296  							},
   297  							{
   298  								Name: test.CurrentCAPIContract,
   299  							},
   300  						},
   301  					},
   302  				}),
   303  			},
   304  			args: args{
   305  				options: []CheckCAPIContractOption{AllowCAPIContract{Contract: v1alpha4Contract}, AllowCAPIContract{Contract: test.PreviousCAPIContractNotSupported}},
   306  			},
   307  			wantErr: false,
   308  		},
   309  		{
   310  			name: "Fails when Cluster API with next contract is installed",
   311  			fields: fields{
   312  				proxy: test.NewFakeProxy().WithObjs(&apiextensionsv1.CustomResourceDefinition{
   313  					ObjectMeta: metav1.ObjectMeta{Name: "clusters.cluster.x-k8s.io"},
   314  					Spec: apiextensionsv1.CustomResourceDefinitionSpec{
   315  						Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
   316  							{
   317  								Name: test.CurrentCAPIContract,
   318  							},
   319  							{
   320  								Name:    test.NextCAPIContractNotSupported,
   321  								Storage: true,
   322  							},
   323  						},
   324  					},
   325  				}),
   326  			},
   327  			args:    args{},
   328  			wantErr: true,
   329  		},
   330  	}
   331  	for _, tt := range tests {
   332  		t.Run(tt.name, func(t *testing.T) {
   333  			g := NewWithT(t)
   334  
   335  			p := &inventoryClient{
   336  				proxy: tt.fields.proxy,
   337  			}
   338  			err := p.CheckCAPIContract(context.Background(), tt.args.options...)
   339  			if tt.wantErr {
   340  				g.Expect(err).To(HaveOccurred())
   341  				return
   342  			}
   343  			g.Expect(err).ToNot(HaveOccurred())
   344  		})
   345  	}
   346  }
   347  
   348  func Test_inventoryClient_CheckSingleProviderInstance(t *testing.T) {
   349  	type fields struct {
   350  		initObjs []client.Object
   351  	}
   352  	tests := []struct {
   353  		name    string
   354  		fields  fields
   355  		wantErr bool
   356  	}{
   357  		{
   358  			name: "Returns error when there are multiple instances of the same provider",
   359  			fields: fields{
   360  				initObjs: []client.Object{
   361  					&clusterctlv1.Provider{Type: string(clusterctlv1.CoreProviderType), ProviderName: "foo", ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "ns1"}},
   362  					&clusterctlv1.Provider{Type: string(clusterctlv1.CoreProviderType), ProviderName: "foo", ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "ns2"}},
   363  					&clusterctlv1.Provider{Type: string(clusterctlv1.InfrastructureProviderType), ProviderName: "bar", ObjectMeta: metav1.ObjectMeta{Name: "bar", Namespace: "ns2"}},
   364  				},
   365  			},
   366  			wantErr: true,
   367  		},
   368  		{
   369  			name: "Does not return error when there is only single instance of all providers",
   370  			fields: fields{
   371  				initObjs: []client.Object{
   372  					&clusterctlv1.Provider{Type: string(clusterctlv1.CoreProviderType), ProviderName: "foo", ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "ns1"}},
   373  					&clusterctlv1.Provider{Type: string(clusterctlv1.CoreProviderType), ProviderName: "foo-1", ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "ns2"}},
   374  					&clusterctlv1.Provider{Type: string(clusterctlv1.InfrastructureProviderType), ProviderName: "bar", ObjectMeta: metav1.ObjectMeta{Name: "bar", Namespace: "ns2"}},
   375  				},
   376  			},
   377  			wantErr: false,
   378  		},
   379  	}
   380  	for _, tt := range tests {
   381  		t.Run(tt.name, func(t *testing.T) {
   382  			g := NewWithT(t)
   383  
   384  			p := newInventoryClient(test.NewFakeProxy().WithObjs(tt.fields.initObjs...), fakePollImmediateWaiter)
   385  			err := p.CheckSingleProviderInstance(context.Background())
   386  			if tt.wantErr {
   387  				g.Expect(err).To(HaveOccurred())
   388  				return
   389  			}
   390  
   391  			g.Expect(err).ToNot(HaveOccurred())
   392  		})
   393  	}
   394  }