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 }