sigs.k8s.io/cluster-api@v1.7.1/cmd/clusterctl/client/repository/repository_memory.go (about)

     1  /*
     2  Copyright 2021 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  
    23  	"github.com/pkg/errors"
    24  	"k8s.io/apimachinery/pkg/runtime"
    25  	"k8s.io/apimachinery/pkg/runtime/serializer"
    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/internal/scheme"
    30  )
    31  
    32  // MemoryRepository contains an instance of the repository data.
    33  type MemoryRepository struct {
    34  	defaultVersion string
    35  	rootPath       string
    36  	componentsPath string
    37  	versions       map[string]bool
    38  	files          map[string][]byte
    39  }
    40  
    41  var _ Repository = &MemoryRepository{}
    42  
    43  // NewMemoryRepository returns a new MemoryRepository instance.
    44  func NewMemoryRepository() *MemoryRepository {
    45  	return &MemoryRepository{
    46  		versions: map[string]bool{},
    47  		files:    map[string][]byte{},
    48  	}
    49  }
    50  
    51  // DefaultVersion returns the default version for this repository.
    52  // NOTE: The DefaultVersion is a required info usually derived from the repository configuration,
    53  // and it is used whenever the users gets files from the repository without providing a specific version.
    54  func (f *MemoryRepository) DefaultVersion() string {
    55  	if f.defaultVersion == "" {
    56  		return latestVersionTag
    57  	}
    58  	return f.defaultVersion
    59  }
    60  
    61  // RootPath returns the RootPath for this repository.
    62  // NOTE: The RootPath is a required info usually derived from the repository configuration,
    63  // and it is used to map the file path to the internal repository structure.
    64  func (f *MemoryRepository) RootPath() string {
    65  	return f.rootPath
    66  }
    67  
    68  // ComponentsPath returns ComponentsPath for this repository
    69  // NOTE: The ComponentsPath is a required info usually derived from the repository configuration,
    70  // and it is used to identify the components yaml for the provider.
    71  func (f *MemoryRepository) ComponentsPath() string {
    72  	return f.componentsPath
    73  }
    74  
    75  // GetFile returns a file for a given provider version.
    76  // NOTE: If the provided version is missing, the default version is used.
    77  func (f *MemoryRepository) GetFile(ctx context.Context, version string, path string) ([]byte, error) {
    78  	if version == "" {
    79  		version = f.DefaultVersion()
    80  	}
    81  	if version == latestVersionTag {
    82  		var err error
    83  		version, err = latestContractRelease(ctx, f, clusterv1.GroupVersion.Version)
    84  		if err != nil {
    85  			return nil, err
    86  		}
    87  	}
    88  	if _, ok := f.versions[version]; !ok {
    89  		return nil, errors.Errorf("unable to get files for version %s", version)
    90  	}
    91  
    92  	for p, c := range f.files {
    93  		if p == vpath(version, path) {
    94  			return c, nil
    95  		}
    96  	}
    97  	return nil, errors.Errorf("unable to get file %s for version %s", path, version)
    98  }
    99  
   100  // GetVersions returns the list of versions that are available.
   101  func (f *MemoryRepository) GetVersions(_ context.Context) ([]string, error) {
   102  	v := make([]string, 0, len(f.versions))
   103  	for k := range f.versions {
   104  		v = append(v, k)
   105  	}
   106  	return v, nil
   107  }
   108  
   109  // WithPaths allows setting of the rootPath and componentsPath fields.
   110  func (f *MemoryRepository) WithPaths(rootPath, componentsPath string) *MemoryRepository {
   111  	f.rootPath = rootPath
   112  	f.componentsPath = componentsPath
   113  	return f
   114  }
   115  
   116  // WithVersions allows setting of the available versions.
   117  // NOTE: When adding a file to the repository for a specific version, a version
   118  // is automatically generated if missing; this func allows to define versions without any file.
   119  func (f *MemoryRepository) WithVersions(version ...string) *MemoryRepository {
   120  	for _, v := range version {
   121  		f.versions[v] = true
   122  	}
   123  	return f
   124  }
   125  
   126  // WithDefaultVersion allows setting of the default version.
   127  func (f *MemoryRepository) WithDefaultVersion(version string) *MemoryRepository {
   128  	f.defaultVersion = version
   129  	return f
   130  }
   131  
   132  // WithFile allows setting of a file for a given version.
   133  // NOTE:
   134  // - If the provided version is missing, a new one will be generated automatically.
   135  // - If the defaultVersion has not been set, it will be initialized with the first version passed in WithFile().
   136  // - If the version is "latest" or "", nothing will be added.
   137  func (f *MemoryRepository) WithFile(version, path string, content []byte) *MemoryRepository {
   138  	if version == latestVersionTag || version == "" {
   139  		return f
   140  	}
   141  
   142  	f.versions[version] = true
   143  	f.files[vpath(version, path)] = content
   144  
   145  	if f.defaultVersion == "" {
   146  		f.defaultVersion = version
   147  	}
   148  	return f
   149  }
   150  
   151  // WithMetadata allows setting of the metadata.
   152  func (f *MemoryRepository) WithMetadata(version string, metadata *clusterctlv1.Metadata) *MemoryRepository {
   153  	codecs := serializer.NewCodecFactory(scheme.Scheme)
   154  
   155  	mediaType := "application/yaml"
   156  	info, match := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), mediaType)
   157  	if !match {
   158  		panic("failed to get SerializerInfo for application/yaml")
   159  	}
   160  
   161  	metadata.SetGroupVersionKind(clusterctlv1.GroupVersion.WithKind("Metadata"))
   162  
   163  	encoder := codecs.EncoderForVersion(info.Serializer, metadata.GroupVersionKind().GroupVersion())
   164  	data, err := runtime.Encode(encoder, metadata)
   165  	if err != nil {
   166  		panic(err)
   167  	}
   168  
   169  	return f.WithFile(version, "metadata.yaml", data)
   170  }
   171  
   172  func vpath(version string, path string) string {
   173  	return fmt.Sprintf("%s/%s", version, path)
   174  }