sigs.k8s.io/cluster-api@v1.7.1/cmd/clusterctl/client/repository/overrides.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  	"net/url"
    21  	"os"
    22  	"path/filepath"
    23  	"runtime"
    24  	"strings"
    25  
    26  	"github.com/adrg/xdg"
    27  	"github.com/drone/envsubst/v2"
    28  	"github.com/pkg/errors"
    29  
    30  	"sigs.k8s.io/cluster-api/cmd/clusterctl/client/config"
    31  	logf "sigs.k8s.io/cluster-api/cmd/clusterctl/log"
    32  )
    33  
    34  const (
    35  	overrideFolder    = "overrides"
    36  	overrideFolderKey = "overridesFolder"
    37  )
    38  
    39  // Overrider provides behavior to determine the overrides layer.
    40  type Overrider interface {
    41  	Path() (string, error)
    42  }
    43  
    44  // overrides implements the Overrider interface.
    45  type overrides struct {
    46  	configVariablesClient config.VariablesClient
    47  	providerLabel         string
    48  	version               string
    49  	filePath              string
    50  }
    51  
    52  type newOverrideInput struct {
    53  	configVariablesClient config.VariablesClient
    54  	provider              config.Provider
    55  	version               string
    56  	filePath              string
    57  }
    58  
    59  // newOverride returns an Overrider.
    60  func newOverride(o *newOverrideInput) Overrider {
    61  	return &overrides{
    62  		configVariablesClient: o.configVariablesClient,
    63  		providerLabel:         o.provider.ManifestLabel(),
    64  		version:               o.version,
    65  		filePath:              o.filePath,
    66  	}
    67  }
    68  
    69  // Path returns the fully formed path to the file within the specified
    70  // overrides config.
    71  func (o *overrides) Path() (string, error) {
    72  	configDirectory, err := xdg.ConfigFile(config.ConfigFolderXDG)
    73  	if err != nil {
    74  		return "", err
    75  	}
    76  	basepath := filepath.Join(configDirectory, overrideFolder)
    77  	f, err := o.configVariablesClient.Get(overrideFolderKey)
    78  	if err == nil && strings.TrimSpace(f) != "" {
    79  		basepath = f
    80  
    81  		basepath, err = envsubst.Eval(basepath, os.Getenv)
    82  		if err != nil {
    83  			return "", errors.Wrapf(err, "unable to evaluate %s: %q", overrideFolderKey, basepath)
    84  		}
    85  
    86  		if runtime.GOOS == "windows" {
    87  			parsedBasePath, err := url.Parse(basepath)
    88  			if err != nil {
    89  				return "", errors.Wrapf(err, "unable to parse %s: %q", overrideFolderKey, basepath)
    90  			}
    91  
    92  			// in case of windows, we should take care of removing the additional / which is required by the URI standard
    93  			// for windows local paths. see https://blogs.msdn.microsoft.com/ie/2006/12/06/file-uris-in-windows/ for more details.
    94  			// Encoded file paths are not required in Windows 10 versions <1803 and are unsupported in Windows 10 >=1803
    95  			// https://support.microsoft.com/en-us/help/4467268/url-encoded-unc-paths-not-url-decoded-in-windows-10-version-1803-later
    96  			basepath = filepath.FromSlash(strings.TrimPrefix(parsedBasePath.Path, "/"))
    97  		}
    98  	}
    99  
   100  	return filepath.Join(
   101  		basepath,
   102  		o.providerLabel,
   103  		o.version,
   104  		o.filePath,
   105  	), nil
   106  }
   107  
   108  // getLocalOverride return local override file from the config folder, if it exists.
   109  // This is required for development purposes, but it can be used also in production as a workaround for problems on the official repositories.
   110  func getLocalOverride(info *newOverrideInput) ([]byte, error) {
   111  	log := logf.Log
   112  
   113  	overridePath, err := newOverride(info).Path()
   114  	log.V(5).Info("Potential override file", "SearchFile", overridePath, "Provider", info.provider.ManifestLabel(), "Version", info.version)
   115  
   116  	if err != nil {
   117  		return nil, err
   118  	}
   119  
   120  	// it the local override exists, use it
   121  	_, err = os.Stat(overridePath)
   122  	if err == nil {
   123  		content, err := os.ReadFile(overridePath) //nolint:gosec
   124  		if err != nil {
   125  			return nil, errors.Wrapf(err, "failed to read local override for %s", overridePath)
   126  		}
   127  		return content, nil
   128  	}
   129  
   130  	// it the local override does not exists, return (so files from the provider's repository could be used)
   131  	if os.IsNotExist(err) {
   132  		return nil, nil
   133  	}
   134  
   135  	// blocks for any other error
   136  	return nil, err
   137  }