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