Skip to content

Commit 69d4494

Browse files
authored
Merge pull request #6913 from cwrau/feature/follow-xdg-standard
⚠️ Follow XDG Directory standard for config/data/... files
2 parents a43982d + a0d0c1d commit 69d4494

26 files changed

+211
-138
lines changed

cmd/clusterctl/client/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ type GetClusterTemplateOptions struct {
134134
ClusterName string
135135

136136
// KubernetesVersion to use for the workload cluster. If unspecified, the value from os env variables
137-
// or the .cluster-api/clusterctl.yaml config file will be used.
137+
// or the $XDG_CONFIG_HOME/cluster-api/clusterctl.yaml or .cluster-api/clusterctl.yaml config file will be used.
138138
KubernetesVersion string
139139

140140
// ControlPlaneMachineCount defines the number of control plane machines to be added to the workload cluster.

cmd/clusterctl/client/config/client.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,12 @@ func newConfigClient(path string, options ...Option) (*configClient, error) {
8686
}
8787

8888
// if there is an injected reader, use it, otherwise use a default one
89+
var err error
8990
if client.reader == nil {
90-
client.reader = newViperReader()
91-
if err := client.reader.Init(path); err != nil {
91+
if client.reader, err = newViperReader(); err != nil {
92+
return nil, errors.Wrap(err, "failed to create the configuration reader")
93+
}
94+
if err = client.reader.Init(path); err != nil {
9295
return nil, errors.Wrap(err, "failed to initialize the configuration reader")
9396
}
9497
}

cmd/clusterctl/client/config/reader_viper.go

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,19 @@ import (
2727
"strings"
2828
"time"
2929

30+
"github.com/adrg/xdg"
3031
"github.com/pkg/errors"
3132
"github.com/spf13/viper"
32-
"k8s.io/client-go/util/homedir"
3333

3434
logf "sigs.k8s.io/cluster-api/cmd/clusterctl/log"
3535
)
3636

3737
const (
38-
// ConfigFolder defines the name of the config folder under $home.
38+
// ConfigFolder defines the old name of the config folder under $HOME.
3939
ConfigFolder = ".cluster-api"
40-
// ConfigName defines the name of the config file under ConfigFolder.
40+
// ConfigFolderXDG defines the name of the config folder under $XDG_CONFIG_HOME.
41+
ConfigFolderXDG = "cluster-api"
42+
// ConfigName defines the name of the config file under ConfigFolderXDG.
4143
ConfigName = "clusterctl"
4244
// DownloadConfigFile is the config file when fetching the config from a remote location.
4345
DownloadConfigFile = "clusterctl-download.yaml"
@@ -58,14 +60,18 @@ func injectConfigPaths(configPaths []string) viperReaderOption {
5860
}
5961

6062
// newViperReader returns a viperReader.
61-
func newViperReader(opts ...viperReaderOption) Reader {
63+
func newViperReader(opts ...viperReaderOption) (Reader, error) {
64+
configDirectory, err := xdg.ConfigFile(ConfigFolderXDG)
65+
if err != nil {
66+
return nil, err
67+
}
6268
vr := &viperReader{
63-
configPaths: []string{filepath.Join(homedir.HomeDir(), ConfigFolder)},
69+
configPaths: []string{configDirectory, filepath.Join(xdg.Home, ConfigFolder)},
6470
}
6571
for _, o := range opts {
6672
o(vr)
6773
}
68-
return vr
74+
return vr, nil
6975
}
7076

7177
// Init initialize the viperReader.
@@ -89,15 +95,17 @@ func (v *viperReader) Init(path string) error {
8995

9096
switch {
9197
case url.Scheme == "https" || url.Scheme == "http":
92-
configPath := filepath.Join(homedir.HomeDir(), ConfigFolder)
98+
var configDirectory string
9399
if len(v.configPaths) > 0 {
94-
configPath = v.configPaths[0]
95-
}
96-
if err := os.MkdirAll(configPath, os.ModePerm); err != nil {
97-
return err
100+
configDirectory = v.configPaths[0]
101+
} else {
102+
configDirectory, err = xdg.ConfigFile(ConfigFolderXDG)
103+
if err != nil {
104+
return err
105+
}
98106
}
99107

100-
downloadConfigFile := filepath.Join(configPath, DownloadConfigFile)
108+
downloadConfigFile := filepath.Join(configDirectory, DownloadConfigFile)
101109
err = downloadFile(url.String(), downloadConfigFile)
102110
if err != nil {
103111
return err
@@ -112,14 +120,14 @@ func (v *viperReader) Init(path string) error {
112120
viper.SetConfigFile(path)
113121
}
114122
} else {
115-
// Checks if there is a default .cluster-api/clusterctl{.extension} file in home directory
123+
// Checks if there is a default $XDG_CONFIG_HOME/cluster-api/clusterctl{.extension} or $HOME/.cluster-api/clusterctl{.extension} file
116124
if !v.checkDefaultConfig() {
117125
// since there is no default config to read from, just skip
118126
// reading in config
119127
log.V(5).Info("No default config file available")
120128
return nil
121129
}
122-
// Configure viper for reading .cluster-api/clusterctl{.extension} in home directory
130+
// Configure viper for reading $XDG_CONFIG_HOME/cluster-api/clusterctl{.extension} or $HOME/.cluster-api/clusterctl{.extension} file
123131
viper.SetConfigName(ConfigName)
124132
for _, p := range v.configPaths {
125133
viper.AddConfigPath(p)

cmd/clusterctl/client/config/reader_viper_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ func Test_viperReader_Init(t *testing.T) {
108108
for _, tt := range tests {
109109
t.Run(tt.name, func(t *testing.T) {
110110
gg := NewWithT(t)
111-
v := newViperReader(injectConfigPaths(tt.configDirs))
111+
v, _ := newViperReader(injectConfigPaths(tt.configDirs))
112112
if tt.expectErr {
113113
gg.Expect(v.Init(tt.configPath)).ToNot(Succeed())
114114
return
@@ -168,7 +168,7 @@ func Test_viperReader_Get(t *testing.T) {
168168
t.Run(tt.name, func(t *testing.T) {
169169
gs := NewWithT(t)
170170

171-
v := newViperReader(injectConfigPaths([]string{dir}))
171+
v, _ := newViperReader(injectConfigPaths([]string{dir}))
172172

173173
gs.Expect(v.Init(configFile)).To(Succeed())
174174

@@ -192,7 +192,8 @@ func Test_viperReader_GetWithoutDefaultConfig(t *testing.T) {
192192

193193
_ = os.Setenv("FOO_FOO", "bar")
194194

195-
v := newViperReader(injectConfigPaths([]string{dir}))
195+
v, err := newViperReader(injectConfigPaths([]string{dir}))
196+
g.Expect(err).NotTo(HaveOccurred())
196197
g.Expect(v.Init("")).To(Succeed())
197198

198199
got, err := v.Get("FOO_FOO")

cmd/clusterctl/client/repository/overrides.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ import (
2323
"runtime"
2424
"strings"
2525

26+
"github.com/adrg/xdg"
2627
"github.com/drone/envsubst/v2"
2728
"github.com/pkg/errors"
28-
"k8s.io/client-go/util/homedir"
2929

3030
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/config"
3131
)
@@ -68,7 +68,11 @@ func newOverride(o *newOverrideInput) Overrider {
6868
// Path returns the fully formed path to the file within the specified
6969
// overrides config.
7070
func (o *overrides) Path() (string, error) {
71-
basepath := filepath.Join(homedir.HomeDir(), config.ConfigFolder, overrideFolder)
71+
configDirectory, err := xdg.ConfigFile(config.ConfigFolderXDG)
72+
if err != nil {
73+
return "", err
74+
}
75+
basepath := filepath.Join(configDirectory, overrideFolder)
7276
f, err := o.configVariablesClient.Get(overrideFolderKey)
7377
if err == nil && strings.TrimSpace(f) != "" {
7478
basepath = f

cmd/clusterctl/client/repository/overrides_test.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,18 @@ import (
2121
"path/filepath"
2222
"testing"
2323

24+
"github.com/adrg/xdg"
2425
. "github.com/onsi/gomega"
25-
"k8s.io/client-go/util/homedir"
2626

2727
clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
2828
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/config"
2929
"sigs.k8s.io/cluster-api/cmd/clusterctl/internal/test"
3030
)
3131

3232
func TestOverrides(t *testing.T) {
33+
configDirectory, err := xdg.ConfigFile(config.ConfigFolderXDG)
34+
NewWithT(t).Expect(err).To(BeNil())
35+
3336
tests := []struct {
3437
name string
3538
configVarClient config.VariablesClient
@@ -39,17 +42,17 @@ func TestOverrides(t *testing.T) {
3942
{
4043
name: "returns default overrides path if no config provided",
4144
configVarClient: test.NewFakeVariableClient(),
42-
expectedPath: filepath.Join(homedir.HomeDir(), config.ConfigFolder, overrideFolder, "infrastructure-myinfra", "v1.0.1", "infra-comp.yaml"),
45+
expectedPath: filepath.Join(configDirectory, overrideFolder, "infrastructure-myinfra", "v1.0.1", "infra-comp.yaml"),
4346
},
4447
{
4548
name: "returns default overrides path if config variable is empty",
4649
configVarClient: test.NewFakeVariableClient().WithVar(overrideFolderKey, ""),
47-
expectedPath: filepath.Join(homedir.HomeDir(), config.ConfigFolder, overrideFolder, "infrastructure-myinfra", "v1.0.1", "infra-comp.yaml"),
50+
expectedPath: filepath.Join(configDirectory, overrideFolder, "infrastructure-myinfra", "v1.0.1", "infra-comp.yaml"),
4851
},
4952
{
5053
name: "returns default overrides path if config variable is whitespace",
5154
configVarClient: test.NewFakeVariableClient().WithVar(overrideFolderKey, " "),
52-
expectedPath: filepath.Join(homedir.HomeDir(), config.ConfigFolder, overrideFolder, "infrastructure-myinfra", "v1.0.1", "infra-comp.yaml"),
55+
expectedPath: filepath.Join(configDirectory, overrideFolder, "infrastructure-myinfra", "v1.0.1", "infra-comp.yaml"),
5356
},
5457
{
5558
name: "uses overrides folder from the config variables",
@@ -86,7 +89,9 @@ func TestOverrides(t *testing.T) {
8689
filePath: "infra-comp.yaml",
8790
})
8891

89-
g.Expect(override.Path()).To(Equal(tt.expectedPath))
92+
overridePath, err := override.Path()
93+
g.Expect(err).To(BeNil())
94+
g.Expect(overridePath).To(Equal(tt.expectedPath))
9095
})
9196
}
9297
}

cmd/clusterctl/cmd/config_repositories.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ var configRepositoryCmd = &cobra.Command{
5656
Display the list of providers and their repository configurations.
5757
5858
clusterctl ships with a list of known providers; if necessary, edit
59-
$HOME/.cluster-api/clusterctl.yaml file to add a new provider or to customize existing ones.`),
59+
$XDG_CONFIG_HOME/cluster-api/clusterctl.yaml file to add a new provider or to customize existing ones.`),
6060

6161
Example: Examples(`
6262
# Displays the list of available providers.

cmd/clusterctl/cmd/config_repositories_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ var template = `---
8686
providers:
8787
# add a custom provider
8888
- name: "my-infra-provider"
89-
url: "/home/.cluster-api/overrides/infrastructure-docker/latest/infrastructure-components.yaml"
89+
url: "/home/.config/cluster-api/overrides/infrastructure-docker/latest/infrastructure-components.yaml"
9090
type: "InfrastructureProvider"
9191
# add a custom provider
9292
- name: "another-provider"
@@ -129,7 +129,7 @@ kubekey InfrastructureProvider https://github.com/kubesphere/kubek
129129
kubevirt InfrastructureProvider https://github.com/kubernetes-sigs/cluster-api-provider-kubevirt/releases/latest/ infrastructure-components.yaml
130130
maas InfrastructureProvider https://github.com/spectrocloud/cluster-api-provider-maas/releases/latest/ infrastructure-components.yaml
131131
metal3 InfrastructureProvider https://github.com/metal3-io/cluster-api-provider-metal3/releases/latest/ infrastructure-components.yaml
132-
my-infra-provider InfrastructureProvider /home/.cluster-api/overrides/infrastructure-docker/latest/ infrastructure-components.yaml
132+
my-infra-provider InfrastructureProvider /home/.config/cluster-api/overrides/infrastructure-docker/latest/ infrastructure-components.yaml
133133
nested InfrastructureProvider https://github.com/kubernetes-sigs/cluster-api-provider-nested/releases/latest/ infrastructure-components.yaml
134134
nutanix InfrastructureProvider https://github.com/nutanix-cloud-native/cluster-api-provider-nutanix/releases/latest/ infrastructure-components.yaml
135135
oci InfrastructureProvider https://github.com/oracle/cluster-api-provider-oci/releases/latest/ infrastructure-components.yaml
@@ -251,7 +251,7 @@ var expectedOutputYaml = `- File: core_components.yaml
251251
- File: infrastructure-components.yaml
252252
Name: my-infra-provider
253253
ProviderType: InfrastructureProvider
254-
URL: /home/.cluster-api/overrides/infrastructure-docker/latest/
254+
URL: /home/.config/cluster-api/overrides/infrastructure-docker/latest/
255255
- File: infrastructure-components.yaml
256256
Name: nested
257257
ProviderType: InfrastructureProvider

cmd/clusterctl/cmd/generate_cluster.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ var generateClusterClusterCmd = &cobra.Command{
5555
Generate templates for creating workload clusters.
5656
5757
clusterctl ships with a list of known providers; if necessary, edit
58-
$HOME/.cluster-api/clusterctl.yaml to add new provider or to customize existing ones.
58+
$XDG_CONFIG_HOME/cluster-api/clusterctl.yaml to add new provider or to customize existing ones.
5959
6060
Each provider configuration links to a repository; clusterctl uses this information
6161
to fetch templates when creating a new cluster.`),
@@ -112,7 +112,7 @@ func init() {
112112
generateClusterClusterCmd.Flags().StringVarP(&gc.targetNamespace, "target-namespace", "n", "",
113113
"The namespace to use for the workload cluster. If unspecified, the current namespace will be used.")
114114
generateClusterClusterCmd.Flags().StringVar(&gc.kubernetesVersion, "kubernetes-version", "",
115-
"The Kubernetes version to use for the workload cluster. If unspecified, the value from OS environment variables or the .cluster-api/clusterctl.yaml config file will be used.")
115+
"The Kubernetes version to use for the workload cluster. If unspecified, the value from OS environment variables or the $XDG_CONFIG_HOME/cluster-api/clusterctl.yaml config file will be used.")
116116
generateClusterClusterCmd.Flags().Int64Var(&gc.controlPlaneMachineCount, "control-plane-machine-count", 1,
117117
"The number of control plane machines for the workload cluster.")
118118
generateClusterClusterCmd.Flags().Int64Var(&gc.workerMachineCount, "worker-machine-count", 0,

cmd/clusterctl/cmd/init.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ var initCmd = &cobra.Command{
5656
to have enough privileges to install the desired components.
5757
5858
Use 'clusterctl config repositories' to get a list of available providers; if necessary, edit
59-
$HOME/.cluster-api/clusterctl.yaml file to add new provider or to customize existing ones.
59+
$XDG_CONFIG_HOME/cluster-api/clusterctl.yaml file to add new provider or to customize existing ones.
6060
6161
Some providers require environment variables to be set before running clusterctl init.
6262
Refer to the provider documentation, or use 'clusterctl config provider [name]' to get a list of required variables.

0 commit comments

Comments
 (0)