sigs.k8s.io/cluster-api@v1.7.1/cmd/clusterctl/client/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 client
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"testing"
    23  	"time"
    24  
    25  	"github.com/pkg/errors"
    26  	"k8s.io/apimachinery/pkg/runtime"
    27  	"k8s.io/apimachinery/pkg/runtime/serializer"
    28  	"k8s.io/apimachinery/pkg/util/wait"
    29  	"sigs.k8s.io/controller-runtime/pkg/client"
    30  
    31  	clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
    32  	"sigs.k8s.io/cluster-api/cmd/clusterctl/client/cluster"
    33  	"sigs.k8s.io/cluster-api/cmd/clusterctl/client/config"
    34  	"sigs.k8s.io/cluster-api/cmd/clusterctl/client/repository"
    35  	"sigs.k8s.io/cluster-api/cmd/clusterctl/client/tree"
    36  	yaml "sigs.k8s.io/cluster-api/cmd/clusterctl/client/yamlprocessor"
    37  	"sigs.k8s.io/cluster-api/cmd/clusterctl/internal/scheme"
    38  	"sigs.k8s.io/cluster-api/cmd/clusterctl/internal/test"
    39  )
    40  
    41  // TestNewFakeClient is a fake test to document fakeClient usage.
    42  func TestNewFakeClient(_ *testing.T) {
    43  	// create a fake config with a provider named P1 and a variable named var
    44  	repository1Config := config.NewProvider("p1", "url", clusterctlv1.CoreProviderType)
    45  
    46  	ctx := context.Background()
    47  
    48  	config1 := newFakeConfig(ctx).
    49  		WithVar("var", "value").
    50  		WithProvider(repository1Config)
    51  
    52  	// create a fake repository with some YAML files in it (usually matching the list of providers defined in the config)
    53  	repository1 := newFakeRepository(ctx, repository1Config, config1).
    54  		WithPaths("root", "components").
    55  		WithDefaultVersion("v1.0").
    56  		WithFile("v1.0", "components.yaml", []byte("content"))
    57  
    58  	// create a fake cluster, eventually adding some existing runtime objects to it
    59  	cluster1 := newFakeCluster(cluster.Kubeconfig{Path: "cluster1"}, config1).
    60  		WithObjs()
    61  
    62  	// create a new fakeClient that allows to execute tests on the fake config, the fake repositories and the fake cluster.
    63  	newFakeClient(context.Background(), config1).
    64  		WithRepository(repository1).
    65  		WithCluster(cluster1)
    66  }
    67  
    68  type fakeClient struct {
    69  	configClient config.Client
    70  	// mapping between kubeconfigPath/context with cluster client
    71  	clusters       map[cluster.Kubeconfig]cluster.Client
    72  	repositories   map[string]repository.Client
    73  	internalClient *clusterctlClient
    74  }
    75  
    76  var _ Client = &fakeClient{}
    77  
    78  func (f fakeClient) GetProvidersConfig() ([]Provider, error) {
    79  	return f.internalClient.GetProvidersConfig()
    80  }
    81  
    82  func (f fakeClient) GetProviderComponents(ctx context.Context, provider string, providerType clusterctlv1.ProviderType, options ComponentsOptions) (Components, error) {
    83  	return f.internalClient.GetProviderComponents(ctx, provider, providerType, options)
    84  }
    85  
    86  func (f fakeClient) GenerateProvider(ctx context.Context, provider string, providerType clusterctlv1.ProviderType, options ComponentsOptions) (Components, error) {
    87  	return f.internalClient.GenerateProvider(ctx, provider, providerType, options)
    88  }
    89  
    90  func (f fakeClient) GetClusterTemplate(ctx context.Context, options GetClusterTemplateOptions) (Template, error) {
    91  	return f.internalClient.GetClusterTemplate(ctx, options)
    92  }
    93  
    94  func (f fakeClient) GetKubeconfig(ctx context.Context, options GetKubeconfigOptions) (string, error) {
    95  	return f.internalClient.GetKubeconfig(ctx, options)
    96  }
    97  
    98  func (f fakeClient) Init(ctx context.Context, options InitOptions) ([]Components, error) {
    99  	return f.internalClient.Init(ctx, options)
   100  }
   101  
   102  func (f fakeClient) InitImages(ctx context.Context, options InitOptions) ([]string, error) {
   103  	return f.internalClient.InitImages(ctx, options)
   104  }
   105  
   106  func (f fakeClient) Delete(ctx context.Context, options DeleteOptions) error {
   107  	return f.internalClient.Delete(ctx, options)
   108  }
   109  
   110  func (f fakeClient) Move(ctx context.Context, options MoveOptions) error {
   111  	return f.internalClient.Move(ctx, options)
   112  }
   113  
   114  func (f fakeClient) PlanUpgrade(ctx context.Context, options PlanUpgradeOptions) ([]UpgradePlan, error) {
   115  	return f.internalClient.PlanUpgrade(ctx, options)
   116  }
   117  
   118  func (f fakeClient) PlanCertManagerUpgrade(ctx context.Context, options PlanUpgradeOptions) (CertManagerUpgradePlan, error) {
   119  	return f.internalClient.PlanCertManagerUpgrade(ctx, options)
   120  }
   121  
   122  func (f fakeClient) ApplyUpgrade(ctx context.Context, options ApplyUpgradeOptions) error {
   123  	return f.internalClient.ApplyUpgrade(ctx, options)
   124  }
   125  
   126  func (f fakeClient) ProcessYAML(ctx context.Context, options ProcessYAMLOptions) (YamlPrinter, error) {
   127  	return f.internalClient.ProcessYAML(ctx, options)
   128  }
   129  
   130  func (f fakeClient) RolloutRestart(ctx context.Context, options RolloutRestartOptions) error {
   131  	return f.internalClient.RolloutRestart(ctx, options)
   132  }
   133  
   134  func (f fakeClient) DescribeCluster(ctx context.Context, options DescribeClusterOptions) (*tree.ObjectTree, error) {
   135  	return f.internalClient.DescribeCluster(ctx, options)
   136  }
   137  
   138  func (f fakeClient) RolloutPause(ctx context.Context, options RolloutPauseOptions) error {
   139  	return f.internalClient.RolloutPause(ctx, options)
   140  }
   141  
   142  func (f fakeClient) RolloutResume(ctx context.Context, options RolloutResumeOptions) error {
   143  	return f.internalClient.RolloutResume(ctx, options)
   144  }
   145  
   146  func (f fakeClient) RolloutUndo(ctx context.Context, options RolloutUndoOptions) error {
   147  	return f.internalClient.RolloutUndo(ctx, options)
   148  }
   149  
   150  func (f fakeClient) TopologyPlan(ctx context.Context, options TopologyPlanOptions) (*cluster.TopologyPlanOutput, error) {
   151  	return f.internalClient.TopologyPlan(ctx, options)
   152  }
   153  
   154  // newFakeClient returns a clusterctl client that allows to execute tests on a set of fake config, fake repositories and fake clusters.
   155  // you can use WithCluster and WithRepository to prepare for the test case.
   156  func newFakeClient(ctx context.Context, configClient config.Client) *fakeClient {
   157  	fake := &fakeClient{
   158  		clusters:     map[cluster.Kubeconfig]cluster.Client{},
   159  		repositories: map[string]repository.Client{},
   160  	}
   161  
   162  	fake.configClient = configClient
   163  	if fake.configClient == nil {
   164  		fake.configClient = newFakeConfig(ctx)
   165  	}
   166  
   167  	var clusterClientFactory = func(i ClusterClientFactoryInput) (cluster.Client, error) {
   168  		// converting the client.Kubeconfig to cluster.Kubeconfig alias
   169  		k := cluster.Kubeconfig(i.Kubeconfig)
   170  		if _, ok := fake.clusters[k]; !ok {
   171  			return nil, errors.Errorf("Cluster for kubeconfig %q and/or context %q does not exist", i.Kubeconfig.Path, i.Kubeconfig.Context)
   172  		}
   173  		return fake.clusters[k], nil
   174  	}
   175  
   176  	fake.internalClient, _ = newClusterctlClient(ctx, "fake-config",
   177  		InjectConfig(fake.configClient),
   178  		InjectClusterClientFactory(clusterClientFactory),
   179  		InjectRepositoryFactory(func(_ context.Context, input RepositoryClientFactoryInput) (repository.Client, error) {
   180  			if _, ok := fake.repositories[input.Provider.ManifestLabel()]; !ok {
   181  				return nil, errors.Errorf("repository for kubeconfig %q does not exist", input.Provider.ManifestLabel())
   182  			}
   183  			return fake.repositories[input.Provider.ManifestLabel()], nil
   184  		}),
   185  	)
   186  
   187  	return fake
   188  }
   189  
   190  func (f *fakeClient) WithCluster(clusterClient cluster.Client) *fakeClient {
   191  	input := clusterClient.Kubeconfig()
   192  	f.clusters[input] = clusterClient
   193  	return f
   194  }
   195  
   196  func (f *fakeClient) WithRepository(repositoryClient repository.Client) *fakeClient {
   197  	if fc, ok := f.configClient.(fakeConfigClient); ok {
   198  		fc.WithProvider(repositoryClient)
   199  	}
   200  	f.repositories[repositoryClient.ManifestLabel()] = repositoryClient
   201  	return f
   202  }
   203  
   204  // newFakeCluster returns a fakeClusterClient that
   205  // internally uses a FakeProxy (based on the controller-runtime FakeClient).
   206  // You can use WithObjs to pre-load a set of runtime objects in the cluster.
   207  func newFakeCluster(kubeconfig cluster.Kubeconfig, configClient config.Client) *fakeClusterClient {
   208  	fake := &fakeClusterClient{
   209  		kubeconfig:   kubeconfig,
   210  		repositories: map[string]repository.Client{},
   211  		certManager:  newFakeCertManagerClient(nil, nil),
   212  	}
   213  
   214  	fake.fakeProxy = test.NewFakeProxy()
   215  	pollImmediateWaiter := func(context.Context, time.Duration, time.Duration, wait.ConditionWithContextFunc) error {
   216  		return nil
   217  	}
   218  
   219  	fake.internalclient = cluster.New(kubeconfig, configClient,
   220  		cluster.InjectProxy(fake.fakeProxy),
   221  		cluster.InjectPollImmediateWaiter(pollImmediateWaiter),
   222  		cluster.InjectRepositoryFactory(func(_ context.Context, provider config.Provider, _ config.Client, _ ...repository.Option) (repository.Client, error) {
   223  			if _, ok := fake.repositories[provider.Name()]; !ok {
   224  				return nil, errors.Errorf("repository for kubeconfig %q does not exist", provider.Name())
   225  			}
   226  			return fake.repositories[provider.Name()], nil
   227  		}),
   228  	)
   229  	return fake
   230  }
   231  
   232  // newFakeCertManagerClient creates a new CertManagerClient
   233  // allows the caller to define which images are needed for the manager to run.
   234  func newFakeCertManagerClient(imagesReturnImages []string, imagesReturnError error) *fakeCertManagerClient {
   235  	return &fakeCertManagerClient{
   236  		images:      imagesReturnImages,
   237  		imagesError: imagesReturnError,
   238  	}
   239  }
   240  
   241  type fakeCertManagerClient struct {
   242  	images          []string
   243  	imagesError     error
   244  	certManagerPlan cluster.CertManagerUpgradePlan
   245  }
   246  
   247  var _ cluster.CertManagerClient = &fakeCertManagerClient{}
   248  
   249  func (p *fakeCertManagerClient) EnsureInstalled(_ context.Context) error {
   250  	return nil
   251  }
   252  
   253  func (p *fakeCertManagerClient) EnsureLatestVersion(_ context.Context) error {
   254  	return nil
   255  }
   256  
   257  func (p *fakeCertManagerClient) PlanUpgrade(_ context.Context) (cluster.CertManagerUpgradePlan, error) {
   258  	return p.certManagerPlan, nil
   259  }
   260  
   261  func (p *fakeCertManagerClient) Images(_ context.Context) ([]string, error) {
   262  	return p.images, p.imagesError
   263  }
   264  
   265  func (p *fakeCertManagerClient) WithCertManagerPlan(plan CertManagerUpgradePlan) *fakeCertManagerClient {
   266  	p.certManagerPlan = cluster.CertManagerUpgradePlan(plan)
   267  	return p
   268  }
   269  
   270  type fakeClusterClient struct {
   271  	kubeconfig      cluster.Kubeconfig
   272  	fakeProxy       *test.FakeProxy
   273  	fakeObjectMover cluster.ObjectMover
   274  	repositories    map[string]repository.Client
   275  	internalclient  cluster.Client
   276  	certManager     cluster.CertManagerClient
   277  }
   278  
   279  var _ cluster.Client = &fakeClusterClient{}
   280  
   281  func (f fakeClusterClient) Kubeconfig() cluster.Kubeconfig {
   282  	return f.kubeconfig
   283  }
   284  
   285  func (f fakeClusterClient) Proxy() cluster.Proxy {
   286  	return f.fakeProxy
   287  }
   288  
   289  func (f *fakeClusterClient) CertManager() cluster.CertManagerClient {
   290  	return f.certManager
   291  }
   292  
   293  func (f fakeClusterClient) ProviderComponents() cluster.ComponentsClient {
   294  	return f.internalclient.ProviderComponents()
   295  }
   296  
   297  func (f fakeClusterClient) ProviderInventory() cluster.InventoryClient {
   298  	return f.internalclient.ProviderInventory()
   299  }
   300  
   301  func (f fakeClusterClient) ProviderInstaller() cluster.ProviderInstaller {
   302  	return f.internalclient.ProviderInstaller()
   303  }
   304  
   305  func (f *fakeClusterClient) ObjectMover() cluster.ObjectMover {
   306  	if f.fakeObjectMover == nil {
   307  		return f.internalclient.ObjectMover()
   308  	}
   309  	return f.fakeObjectMover
   310  }
   311  
   312  func (f *fakeClusterClient) ProviderUpgrader() cluster.ProviderUpgrader {
   313  	return f.internalclient.ProviderUpgrader()
   314  }
   315  
   316  func (f *fakeClusterClient) Template() cluster.TemplateClient {
   317  	return f.internalclient.Template()
   318  }
   319  
   320  func (f *fakeClusterClient) WorkloadCluster() cluster.WorkloadCluster {
   321  	return f.internalclient.WorkloadCluster()
   322  }
   323  
   324  func (f *fakeClusterClient) Topology() cluster.TopologyClient {
   325  	return f.internalclient.Topology()
   326  }
   327  
   328  func (f *fakeClusterClient) WithObjs(objs ...client.Object) *fakeClusterClient {
   329  	f.fakeProxy.WithObjs(objs...)
   330  	return f
   331  }
   332  
   333  func (f *fakeClusterClient) WithProviderInventory(name string, providerType clusterctlv1.ProviderType, version, targetNamespace string) *fakeClusterClient {
   334  	f.fakeProxy.WithProviderInventory(name, providerType, version, targetNamespace)
   335  	return f
   336  }
   337  
   338  func (f *fakeClusterClient) WithRepository(repositoryClient repository.Client) *fakeClusterClient {
   339  	f.repositories[repositoryClient.Name()] = repositoryClient
   340  	return f
   341  }
   342  
   343  func (f *fakeClusterClient) WithObjectMover(mover cluster.ObjectMover) *fakeClusterClient {
   344  	f.fakeObjectMover = mover
   345  	return f
   346  }
   347  
   348  func (f *fakeClusterClient) WithCertManagerClient(client cluster.CertManagerClient) *fakeClusterClient {
   349  	f.certManager = client
   350  	return f
   351  }
   352  
   353  // newFakeConfig return a fake implementation of the client for low-level config library.
   354  // The implementation uses a FakeReader that stores configuration settings in a map; you can use
   355  // the WithVar or WithProvider methods to set the map values.
   356  func newFakeConfig(ctx context.Context) *fakeConfigClient {
   357  	fakeReader := test.NewFakeReader()
   358  
   359  	client, _ := config.New(ctx, "fake-config", config.InjectReader(fakeReader))
   360  
   361  	return &fakeConfigClient{
   362  		fakeReader:     fakeReader,
   363  		internalclient: client,
   364  	}
   365  }
   366  
   367  type fakeConfigClient struct {
   368  	fakeReader     *test.FakeReader
   369  	internalclient config.Client
   370  }
   371  
   372  var _ config.Client = &fakeConfigClient{}
   373  
   374  func (f fakeConfigClient) CertManager() config.CertManagerClient {
   375  	return f.internalclient.CertManager()
   376  }
   377  
   378  func (f fakeConfigClient) Providers() config.ProvidersClient {
   379  	return f.internalclient.Providers()
   380  }
   381  
   382  func (f fakeConfigClient) Variables() config.VariablesClient {
   383  	return f.internalclient.Variables()
   384  }
   385  
   386  func (f fakeConfigClient) ImageMeta() config.ImageMetaClient {
   387  	return f.internalclient.ImageMeta()
   388  }
   389  
   390  func (f *fakeConfigClient) WithVar(key, value string) *fakeConfigClient {
   391  	f.fakeReader.WithVar(key, value)
   392  	return f
   393  }
   394  
   395  func (f *fakeConfigClient) WithProvider(provider config.Provider) *fakeConfigClient {
   396  	f.fakeReader.WithProvider(provider.Name(), provider.Type(), provider.URL())
   397  	return f
   398  }
   399  
   400  // newFakeRepository return a fake implementation of the client for low-level repository library.
   401  // The implementation stores configuration settings in a map; you can use
   402  // the WithPaths or WithDefaultVersion methods to configure the repository and WithFile to set the map values.
   403  func newFakeRepository(ctx context.Context, provider config.Provider, configClient config.Client) *fakeRepositoryClient {
   404  	fakeRepository := repository.NewMemoryRepository()
   405  
   406  	if configClient == nil {
   407  		configClient = newFakeConfig(ctx)
   408  	}
   409  
   410  	return &fakeRepositoryClient{
   411  		Provider:       provider,
   412  		configClient:   configClient,
   413  		fakeRepository: fakeRepository,
   414  		processor:      yaml.NewSimpleProcessor(),
   415  	}
   416  }
   417  
   418  type fakeRepositoryClient struct {
   419  	config.Provider
   420  	configClient   config.Client
   421  	fakeRepository *repository.MemoryRepository
   422  	processor      yaml.Processor
   423  }
   424  
   425  var _ repository.Client = &fakeRepositoryClient{}
   426  
   427  func (f fakeRepositoryClient) DefaultVersion() string {
   428  	return f.fakeRepository.DefaultVersion()
   429  }
   430  
   431  func (f fakeRepositoryClient) GetVersions(ctx context.Context) ([]string, error) {
   432  	return f.fakeRepository.GetVersions(ctx)
   433  }
   434  
   435  func (f fakeRepositoryClient) Components() repository.ComponentsClient {
   436  	// use a fakeComponentClient (instead of the internal client used in other fake objects) we can de deterministic on what is returned (e.g. avoid interferences from overrides)
   437  	return &fakeComponentClient{
   438  		provider:       f.Provider,
   439  		fakeRepository: f.fakeRepository,
   440  		configClient:   f.configClient,
   441  		processor:      f.processor,
   442  	}
   443  }
   444  
   445  func (f fakeRepositoryClient) Templates(version string) repository.TemplateClient {
   446  	// Use a fakeTemplateClient (instead of the internal client used in other fake objects) we can be deterministic on what is returned (e.g. avoid interferences from overrides)
   447  	return &fakeTemplateClient{
   448  		version:               version,
   449  		fakeRepository:        f.fakeRepository,
   450  		configVariablesClient: f.configClient.Variables(),
   451  		processor:             f.processor,
   452  	}
   453  }
   454  
   455  func (f fakeRepositoryClient) ClusterClasses(version string) repository.ClusterClassClient {
   456  	// Use a fakeTemplateClient (instead of the internal client used in other fake objects) we can be deterministic on what is returned (e.g. avoid interferences from overrides)
   457  	return &fakeClusterClassClient{
   458  		version:               version,
   459  		fakeRepository:        f.fakeRepository,
   460  		configVariablesClient: f.configClient.Variables(),
   461  		processor:             f.processor,
   462  	}
   463  }
   464  
   465  func (f fakeRepositoryClient) Metadata(version string) repository.MetadataClient {
   466  	// Use a fakeMetadataClient (instead of the internal client used in other fake objects) we can be deterministic on what is returned (e.g. avoid interferences from overrides)
   467  	return &fakeMetadataClient{
   468  		version:        version,
   469  		fakeRepository: f.fakeRepository,
   470  	}
   471  }
   472  
   473  func (f *fakeRepositoryClient) WithPaths(rootPath, componentsPath string) *fakeRepositoryClient {
   474  	f.fakeRepository.WithPaths(rootPath, componentsPath)
   475  	return f
   476  }
   477  
   478  func (f *fakeRepositoryClient) WithDefaultVersion(version string) *fakeRepositoryClient {
   479  	f.fakeRepository.WithDefaultVersion(version)
   480  	return f
   481  }
   482  
   483  func (f *fakeRepositoryClient) WithVersions(version ...string) *fakeRepositoryClient {
   484  	f.fakeRepository.WithVersions(version...)
   485  	return f
   486  }
   487  
   488  func (f *fakeRepositoryClient) WithMetadata(version string, metadata *clusterctlv1.Metadata) *fakeRepositoryClient {
   489  	f.fakeRepository.WithMetadata(version, metadata)
   490  	return f
   491  }
   492  
   493  func (f *fakeRepositoryClient) WithFile(version, path string, content []byte) *fakeRepositoryClient {
   494  	f.fakeRepository.WithFile(version, path, content)
   495  	return f
   496  }
   497  
   498  // fakeTemplateClient provides a super simple TemplateClient (e.g. without support for local overrides).
   499  type fakeTemplateClient struct {
   500  	version               string
   501  	fakeRepository        *repository.MemoryRepository
   502  	configVariablesClient config.VariablesClient
   503  	processor             yaml.Processor
   504  }
   505  
   506  func (f *fakeTemplateClient) Get(ctx context.Context, flavor, targetNamespace string, skipTemplateProcess bool) (repository.Template, error) {
   507  	name := "cluster-template"
   508  	if flavor != "" {
   509  		name = fmt.Sprintf("%s-%s", name, flavor)
   510  	}
   511  	name = fmt.Sprintf("%s.yaml", name)
   512  
   513  	content, err := f.fakeRepository.GetFile(ctx, f.version, name)
   514  	if err != nil {
   515  		return nil, err
   516  	}
   517  	return repository.NewTemplate(repository.TemplateInput{
   518  		RawArtifact:           content,
   519  		ConfigVariablesClient: f.configVariablesClient,
   520  		Processor:             f.processor,
   521  		TargetNamespace:       targetNamespace,
   522  		SkipTemplateProcess:   skipTemplateProcess,
   523  	})
   524  }
   525  
   526  // fakeClusterClassClient provides a super simple TemplateClient (e.g. without support for local overrides).
   527  type fakeClusterClassClient struct {
   528  	version               string
   529  	fakeRepository        *repository.MemoryRepository
   530  	configVariablesClient config.VariablesClient
   531  	processor             yaml.Processor
   532  }
   533  
   534  func (f *fakeClusterClassClient) Get(ctx context.Context, class, targetNamespace string, skipTemplateProcess bool) (repository.Template, error) {
   535  	name := fmt.Sprintf("clusterclass-%s.yaml", class)
   536  	content, err := f.fakeRepository.GetFile(ctx, f.version, name)
   537  	if err != nil {
   538  		return nil, err
   539  	}
   540  	return repository.NewTemplate(repository.TemplateInput{
   541  		RawArtifact:           content,
   542  		ConfigVariablesClient: f.configVariablesClient,
   543  		Processor:             f.processor,
   544  		TargetNamespace:       targetNamespace,
   545  		SkipTemplateProcess:   skipTemplateProcess,
   546  	})
   547  }
   548  
   549  // fakeMetadataClient provides a super simple MetadataClient (e.g. without support for local overrides/embedded metadata).
   550  type fakeMetadataClient struct {
   551  	version        string
   552  	fakeRepository *repository.MemoryRepository
   553  }
   554  
   555  func (f *fakeMetadataClient) Get(ctx context.Context) (*clusterctlv1.Metadata, error) {
   556  	content, err := f.fakeRepository.GetFile(ctx, f.version, "metadata.yaml")
   557  	if err != nil {
   558  		return nil, err
   559  	}
   560  	obj := &clusterctlv1.Metadata{}
   561  	codecFactory := serializer.NewCodecFactory(scheme.Scheme)
   562  
   563  	if err := runtime.DecodeInto(codecFactory.UniversalDecoder(), content, obj); err != nil {
   564  		return nil, errors.Wrap(err, "error decoding metadata.yaml")
   565  	}
   566  
   567  	return obj, nil
   568  }
   569  
   570  // fakeComponentClient provides a super simple ComponentClient (e.g. without support for local overrides).
   571  type fakeComponentClient struct {
   572  	provider       config.Provider
   573  	fakeRepository *repository.MemoryRepository
   574  	configClient   config.Client
   575  	processor      yaml.Processor
   576  }
   577  
   578  func (f *fakeComponentClient) Raw(ctx context.Context, options repository.ComponentsOptions) ([]byte, error) {
   579  	return f.getRawBytes(ctx, &options)
   580  }
   581  
   582  func (f *fakeComponentClient) Get(ctx context.Context, options repository.ComponentsOptions) (repository.Components, error) {
   583  	content, err := f.getRawBytes(ctx, &options)
   584  	if err != nil {
   585  		return nil, err
   586  	}
   587  
   588  	return repository.NewComponents(
   589  		repository.ComponentsInput{
   590  			Provider:     f.provider,
   591  			ConfigClient: f.configClient,
   592  			Processor:    f.processor,
   593  			RawYaml:      content,
   594  			Options:      options,
   595  		},
   596  	)
   597  }
   598  
   599  func (f *fakeComponentClient) getRawBytes(ctx context.Context, options *repository.ComponentsOptions) ([]byte, error) {
   600  	if options.Version == "" {
   601  		options.Version = f.fakeRepository.DefaultVersion()
   602  	}
   603  	path := f.fakeRepository.ComponentsPath()
   604  
   605  	return f.fakeRepository.GetFile(ctx, options.Version, path)
   606  }