diff --git a/cmd/clusterctl/pkg/client/config/reader_viper.go b/cmd/clusterctl/pkg/client/config/reader_viper.go index b5e16f1d2d76..a59096e71cb6 100644 --- a/cmd/clusterctl/pkg/client/config/reader_viper.go +++ b/cmd/clusterctl/pkg/client/config/reader_viper.go @@ -26,6 +26,9 @@ import ( "k8s.io/klog" ) +// ConfigFolder defines the name of the config folder under $home +const ConfigFolder = "cluster-api" + // viperReader implements Reader using viper as backend for reading from environment variables // and from a clusterctl config file. type viperReader struct { @@ -44,7 +47,7 @@ func (v *viperReader) Init(path string) error { } else { // Configure for searching cluster-api/.clusterctl{.extension} in home directory viper.SetConfigName(".clusterctl") - viper.AddConfigPath(filepath.Join(homedir.HomeDir(), "cluster-api")) + viper.AddConfigPath(filepath.Join(homedir.HomeDir(), ConfigFolder)) } // Configure for reading environment variables as well, and more specifically: diff --git a/cmd/clusterctl/pkg/client/repository/client.go b/cmd/clusterctl/pkg/client/repository/client.go index 752f417e5d0c..3253ac382a24 100644 --- a/cmd/clusterctl/pkg/client/repository/client.go +++ b/cmd/clusterctl/pkg/client/repository/client.go @@ -17,9 +17,13 @@ limitations under the License. package repository import ( + "io/ioutil" "net/url" + "os" + "path/filepath" "github.com/pkg/errors" + "k8s.io/client-go/util/homedir" "sigs.k8s.io/cluster-api/cmd/clusterctl/pkg/client/config" "sigs.k8s.io/cluster-api/cmd/clusterctl/pkg/internal/test" ) @@ -151,3 +155,32 @@ func repositoryFactory(providerConfig config.Provider, configVariablesClient con return nil, errors.Errorf("invalid provider url. there are no provider implementation for %q schema", rURL.Scheme) } + +const overrideFolder = "overrides" + +// getLocalOverride return local override file from the config folder, if it exists. +// This is required for development purposes, but it can be used also in production as a workaround for problems on the official repositories +func getLocalOverride(provider config.Provider, version, path string) ([]byte, error) { + + // local override files are searched at $home/cluster-api/overrides/// + homeFolder := filepath.Join(homedir.HomeDir(), config.ConfigFolder) + overridePath := filepath.Join(homeFolder, overrideFolder, provider.Name(), version, path) + + // it the local override exists, use it + _, err := os.Stat(overridePath) + if err == nil { + content, err := ioutil.ReadFile(overridePath) + if err != nil { + return nil, errors.Wrapf(err, "failed to read local override for %s/%s/%s", provider.Name(), version, path) + } + return content, nil + } + + // it the local override does not exists, return (so files from the provider's repository could be used) + if os.IsNotExist(err) { + return nil, nil + } + + // blocks for any other error + return nil, err +} diff --git a/cmd/clusterctl/pkg/client/repository/components_client.go b/cmd/clusterctl/pkg/client/repository/components_client.go index dc95bd573c7d..c2ae7f258037 100644 --- a/cmd/clusterctl/pkg/client/repository/components_client.go +++ b/cmd/clusterctl/pkg/client/repository/components_client.go @@ -55,11 +55,17 @@ func (f *componentsClient) Get(version, targetNamespace, watchingNamespace strin // retrieve the path where the path is stored path := f.repository.ComponentsPath() - // read from the components path. - // If file is a path, the entire content of the path is returned. - file, err := f.repository.GetFile(version, path) + // read the component YAML, reading the local override file if it exists, otherwise read from the provider repository + file, err := getLocalOverride(f.provider, version, path) if err != nil { - return nil, errors.Wrapf(err, "failed to read %q from the repository for provider %q", path, f.provider.Name()) + return nil, err + } + + if file == nil { + file, err = f.repository.GetFile(version, path) + if err != nil { + return nil, errors.Wrapf(err, "failed to read %q from provider's repository %q", path, f.provider.Name()) + } } return newComponents(f.provider, version, file, f.configVariablesClient, targetNamespace, watchingNamespace)