sigs.k8s.io/cluster-api@v1.6.3/cmd/clusterctl/client/repository/repository_local_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 repository
    18  
    19  import (
    20  	"context"
    21  	"os"
    22  	"path/filepath"
    23  	"testing"
    24  
    25  	. "github.com/onsi/gomega"
    26  
    27  	clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
    28  	"sigs.k8s.io/cluster-api/cmd/clusterctl/client/config"
    29  	"sigs.k8s.io/cluster-api/cmd/clusterctl/internal/test"
    30  )
    31  
    32  func Test_localRepository_newLocalRepository(t *testing.T) {
    33  	type fields struct {
    34  		provider              config.Provider
    35  		configVariablesClient config.VariablesClient
    36  	}
    37  	type want struct {
    38  		basepath       string
    39  		providerLabel  string
    40  		defaultVersion string
    41  		rootPath       string
    42  		componentsPath string
    43  	}
    44  	tests := []struct {
    45  		name    string
    46  		fields  fields
    47  		want    want
    48  		wantErr bool
    49  	}{
    50  		{
    51  			name: "successfully creates new local repository object with a single version",
    52  			fields: fields{
    53  				provider:              config.NewProvider("foo", "/base/path/bootstrap-foo/v1.0.0/bootstrap-components.yaml", clusterctlv1.BootstrapProviderType),
    54  				configVariablesClient: test.NewFakeVariableClient(),
    55  			},
    56  			want: want{
    57  				basepath:       "/base/path",
    58  				providerLabel:  "bootstrap-foo",
    59  				defaultVersion: "v1.0.0",
    60  				rootPath:       "",
    61  				componentsPath: "bootstrap-components.yaml",
    62  			},
    63  			wantErr: false,
    64  		},
    65  		{
    66  			name: "successfully creates new local repository object with a single version and no basepath",
    67  			fields: fields{
    68  				provider:              config.NewProvider("foo", "/bootstrap-foo/v1.0.0/bootstrap-components.yaml", clusterctlv1.BootstrapProviderType),
    69  				configVariablesClient: test.NewFakeVariableClient(),
    70  			},
    71  			want: want{
    72  				basepath:       "/",
    73  				providerLabel:  "bootstrap-foo",
    74  				defaultVersion: "v1.0.0",
    75  				rootPath:       "",
    76  				componentsPath: "bootstrap-components.yaml",
    77  			},
    78  			wantErr: false,
    79  		},
    80  		{
    81  			name: "fails if an absolute path not specified",
    82  			fields: fields{
    83  				provider:              config.NewProvider("foo", "./bootstrap-foo/v1/bootstrap-components.yaml", clusterctlv1.BootstrapProviderType),
    84  				configVariablesClient: test.NewFakeVariableClient(),
    85  			},
    86  			want:    want{},
    87  			wantErr: true,
    88  		},
    89  		{
    90  			name: "fails if provider id does not match in the path",
    91  			fields: fields{
    92  				provider:              config.NewProvider("foo", "/foo/bar/bootstrap-bar/v1/bootstrap-components.yaml", clusterctlv1.BootstrapProviderType),
    93  				configVariablesClient: test.NewFakeVariableClient(),
    94  			},
    95  			want:    want{},
    96  			wantErr: true,
    97  		},
    98  		{
    99  			name: "fails if malformed path: invalid version directory",
   100  			fields: fields{
   101  				provider:              config.NewProvider("foo", "/foo/bar/bootstrap-foo/v.a.b.c/bootstrap-components.yaml", clusterctlv1.BootstrapProviderType),
   102  				configVariablesClient: test.NewFakeVariableClient(),
   103  			},
   104  			want:    want{},
   105  			wantErr: true,
   106  		},
   107  	}
   108  	for _, tt := range tests {
   109  		t.Run(tt.name, func(t *testing.T) {
   110  			g := NewWithT(t)
   111  
   112  			got, err := newLocalRepository(context.Background(), tt.fields.provider, tt.fields.configVariablesClient)
   113  			if tt.wantErr {
   114  				g.Expect(err).To(HaveOccurred())
   115  				return
   116  			}
   117  			g.Expect(err).ToNot(HaveOccurred())
   118  
   119  			g.Expect(got.basepath).To(Equal(tt.want.basepath))
   120  			g.Expect(got.providerLabel).To(Equal(tt.want.providerLabel))
   121  			g.Expect(got.DefaultVersion()).To(Equal(tt.want.defaultVersion))
   122  			g.Expect(got.RootPath()).To(Equal(tt.want.rootPath))
   123  			g.Expect(got.ComponentsPath()).To(Equal(tt.want.componentsPath))
   124  		})
   125  	}
   126  }
   127  
   128  func createTempDir(t *testing.T) string {
   129  	t.Helper()
   130  
   131  	dir, err := os.MkdirTemp("", "cc")
   132  	if err != nil {
   133  		t.Fatalf("err: %s", err)
   134  	}
   135  	return dir
   136  }
   137  
   138  func createLocalTestProviderFile(t *testing.T, tmpDir, path, msg string) string {
   139  	t.Helper()
   140  
   141  	g := NewWithT(t)
   142  
   143  	dst := filepath.Join(tmpDir, path)
   144  	// Create all directories in the standard layout
   145  	g.Expect(os.MkdirAll(filepath.Dir(dst), 0750)).To(Succeed())
   146  	g.Expect(os.WriteFile(dst, []byte(msg), 0600)).To(Succeed())
   147  
   148  	return dst
   149  }
   150  
   151  func Test_localRepository_newLocalRepository_Latest(t *testing.T) {
   152  	g := NewWithT(t)
   153  
   154  	tmpDir := createTempDir(t)
   155  	defer os.RemoveAll(tmpDir)
   156  
   157  	// Create several release directories
   158  	createLocalTestProviderFile(t, tmpDir, "bootstrap-foo/v1.0.0/bootstrap-components.yaml", "foo: bar")
   159  	createLocalTestProviderFile(t, tmpDir, "bootstrap-foo/v1.0.1/bootstrap-components.yaml", "foo: bar")
   160  	createLocalTestProviderFile(t, tmpDir, "bootstrap-foo/v2.0.0-alpha.0/bootstrap-components.yaml", "foo: bar")
   161  	createLocalTestProviderFile(t, tmpDir, "bootstrap-foo/Foo.Bar/bootstrap-components.yaml", "foo: bar")
   162  	createLocalTestProviderFile(t, tmpDir, "bootstrap-foo/foo.file", "foo: bar")
   163  
   164  	// Provider URL for the latest release
   165  	p2URLLatest := "bootstrap-foo/latest/bootstrap-components.yaml"
   166  	p2URLLatestAbs := filepath.Join(tmpDir, p2URLLatest)
   167  	p2 := config.NewProvider("foo", p2URLLatestAbs, clusterctlv1.BootstrapProviderType)
   168  
   169  	got, err := newLocalRepository(context.Background(), p2, test.NewFakeVariableClient())
   170  	g.Expect(err).ToNot(HaveOccurred())
   171  
   172  	g.Expect(got.basepath).To(Equal(tmpDir))
   173  	g.Expect(got.providerLabel).To(Equal("bootstrap-foo"))
   174  	g.Expect(got.DefaultVersion()).To(Equal("v1.0.1"))
   175  	g.Expect(got.RootPath()).To(BeEmpty())
   176  	g.Expect(got.ComponentsPath()).To(Equal("bootstrap-components.yaml"))
   177  }
   178  
   179  func Test_localRepository_GetFile(t *testing.T) {
   180  	tmpDir := createTempDir(t)
   181  	defer os.RemoveAll(tmpDir)
   182  
   183  	// Provider 1: URL is for the only release available
   184  	dst1 := createLocalTestProviderFile(t, tmpDir, "bootstrap-foo/v1.0.0/bootstrap-components.yaml", "foo: bar")
   185  	p1 := config.NewProvider("foo", dst1, clusterctlv1.BootstrapProviderType)
   186  
   187  	// Provider 2: URL is for the latest release
   188  	createLocalTestProviderFile(t, tmpDir, "bootstrap-bar/v1.0.0/bootstrap-components.yaml", "version: v1.0.0")
   189  	createLocalTestProviderFile(t, tmpDir, "bootstrap-bar/v1.0.1/bootstrap-components.yaml", "version: v1.0.1")
   190  	createLocalTestProviderFile(t, tmpDir, "bootstrap-bar/v2.0.0-alpha.0/bootstrap-components.yaml", "version: v2.0.0-alpha.0")
   191  	createLocalTestProviderFile(t, tmpDir, "bootstrap-bar/Foo.Bar/bootstrap-components.yaml", "version: Foo.Bar")
   192  	createLocalTestProviderFile(t, tmpDir, "bootstrap-bar/foo.file", "foo: bar")
   193  	p2URLLatest := "bootstrap-bar/latest/bootstrap-components.yaml"
   194  	p2URLLatestAbs := filepath.Join(tmpDir, p2URLLatest)
   195  	p2 := config.NewProvider("bar", p2URLLatestAbs, clusterctlv1.BootstrapProviderType)
   196  
   197  	// Provider 3: URL is for only prerelease available
   198  	dst3 := createLocalTestProviderFile(t, tmpDir, "bootstrap-baz/v1.0.0-alpha.0/bootstrap-components.yaml", "version: v1.0.0-alpha.0")
   199  	p3 := config.NewProvider("baz", dst3, clusterctlv1.BootstrapProviderType)
   200  
   201  	type fields struct {
   202  		provider              config.Provider
   203  		configVariablesClient config.VariablesClient
   204  	}
   205  	type args struct {
   206  		version  string
   207  		fileName string
   208  	}
   209  	type want struct {
   210  		contents string
   211  	}
   212  	tests := []struct {
   213  		name    string
   214  		fields  fields
   215  		args    args
   216  		want    want
   217  		wantErr bool
   218  	}{
   219  		{
   220  			name: "Get file from release directory",
   221  			fields: fields{
   222  				provider:              p1,
   223  				configVariablesClient: test.NewFakeVariableClient(),
   224  			},
   225  			args: args{
   226  				version:  "v1.0.0",
   227  				fileName: "bootstrap-components.yaml",
   228  			},
   229  			want: want{
   230  				contents: "foo: bar",
   231  			},
   232  			wantErr: false,
   233  		},
   234  		{
   235  			name: "Get file from latest release directory",
   236  			fields: fields{
   237  				provider:              p2,
   238  				configVariablesClient: test.NewFakeVariableClient(),
   239  			},
   240  			args: args{
   241  				version:  "latest",
   242  				fileName: "bootstrap-components.yaml",
   243  			},
   244  			want: want{
   245  				contents: "version: v1.0.1", // We use the file contents to determine data was read from latest release
   246  			},
   247  			wantErr: false,
   248  		},
   249  		{
   250  			name: "Get file from default version release directory",
   251  			fields: fields{
   252  				provider:              p2,
   253  				configVariablesClient: test.NewFakeVariableClient(),
   254  			},
   255  			args: args{
   256  				version:  "",
   257  				fileName: "bootstrap-components.yaml",
   258  			},
   259  			want: want{
   260  				contents: "version: v1.0.1", // We use the file contents to determine data was read from latest release
   261  			},
   262  			wantErr: false,
   263  		},
   264  		{
   265  			name: "Get file from pre-release version release directory",
   266  			fields: fields{
   267  				provider:              p2,
   268  				configVariablesClient: test.NewFakeVariableClient(),
   269  			},
   270  			args: args{
   271  				version:  "v2.0.0-alpha.0",
   272  				fileName: "bootstrap-components.yaml",
   273  			},
   274  			want: want{
   275  				contents: "version: v2.0.0-alpha.0", // We use the file contents to determine data was read from latest release
   276  			},
   277  			wantErr: false,
   278  		},
   279  		{
   280  			name: "Get file from latest prerelease directory if no releases",
   281  			fields: fields{
   282  				provider:              p3,
   283  				configVariablesClient: test.NewFakeVariableClient(),
   284  			},
   285  			args: args{
   286  				version:  "latest",
   287  				fileName: "bootstrap-components.yaml",
   288  			},
   289  			want: want{
   290  				contents: "version: v1.0.0-alpha.0", // We use the file contents to determine data was read from latest prerelease
   291  			},
   292  			wantErr: false,
   293  		},
   294  	}
   295  	for _, tt := range tests {
   296  		t.Run(tt.name, func(t *testing.T) {
   297  			g := NewWithT(t)
   298  
   299  			r, err := newLocalRepository(context.Background(), tt.fields.provider, tt.fields.configVariablesClient)
   300  			g.Expect(err).ToNot(HaveOccurred())
   301  
   302  			got, err := r.GetFile(context.Background(), tt.args.version, tt.args.fileName)
   303  			if tt.wantErr {
   304  				g.Expect(err).To(HaveOccurred())
   305  				return
   306  			}
   307  
   308  			g.Expect(err).ToNot(HaveOccurred())
   309  			g.Expect(string(got)).To(Equal(tt.want.contents))
   310  		})
   311  	}
   312  }
   313  
   314  func Test_localRepository_GetVersions(t *testing.T) {
   315  	tmpDir := createTempDir(t)
   316  	defer os.RemoveAll(tmpDir)
   317  
   318  	// Provider 1: has a single release available
   319  	dst1 := createLocalTestProviderFile(t, tmpDir, "bootstrap-foo/v1.0.0/bootstrap-components.yaml", "foo: bar")
   320  	p1 := config.NewProvider("foo", dst1, clusterctlv1.BootstrapProviderType)
   321  
   322  	// Provider 2: Has multiple releases available
   323  	createLocalTestProviderFile(t, tmpDir, "bootstrap-bar/v1.0.0/bootstrap-components.yaml", "version: v1.0.0")
   324  	createLocalTestProviderFile(t, tmpDir, "bootstrap-bar/v1.0.1/bootstrap-components.yaml", "version: v1.0.1")
   325  	createLocalTestProviderFile(t, tmpDir, "bootstrap-bar/v2.0.1/bootstrap-components.yaml", "version: v2.0.1")
   326  	createLocalTestProviderFile(t, tmpDir, "bootstrap-bar/v2.0.2+exp.sha.5114f85/bootstrap-components.yaml", "version: v2.0.2+exp.sha.5114f85")
   327  	createLocalTestProviderFile(t, tmpDir, "bootstrap-bar/v2.0.3-alpha/bootstrap-components.yaml", "version: v2.0.3-alpha")
   328  	createLocalTestProviderFile(t, tmpDir, "bootstrap-bar/Foo.Bar/bootstrap-components.yaml", "version: Foo.Bar")
   329  	createLocalTestProviderFile(t, tmpDir, "bootstrap-bar/foo.file", "foo: bar")
   330  	p2URLLatest := "bootstrap-bar/latest/bootstrap-components.yaml"
   331  	p2URLLatestAbs := filepath.Join(tmpDir, p2URLLatest)
   332  	p2 := config.NewProvider("bar", p2URLLatestAbs, clusterctlv1.BootstrapProviderType)
   333  
   334  	type fields struct {
   335  		provider              config.Provider
   336  		configVariablesClient config.VariablesClient
   337  	}
   338  	type want struct {
   339  		versions []string
   340  	}
   341  	tests := []struct {
   342  		name    string
   343  		fields  fields
   344  		want    want
   345  		wantErr bool
   346  	}{
   347  		{
   348  			name: "Get the only release available from release directory",
   349  			fields: fields{
   350  				provider:              p1,
   351  				configVariablesClient: test.NewFakeVariableClient(),
   352  			},
   353  			want: want{
   354  				versions: []string{"v1.0.0"},
   355  			},
   356  			wantErr: false,
   357  		},
   358  		{
   359  			name: "Get all valid releases available from release directory",
   360  			fields: fields{
   361  				provider:              p2,
   362  				configVariablesClient: test.NewFakeVariableClient(),
   363  			},
   364  			want: want{
   365  				versions: []string{"v1.0.0", "v1.0.1", "v2.0.1", "v2.0.2+exp.sha.5114f85", "v2.0.3-alpha"},
   366  			},
   367  			wantErr: false,
   368  		},
   369  	}
   370  	for _, tt := range tests {
   371  		t.Run(tt.name, func(t *testing.T) {
   372  			g := NewWithT(t)
   373  
   374  			ctx := context.Background()
   375  
   376  			r, err := newLocalRepository(ctx, tt.fields.provider, tt.fields.configVariablesClient)
   377  			g.Expect(err).ToNot(HaveOccurred())
   378  
   379  			got, err := r.GetVersions(ctx)
   380  			if tt.wantErr {
   381  				g.Expect(err).To(HaveOccurred())
   382  				return
   383  			}
   384  			g.Expect(err).ToNot(HaveOccurred())
   385  
   386  			g.Expect(got).To(ConsistOf(tt.want.versions))
   387  		})
   388  	}
   389  }