sigs.k8s.io/cluster-api@v1.7.1/cmd/clusterctl/client/repository/components_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 repository
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"testing"
    23  
    24  	. "github.com/onsi/gomega"
    25  	"github.com/pkg/errors"
    26  
    27  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    28  	clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
    29  	"sigs.k8s.io/cluster-api/cmd/clusterctl/client/config"
    30  	yaml "sigs.k8s.io/cluster-api/cmd/clusterctl/client/yamlprocessor"
    31  	"sigs.k8s.io/cluster-api/cmd/clusterctl/internal/test"
    32  	utilyaml "sigs.k8s.io/cluster-api/util/yaml"
    33  )
    34  
    35  const (
    36  	variableName  = "FOO"
    37  	variableValue = "foo"
    38  )
    39  
    40  var controllerYaml = []byte("apiVersion: apps/v1\n" +
    41  	"kind: Deployment\n" +
    42  	"metadata:\n" +
    43  	"  name: my-controller\n" +
    44  	"spec:\n" +
    45  	"  template:\n" +
    46  	"    spec:\n" +
    47  	"      containers:\n" +
    48  	"      - name: manager\n" +
    49  	"        image: docker.io/library/image:latest\n")
    50  
    51  const namespaceName = "capa-system"
    52  
    53  var namespaceYaml = []byte("apiVersion: v1\n" +
    54  	"kind: Namespace\n" +
    55  	"metadata:\n" +
    56  	fmt.Sprintf("  name: %s", namespaceName))
    57  
    58  var configMapYaml = []byte("apiVersion: v1\n" +
    59  	"data:\n" +
    60  	fmt.Sprintf("  variable: ${%s}\n", variableName) +
    61  	"kind: ConfigMap\n" +
    62  	"metadata:\n" +
    63  	"  name: manager")
    64  
    65  func Test_componentsClient_Get(t *testing.T) {
    66  	g := NewWithT(t)
    67  
    68  	p1 := config.NewProvider("p1", "", clusterctlv1.BootstrapProviderType)
    69  
    70  	configClient, err := config.New(context.Background(), "", config.InjectReader(test.NewFakeReader().WithVar(variableName, variableValue)))
    71  	g.Expect(err).ToNot(HaveOccurred())
    72  
    73  	type fields struct {
    74  		provider   config.Provider
    75  		repository Repository
    76  		processor  yaml.Processor
    77  	}
    78  	type args struct {
    79  		version         string
    80  		targetNamespace string
    81  		skipVariables   bool
    82  	}
    83  	type want struct {
    84  		provider        config.Provider
    85  		version         string
    86  		targetNamespace string
    87  		variables       []string
    88  	}
    89  	tests := []struct {
    90  		name    string
    91  		fields  fields
    92  		args    args
    93  		want    want
    94  		wantErr bool
    95  	}{
    96  		{
    97  			name: "successfully gets the components",
    98  			fields: fields{
    99  				provider: p1,
   100  				repository: NewMemoryRepository().
   101  					WithPaths("root", "components.yaml").
   102  					WithDefaultVersion("v1.0.0").
   103  					WithFile("v1.0.0", "components.yaml", utilyaml.JoinYaml(namespaceYaml, controllerYaml, configMapYaml)),
   104  			},
   105  			args: args{
   106  				version:         "v1.0.0",
   107  				targetNamespace: "",
   108  			},
   109  			want: want{
   110  				provider:        p1,
   111  				version:         "v1.0.0",               // version detected
   112  				targetNamespace: namespaceName,          // default targetNamespace detected
   113  				variables:       []string{variableName}, // variable detected
   114  			},
   115  			wantErr: false,
   116  		},
   117  		{
   118  			name: "successfully gets the components even with SkipTemplateProcess defined",
   119  			fields: fields{
   120  				provider: p1,
   121  				repository: NewMemoryRepository().
   122  					WithPaths("root", "components.yaml").
   123  					WithDefaultVersion("v1.0.0").
   124  					WithFile("v1.0.0", "components.yaml", utilyaml.JoinYaml(namespaceYaml, controllerYaml, configMapYaml)),
   125  			},
   126  			args: args{
   127  				version:         "v1.0.0",
   128  				targetNamespace: "",
   129  				skipVariables:   true,
   130  			},
   131  			want: want{
   132  				provider:        p1,
   133  				version:         "v1.0.0",               // version detected
   134  				targetNamespace: namespaceName,          // default targetNamespace detected
   135  				variables:       []string{variableName}, // variable detected
   136  			},
   137  			wantErr: false,
   138  		},
   139  		{
   140  			name: "targetNamespace overrides default targetNamespace",
   141  			fields: fields{
   142  				provider: p1,
   143  				repository: NewMemoryRepository().
   144  					WithPaths("root", "components.yaml").
   145  					WithDefaultVersion("v1.0.0").
   146  					WithFile("v1.0.0", "components.yaml", utilyaml.JoinYaml(namespaceYaml, controllerYaml, configMapYaml)),
   147  			},
   148  			args: args{
   149  				version:         "v1.0.0",
   150  				targetNamespace: "ns2",
   151  			},
   152  			want: want{
   153  				provider:        p1,
   154  				version:         "v1.0.0",               // version detected
   155  				targetNamespace: "ns2",                  // targetNamespace overrides default targetNamespace
   156  				variables:       []string{variableName}, // variable detected
   157  			},
   158  			wantErr: false,
   159  		},
   160  		{
   161  			name: "Fails if components file does not exists",
   162  			fields: fields{
   163  				provider: p1,
   164  				repository: NewMemoryRepository().
   165  					WithPaths("root", "components.yaml").
   166  					WithDefaultVersion("v1.0.0"),
   167  			},
   168  			args: args{
   169  				version:         "v1.0.0",
   170  				targetNamespace: "",
   171  			},
   172  			wantErr: true,
   173  		},
   174  		{
   175  			name: "Fails if default targetNamespace does not exists",
   176  			fields: fields{
   177  				provider: p1,
   178  				repository: NewMemoryRepository().
   179  					WithPaths("root", "components.yaml").
   180  					WithDefaultVersion("v1.0.0").
   181  					WithFile("v1.0.0", "components.yaml", utilyaml.JoinYaml(controllerYaml, configMapYaml)),
   182  			},
   183  			args: args{
   184  				version:         "v1.0.0",
   185  				targetNamespace: "",
   186  			},
   187  			wantErr: true,
   188  		},
   189  		{
   190  			name: "Pass if default targetNamespace does not exists but a target targetNamespace is set",
   191  			fields: fields{
   192  				provider: p1,
   193  				repository: NewMemoryRepository().
   194  					WithPaths("root", "components.yaml").
   195  					WithDefaultVersion("v1.0.0").
   196  					WithFile("v1.0.0", "components.yaml", utilyaml.JoinYaml(controllerYaml, configMapYaml)),
   197  			},
   198  			args: args{
   199  				version:         "v1.0.0",
   200  				targetNamespace: "ns2",
   201  			},
   202  			want: want{
   203  				provider:        p1,
   204  				version:         "v1.0.0",               // version detected
   205  				targetNamespace: "ns2",                  // target targetNamespace applied
   206  				variables:       []string{variableName}, // variable detected
   207  			},
   208  			wantErr: false,
   209  		},
   210  		{
   211  			name: "Fails if requested version does not exists",
   212  			fields: fields{
   213  				provider: p1,
   214  				repository: NewMemoryRepository().
   215  					WithPaths("root", "components.yaml").
   216  					WithDefaultVersion("v1.0.0").
   217  					WithFile("v1.0.0", "components.yaml", utilyaml.JoinYaml(controllerYaml, configMapYaml)),
   218  			},
   219  			args: args{
   220  				version:         "v2.0.0",
   221  				targetNamespace: "",
   222  			},
   223  			wantErr: true,
   224  		},
   225  		{
   226  			name: "Fails if yaml processor cannot get Variables",
   227  			fields: fields{
   228  				provider: p1,
   229  				repository: NewMemoryRepository().
   230  					WithPaths("root", "components.yaml").
   231  					WithDefaultVersion("v1.0.0").
   232  					WithFile("v1.0.0", "components.yaml", utilyaml.JoinYaml(namespaceYaml, controllerYaml, configMapYaml)),
   233  				processor: test.NewFakeProcessor().WithGetVariablesErr(errors.New("cannot get vars")),
   234  			},
   235  			args: args{
   236  				version:         "v1.0.0",
   237  				targetNamespace: "default",
   238  			},
   239  			wantErr: true,
   240  		},
   241  		{
   242  			name: "Fails if yaml processor cannot process the raw yaml",
   243  			fields: fields{
   244  				provider: p1,
   245  				repository: NewMemoryRepository().
   246  					WithPaths("root", "components.yaml").
   247  					WithDefaultVersion("v1.0.0").
   248  					WithFile("v1.0.0", "components.yaml", utilyaml.JoinYaml(namespaceYaml, controllerYaml, configMapYaml)),
   249  
   250  				processor: test.NewFakeProcessor().WithProcessErr(errors.New("cannot process")),
   251  			},
   252  			args: args{
   253  				version:         "v1.0.0",
   254  				targetNamespace: "default",
   255  			},
   256  			wantErr: true,
   257  		},
   258  	}
   259  	for _, tt := range tests {
   260  		t.Run(tt.name, func(t *testing.T) {
   261  			gs := NewWithT(t)
   262  
   263  			ctx := context.Background()
   264  
   265  			options := ComponentsOptions{
   266  				Version:             tt.args.version,
   267  				TargetNamespace:     tt.args.targetNamespace,
   268  				SkipTemplateProcess: tt.args.skipVariables,
   269  			}
   270  			f := newComponentsClient(tt.fields.provider, tt.fields.repository, configClient)
   271  			if tt.fields.processor != nil {
   272  				f.processor = tt.fields.processor
   273  			}
   274  			got, err := f.Get(ctx, options)
   275  			if tt.wantErr {
   276  				gs.Expect(err).To(HaveOccurred())
   277  				return
   278  			}
   279  			gs.Expect(err).ToNot(HaveOccurred())
   280  
   281  			gs.Expect(got.Name()).To(Equal(tt.want.provider.Name()))
   282  			gs.Expect(got.Type()).To(Equal(tt.want.provider.Type()))
   283  			gs.Expect(got.Version()).To(Equal(tt.want.version))
   284  			gs.Expect(got.TargetNamespace()).To(Equal(tt.want.targetNamespace))
   285  			gs.Expect(got.Variables()).To(Equal(tt.want.variables))
   286  
   287  			yaml, err := got.Yaml()
   288  			if err != nil {
   289  				t.Errorf("got.Yaml() error = %v", err)
   290  				return
   291  			}
   292  
   293  			if !tt.args.skipVariables && len(tt.want.variables) > 0 {
   294  				gs.Expect(yaml).To(ContainSubstring(variableValue))
   295  			}
   296  
   297  			// Verify that when SkipTemplateProcess is set we have all the variables
   298  			// in the template without the values processed.
   299  			if tt.args.skipVariables {
   300  				for _, v := range tt.want.variables {
   301  					gs.Expect(yaml).To(ContainSubstring(v))
   302  				}
   303  			}
   304  
   305  			for _, o := range got.Objs() {
   306  				for _, v := range []string{clusterctlv1.ClusterctlLabel, clusterv1.ProviderNameLabel} {
   307  					gs.Expect(o.GetLabels()).To(HaveKey(v))
   308  				}
   309  			}
   310  		})
   311  	}
   312  }