Skip to content

Commit 64ab1f7

Browse files
committedJan 28, 2025··
Load provider definition dynamically
1 parent cae713c commit 64ab1f7

18 files changed

+305
-120
lines changed
 

‎.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,6 @@ vendor
3131

3232
# mkdocs folder
3333
mkdocs
34+
35+
# Loadable providers
36+
templates/provider/kcm/files/

‎Dockerfile

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ RUN go mod download
3030
COPY cmd/main.go cmd/main.go
3131
COPY api/ api/
3232
COPY internal/ internal/
33+
COPY providers/ providers/
3334

3435
# Build
3536
# the GOARCH has not a default value to allow the binary be built according to the host where the command

‎Makefile

+11-2
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,13 @@ $(IMAGES_PACKAGE_DIR): | $(LOCALBIN)
149149

150150
TEMPLATE_FOLDERS = $(patsubst $(TEMPLATES_DIR)/%,%,$(wildcard $(TEMPLATES_DIR)/*))
151151

152+
.PHONY: load-providers
153+
load-providers:
154+
@mkdir -p $(PROVIDER_TEMPLATES_DIR)/kcm/files/providers
155+
@cp -a providers/*.yml $(PROVIDER_TEMPLATES_DIR)/kcm/files/providers/
156+
152157
.PHONY: helm-package
153-
helm-package: $(CHARTS_PACKAGE_DIR) $(EXTENSION_CHARTS_PACKAGE_DIR) helm
158+
helm-package: $(CHARTS_PACKAGE_DIR) $(EXTENSION_CHARTS_PACKAGE_DIR) helm load-providers
154159
@make $(patsubst %,package-%-tmpl,$(TEMPLATE_FOLDERS))
155160

156161
package-%-tmpl:
@@ -226,7 +231,11 @@ endif
226231
.PHONY: kind-deploy
227232
kind-deploy: kind
228233
@if ! $(KIND) get clusters | grep -q "^$(KIND_CLUSTER_NAME)$$"; then \
229-
$(KIND) create cluster -n $(KIND_CLUSTER_NAME); \
234+
if [ -n "$(KIND_CONFIG_PATH)" ]; then \
235+
$(KIND) create cluster -n $(KIND_CLUSTER_NAME) --config "$(KIND_CONFIG_PATH)"; \
236+
else \
237+
$(KIND) create cluster -n $(KIND_CLUSTER_NAME); \
238+
fi \
230239
fi
231240

232241
.PHONY: kind-undeploy

‎cmd/main.go

+22
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ package main
1717
import (
1818
"crypto/tls"
1919
"flag"
20+
"fmt"
2021
"os"
22+
"strings"
2123

2224
hcv2 "github.com/fluxcd/helm-controller/api/v2"
2325
sourcev1 "github.com/fluxcd/source-controller/api/v1"
@@ -37,8 +39,10 @@ import (
3739
"sigs.k8s.io/controller-runtime/pkg/webhook"
3840

3941
kcmv1 "github.com/K0rdent/kcm/api/v1alpha1"
42+
"github.com/K0rdent/kcm/internal/build"
4043
"github.com/K0rdent/kcm/internal/controller"
4144
"github.com/K0rdent/kcm/internal/helm"
45+
"github.com/K0rdent/kcm/internal/providers"
4246
"github.com/K0rdent/kcm/internal/telemetry"
4347
"github.com/K0rdent/kcm/internal/utils"
4448
kcmwebhook "github.com/K0rdent/kcm/internal/webhook"
@@ -115,6 +119,24 @@ func main() {
115119
Development: true,
116120
}
117121
opts.BindFlags(flag.CommandLine)
122+
123+
flag.Usage = func() {
124+
var defaultUsage strings.Builder
125+
{
126+
oldOutput := flag.CommandLine.Output()
127+
flag.CommandLine.SetOutput(&defaultUsage)
128+
flag.PrintDefaults()
129+
flag.CommandLine.SetOutput(oldOutput)
130+
}
131+
132+
_, _ = fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
133+
_, _ = fmt.Fprint(os.Stderr, defaultUsage.String())
134+
_, _ = fmt.Fprintf(os.Stderr, "\nSupported providers:\n")
135+
for _, el := range providers.List() {
136+
_, _ = fmt.Fprintf(os.Stderr, " - %s\n", el)
137+
}
138+
_, _ = fmt.Fprintf(os.Stderr, "\nVersion: %s\n", build.Version)
139+
}
118140
flag.Parse()
119141

120142
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))

‎config/dev/aks-credentials.yaml

+10
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,13 @@ spec:
2525
kind: Secret
2626
name: azure-aks-credential
2727
namespace: ${NAMESPACE}
28+
---
29+
apiVersion: v1
30+
kind: ConfigMap
31+
metadata:
32+
name: azure-cluster-identity-resource-template
33+
namespace: ${NAMESPACE}
34+
labels:
35+
k0rdent.mirantis.com/component: "kcm"
36+
annotations:
37+
projectsveltos.io/template: "true"

‎config/dev/aws-credentials.yaml

+10
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,13 @@ spec:
3737
kind: AWSClusterStaticIdentity
3838
name: aws-cluster-identity
3939
namespace: ${NAMESPACE}
40+
---
41+
apiVersion: v1
42+
kind: ConfigMap
43+
metadata:
44+
name: aws-cluster-identity-resource-template
45+
namespace: ${NAMESPACE}
46+
labels:
47+
k0rdent.mirantis.com/component: "kcm"
48+
annotations:
49+
projectsveltos.io/template: "true"

‎internal/providers/azure.go ‎internal/projectroot/path.go

+12-20
Original file line numberDiff line numberDiff line change
@@ -12,28 +12,20 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
package providers
15+
package projectroot
1616

1717
import (
18-
"k8s.io/apimachinery/pkg/runtime/schema"
18+
"path/filepath"
19+
"runtime"
1920
)
2021

21-
type ProviderAzure struct{}
22+
// WARNING: This path resolution is dependent on the source file's location
23+
// in the project structure. Moving this file to a different directory will
24+
// change the resolved path. When refactoring or restructuring the project,
25+
// ensure to update this path resolution accordingly.
26+
var (
27+
_, b, _, _ = runtime.Caller(0)
2228

23-
var _ ProviderModule = (*ProviderAzure)(nil)
24-
25-
func init() {
26-
Register(&ProviderAzure{})
27-
}
28-
29-
func (*ProviderAzure) GetName() string {
30-
return "azure"
31-
}
32-
33-
func (*ProviderAzure) GetClusterGVK() schema.GroupVersionKind {
34-
return schema.GroupVersionKind{}
35-
}
36-
37-
func (*ProviderAzure) GetClusterIdentityKinds() []string {
38-
return []string{"AzureClusterIdentity"}
39-
}
29+
// Path is root folder of this project.
30+
Path = filepath.Join(filepath.Dir(b), "../..")
31+
)

‎internal/providers/aws.go

-43
This file was deleted.

‎internal/providers/vsphere.go ‎internal/providers/loader.go

+19-14
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,30 @@
1515
package providers
1616

1717
import (
18-
"k8s.io/apimachinery/pkg/runtime/schema"
19-
)
18+
"cmp"
19+
"fmt"
20+
"os"
21+
"path/filepath"
22+
"testing"
2023

21-
type ProvidervSphere struct{}
24+
"github.com/K0rdent/kcm/internal/projectroot"
25+
)
2226

23-
var _ ProviderModule = (*ProvidervSphere)(nil)
27+
const EnvProvidersPathGlob = "PROVIDERS_PATH_GLOB"
2428

2529
func init() {
26-
Register(&ProvidervSphere{})
27-
}
30+
var baseDir string
2831

29-
func (*ProvidervSphere) GetName() string {
30-
return "vsphere"
31-
}
32+
if testing.Testing() {
33+
baseDir = projectroot.Path
34+
}
3235

33-
func (*ProvidervSphere) GetClusterGVK() schema.GroupVersionKind {
34-
return schema.GroupVersionKind{}
35-
}
36+
providersGlob := cmp.Or(
37+
os.Getenv(EnvProvidersPathGlob),
38+
filepath.Join(baseDir, "providers", "*.yml"),
39+
)
3640

37-
func (*ProvidervSphere) GetClusterIdentityKinds() []string {
38-
return []string{"VSphereClusterIdentity"}
41+
if err := RegisterProvidersFromGlob(providersGlob); err != nil {
42+
panic(fmt.Sprintf("failed to register providers: %v", err))
43+
}
3944
}

‎internal/providers/openstack.go

-39
This file was deleted.

‎internal/providers/yaml.go

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// Copyright 2024
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package providers
16+
17+
import (
18+
"fmt"
19+
"os"
20+
"path/filepath"
21+
"slices"
22+
23+
"gopkg.in/yaml.v3"
24+
"k8s.io/apimachinery/pkg/runtime/schema"
25+
)
26+
27+
// GVK represents the GroupVersionKind structure in YAML.
28+
type GVK struct {
29+
Group string `yaml:"group"`
30+
Version string `yaml:"version"`
31+
Kind string `yaml:"kind"`
32+
}
33+
34+
// YAMLProviderDefinition represents a YAML-based provider configuration.
35+
type YAMLProviderDefinition struct {
36+
Name string `yaml:"name"`
37+
ClusterGVK GVK `yaml:"clusterGVK"`
38+
ClusterIdentityKinds []string `yaml:"clusterIdentityKinds"`
39+
}
40+
41+
var _ ProviderModule = (*YAMLProviderDefinition)(nil)
42+
43+
func (p *YAMLProviderDefinition) GetName() string {
44+
return p.Name
45+
}
46+
47+
func (p *YAMLProviderDefinition) GetClusterGVK() schema.GroupVersionKind {
48+
return schema.GroupVersionKind{
49+
Group: p.ClusterGVK.Group,
50+
Version: p.ClusterGVK.Version,
51+
Kind: p.ClusterGVK.Kind,
52+
}
53+
}
54+
55+
func (p *YAMLProviderDefinition) GetClusterIdentityKinds() []string {
56+
return slices.Clone(p.ClusterIdentityKinds)
57+
}
58+
59+
// RegisterFromYAML registers a provider from a YAML file.
60+
func RegisterFromYAML(yamlFile string) error {
61+
data, err := os.ReadFile(yamlFile)
62+
if err != nil {
63+
return fmt.Errorf("failed to read YAML file: %w", err)
64+
}
65+
66+
var ypd YAMLProviderDefinition
67+
68+
if err := yaml.Unmarshal(data, &ypd); err != nil {
69+
return fmt.Errorf("failed to unmarshal YAML: %w", err)
70+
}
71+
72+
Register(&ypd)
73+
74+
return nil
75+
}
76+
77+
// RegisterProvidersFromGlob loads and registers provider YAML files matching the glob pattern.
78+
func RegisterProvidersFromGlob(pattern string) error {
79+
matches, err := filepath.Glob(pattern)
80+
if err != nil {
81+
return fmt.Errorf("failed to glob pattern %q: %w", pattern, err)
82+
}
83+
84+
for _, file := range matches {
85+
if err := RegisterFromYAML(file); err != nil {
86+
return fmt.Errorf("provider %s: %w", filepath.Base(file), err)
87+
}
88+
}
89+
90+
return nil
91+
}

0 commit comments

Comments
 (0)