sigs.k8s.io/cluster-api@v1.7.1/cmd/clusterctl/client/config/providers_client_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 config
    18  
    19  import (
    20  	"fmt"
    21  	"os"
    22  	"sort"
    23  	"testing"
    24  
    25  	. "github.com/onsi/gomega"
    26  	"github.com/onsi/gomega/format"
    27  
    28  	clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
    29  	"sigs.k8s.io/cluster-api/cmd/clusterctl/internal/test"
    30  )
    31  
    32  func Test_providers_List(t *testing.T) {
    33  	reader := test.NewFakeReader()
    34  
    35  	p := &providersClient{
    36  		reader: reader,
    37  	}
    38  
    39  	defaults := p.defaults()
    40  	sort.Slice(defaults, func(i, j int) bool {
    41  		return defaults[i].Less(defaults[j])
    42  	})
    43  
    44  	defaultsAndZZZ := append(defaults, NewProvider("zzz", "https://zzz/infrastructure-components.yaml", "InfrastructureProvider"))
    45  	// AddonProviders are at the end of the list so we want to make sure this InfrastructureProvider is before the AddonProviders.
    46  	sort.Slice(defaultsAndZZZ, func(i, j int) bool {
    47  		return defaultsAndZZZ[i].Less(defaultsAndZZZ[j])
    48  	})
    49  
    50  	defaultsWithOverride := append([]Provider{}, defaults...)
    51  	defaultsWithOverride[0] = NewProvider(defaults[0].Name(), "https://zzz/infrastructure-components.yaml", defaults[0].Type())
    52  
    53  	type fields struct {
    54  		configGetter Reader
    55  	}
    56  	tests := []struct {
    57  		name    string
    58  		fields  fields
    59  		envVars map[string]string
    60  		want    []Provider
    61  		wantErr bool
    62  	}{
    63  		{
    64  			name: "Returns default provider configurations",
    65  			fields: fields{
    66  				configGetter: test.NewFakeReader(),
    67  			},
    68  			want:    defaults,
    69  			wantErr: false,
    70  		},
    71  		{
    72  			name: "Returns user defined provider configurations",
    73  			fields: fields{
    74  				configGetter: test.NewFakeReader().
    75  					WithVar(
    76  						ProvidersConfigKey,
    77  						"- name: \"zzz\"\n"+
    78  							"  url: \"https://zzz/infrastructure-components.yaml\"\n"+
    79  							"  type: \"InfrastructureProvider\"\n",
    80  					),
    81  			},
    82  			want:    defaultsAndZZZ,
    83  			wantErr: false,
    84  		},
    85  		{
    86  			name: "Returns user defined provider configurations with evaluated env vars",
    87  			fields: fields{
    88  				configGetter: test.NewFakeReader().
    89  					WithVar(
    90  						ProvidersConfigKey,
    91  						"- name: \"zzz\"\n"+
    92  							"  url: \"${TEST_REPO_PATH}/infrastructure-components.yaml\"\n"+
    93  							"  type: \"InfrastructureProvider\"\n",
    94  					),
    95  			},
    96  			envVars: map[string]string{
    97  				"TEST_REPO_PATH": "https://zzz",
    98  			},
    99  			want:    defaultsAndZZZ,
   100  			wantErr: false,
   101  		},
   102  		{
   103  			name: "User defined provider configurations override defaults",
   104  			fields: fields{
   105  				configGetter: test.NewFakeReader().
   106  					WithVar(
   107  						ProvidersConfigKey,
   108  						fmt.Sprintf("- name: %q\n", defaults[0].Name())+
   109  							"  url: \"https://zzz/infrastructure-components.yaml\"\n"+
   110  							fmt.Sprintf("  type: %q\n", defaults[0].Type()),
   111  					),
   112  			},
   113  			want:    defaultsWithOverride,
   114  			wantErr: false,
   115  		},
   116  		{
   117  			name: "Fails for invalid user defined provider configurations",
   118  			fields: fields{
   119  				configGetter: test.NewFakeReader().
   120  					WithVar(
   121  						ProvidersConfigKey,
   122  						"- foo\n",
   123  					),
   124  			},
   125  			want:    nil,
   126  			wantErr: true,
   127  		},
   128  		{
   129  			name: "Fails for invalid user defined provider configurations",
   130  			fields: fields{
   131  				configGetter: test.NewFakeReader().
   132  					WithVar(
   133  						ProvidersConfigKey,
   134  						"- name: \"\"\n"+ // name must not be empty
   135  							"  url: \"\"\n"+
   136  							"  type: \"\"\n",
   137  					),
   138  			},
   139  			want:    nil,
   140  			wantErr: true,
   141  		},
   142  	}
   143  
   144  	format.MaxLength = 15000 // This way it doesn't truncate the output on test failure
   145  
   146  	for _, tt := range tests {
   147  		t.Run(tt.name, func(t *testing.T) {
   148  			g := NewWithT(t)
   149  
   150  			for k, v := range tt.envVars {
   151  				g.Expect(os.Setenv(k, v)).To(Succeed())
   152  			}
   153  			defer func() {
   154  				for k := range tt.envVars {
   155  					g.Expect(os.Unsetenv(k)).To(Succeed())
   156  				}
   157  			}()
   158  			p := &providersClient{
   159  				reader: tt.fields.configGetter,
   160  			}
   161  			got, err := p.List()
   162  			if tt.wantErr {
   163  				g.Expect(err).To(HaveOccurred())
   164  				return
   165  			}
   166  
   167  			g.Expect(err).ToNot(HaveOccurred())
   168  			g.Expect(got).To(Equal(tt.want))
   169  		})
   170  	}
   171  }
   172  
   173  func Test_validateProvider(t *testing.T) {
   174  	type args struct {
   175  		r Provider
   176  	}
   177  	tests := []struct {
   178  		name    string
   179  		args    args
   180  		wantErr bool
   181  	}{
   182  		{
   183  			name: "Pass",
   184  			args: args{
   185  				r: NewProvider("foo", "https://something.com", clusterctlv1.InfrastructureProviderType),
   186  			},
   187  			wantErr: false,
   188  		},
   189  		{
   190  			name: "Pass (core provider)",
   191  			args: args{
   192  				r: NewProvider(ClusterAPIProviderName, "https://something.com", clusterctlv1.CoreProviderType),
   193  			},
   194  			wantErr: false,
   195  		},
   196  		{
   197  			name: "Fails if cluster-api name used with wrong type",
   198  			args: args{
   199  				r: NewProvider(ClusterAPIProviderName, "https://something.com", clusterctlv1.BootstrapProviderType),
   200  			},
   201  			wantErr: true,
   202  		},
   203  		{
   204  			name: "Fails if CoreProviderType used with wrong name",
   205  			args: args{
   206  				r: NewProvider("sss", "https://something.com", clusterctlv1.CoreProviderType),
   207  			},
   208  			wantErr: true,
   209  		},
   210  		{
   211  			name: "Fails if name is empty",
   212  			args: args{
   213  				r: NewProvider("", "", ""),
   214  			},
   215  			wantErr: true,
   216  		},
   217  		{
   218  			name: "Fails if name is not valid",
   219  			args: args{
   220  				r: NewProvider("FOo", "https://something.com", ""),
   221  			},
   222  			wantErr: true,
   223  		},
   224  		{
   225  			name: "Fails if url is empty",
   226  			args: args{
   227  				r: NewProvider("foo", "", ""),
   228  			},
   229  			wantErr: true,
   230  		},
   231  		{
   232  			name: "Fails if url is not valid",
   233  			args: args{
   234  				r: NewProvider("foo", "%gh&%ij", "bar"),
   235  			},
   236  			wantErr: true,
   237  		},
   238  		{
   239  			name: "Fails if type is empty",
   240  			args: args{
   241  				r: NewProvider("foo", "https://something.com", ""),
   242  			},
   243  			wantErr: true,
   244  		},
   245  		{
   246  			name: "Fails if type is not valid",
   247  			args: args{
   248  				r: NewProvider("foo", "https://something.com", "bar"),
   249  			},
   250  			wantErr: true,
   251  		},
   252  	}
   253  	for _, tt := range tests {
   254  		t.Run(tt.name, func(t *testing.T) {
   255  			g := NewWithT(t)
   256  
   257  			err := validateProvider(tt.args.r)
   258  			if tt.wantErr {
   259  				g.Expect(err).To(HaveOccurred())
   260  			} else {
   261  				g.Expect(err).ToNot(HaveOccurred())
   262  			}
   263  		})
   264  	}
   265  }
   266  
   267  // check if Defaults returns valid provider repository configurations
   268  // this is a safeguard for catching changes leading to formally invalid default configurations.
   269  func Test_providers_Defaults(t *testing.T) {
   270  	g := NewWithT(t)
   271  
   272  	reader := test.NewFakeReader()
   273  
   274  	p := &providersClient{
   275  		reader: reader,
   276  	}
   277  
   278  	defaults := p.defaults()
   279  
   280  	for _, d := range defaults {
   281  		err := validateProvider(d)
   282  		g.Expect(err).ToNot(HaveOccurred())
   283  	}
   284  }
   285  
   286  func Test_providers_Get(t *testing.T) {
   287  	reader := test.NewFakeReader()
   288  
   289  	p := &providersClient{
   290  		reader: reader,
   291  	}
   292  
   293  	defaults := p.defaults()
   294  
   295  	type args struct {
   296  		name         string
   297  		providerType clusterctlv1.ProviderType
   298  	}
   299  	tests := []struct {
   300  		name    string
   301  		args    args
   302  		want    Provider
   303  		wantErr bool
   304  	}{
   305  		{
   306  			name: "pass",
   307  			args: args{
   308  				name:         p.defaults()[0].Name(),
   309  				providerType: p.defaults()[0].Type(),
   310  			},
   311  			want:    defaults[0],
   312  			wantErr: false,
   313  		},
   314  		{
   315  			name: "kubeadm bootstrap",
   316  			args: args{
   317  				name:         KubeadmBootstrapProviderName,
   318  				providerType: clusterctlv1.BootstrapProviderType,
   319  			},
   320  			want:    NewProvider(KubeadmBootstrapProviderName, "https://github.com/kubernetes-sigs/cluster-api/releases/latest/bootstrap-components.yaml", clusterctlv1.BootstrapProviderType),
   321  			wantErr: false,
   322  		},
   323  		{
   324  			name: "kubeadm control-plane",
   325  			args: args{
   326  				name:         KubeadmControlPlaneProviderName,
   327  				providerType: clusterctlv1.ControlPlaneProviderType,
   328  			},
   329  			want:    NewProvider(KubeadmControlPlaneProviderName, "https://github.com/kubernetes-sigs/cluster-api/releases/latest/control-plane-components.yaml", clusterctlv1.ControlPlaneProviderType),
   330  			wantErr: false,
   331  		},
   332  		{
   333  			name: "fails if the provider does not exists (wrong name)",
   334  			args: args{
   335  				name:         "foo",
   336  				providerType: clusterctlv1.CoreProviderType,
   337  			},
   338  			want:    nil,
   339  			wantErr: true,
   340  		},
   341  		{
   342  			name: "fails if the provider does not exists (wrong type)",
   343  			args: args{
   344  				name:         ClusterAPIProviderName,
   345  				providerType: clusterctlv1.InfrastructureProviderType,
   346  			},
   347  			want:    nil,
   348  			wantErr: true,
   349  		},
   350  	}
   351  	for _, tt := range tests {
   352  		t.Run(tt.name, func(t *testing.T) {
   353  			g := NewWithT(t)
   354  
   355  			p := &providersClient{
   356  				reader: reader,
   357  			}
   358  			got, err := p.Get(tt.args.name, tt.args.providerType)
   359  			if tt.wantErr {
   360  				g.Expect(err).To(HaveOccurred())
   361  				return
   362  			}
   363  
   364  			g.Expect(err).ToNot(HaveOccurred())
   365  			g.Expect(got).To(Equal(tt.want))
   366  		})
   367  	}
   368  }