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 }