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