Skip to content

Commit 9b9847e

Browse files
committed
Feature: karmadactl init supports deployment through configuration files
Signed-off-by: tiansuo114 <[email protected]> fix lint Signed-off-by: tiansuo114 <[email protected]>
1 parent f7d6da3 commit 9b9847e

File tree

11 files changed

+1467
-5
lines changed

11 files changed

+1467
-5
lines changed

.github/workflows/cli.yaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ jobs:
3636
uses: actions/setup-go@v5
3737
with:
3838
go-version-file: go.mod
39-
4039
- name: run karmadactl init test
4140
run: |
4241
export CLUSTER_VERSION=kindest/node:${{ matrix.k8s }}
@@ -48,7 +47,7 @@ jobs:
4847
export KUBECONFIG=${HOME}/karmada/karmada-apiserver.config
4948
GO111MODULE=on go install github.com/onsi/ginkgo/v2/ginkgo
5049
ginkgo -v --race --trace -p --focus="[BasicPropagation] propagation testing deployment propagation testing" ./test/e2e/
51-
- name: export logs
50+
- name: export logs
5251
if: always()
5352
run: |
5453
export ARTIFACTS_PATH=${{ github.workspace }}/karmadactl-test-logs/${{ matrix.k8s }}/

hack/verify-license.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ missing_license_header_files="$($ADDLICENSE_BIN \
4040
-ignore "**/*.yml" \
4141
-ignore "**/*.json" \
4242
-ignore ".idea/**" \
43+
-ignore ".git/**"
4344
.)" || true
4445

4546
if [[ "$missing_license_header_files" ]]; then

pkg/karmadactl/cmdinit/cmdinit.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,10 @@ var (
7676
%[1]s init --karmada-apiserver-replicas 3 --etcd-replicas 3 --etcd-storage-mode PVC --storage-classes-name {StorageClassesName}
7777
7878
# Specify external IPs(load balancer or HA IP) which used to sign the certificate
79-
%[1]s init --cert-external-ip 10.235.1.2 --cert-external-dns www.karmada.io`)
79+
%[1]s init --cert-external-ip 10.235.1.2 --cert-external-dns www.karmada.io
80+
81+
# Install Karmada using a configuration file
82+
%[1]s init --config /path/to/your/config/file.yaml`)
8083
)
8184

8285
// NewCmdInit install Karmada on Kubernetes
@@ -149,6 +152,7 @@ func NewCmdInit(parentCommand string) *cobra.Command {
149152
flags.StringVar(&opts.ExternalEtcdKeyPrefix, "external-etcd-key-prefix", "", "The key prefix to be configured to kube-apiserver through --etcd-prefix.")
150153
// karmada
151154
flags.StringVar(&opts.CRDs, "crds", kubernetes.DefaultCrdURL, "Karmada crds resource.(local file e.g. --crds /root/crds.tar.gz)")
155+
flags.StringVar(&opts.KarmadaInitFilePath, "config", "", "Karmada init file path")
152156
flags.StringVarP(&opts.KarmadaAPIServerAdvertiseAddress, "karmada-apiserver-advertise-address", "", "", "The IP address the Karmada API Server will advertise it's listening on. If not set, the address on the master node will be used.")
153157
flags.Int32VarP(&opts.KarmadaAPIServerNodePort, "port", "p", 32443, "Karmada apiserver service node port")
154158
flags.StringVarP(&opts.KarmadaDataPath, "karmada-data", "d", "/etc/karmada", "Karmada data path. kubeconfig cert and crds files")
@@ -166,6 +170,7 @@ func NewCmdInit(parentCommand string) *cobra.Command {
166170
flags.StringVarP(&opts.KarmadaAggregatedAPIServerImage, "karmada-aggregated-apiserver-image", "", kubernetes.DefaultKarmadaAggregatedAPIServerImage, "Karmada aggregated apiserver image")
167171
flags.Int32VarP(&opts.KarmadaAggregatedAPIServerReplicas, "karmada-aggregated-apiserver-replicas", "", 1, "Karmada aggregated apiserver replica set")
168172
flags.IntVarP(&opts.WaitComponentReadyTimeout, "wait-component-ready-timeout", "", cmdinitoptions.WaitComponentReadyTimeout, "Wait for karmada component ready timeout. 0 means wait forever")
173+
169174
return cmd
170175
}
171176

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
Copyright 2024 The Karmada 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 config
18+
19+
import (
20+
"fmt"
21+
"os"
22+
"sort"
23+
24+
"k8s.io/apimachinery/pkg/runtime/schema"
25+
yamlserializer "k8s.io/apimachinery/pkg/runtime/serializer/yaml"
26+
"k8s.io/apimachinery/pkg/util/yaml"
27+
"k8s.io/klog/v2"
28+
)
29+
30+
// LoadInitConfiguration loads the InitConfiguration from the specified file path.
31+
// It delegates the actual loading to the loadInitConfigurationFromFile function.
32+
func LoadInitConfiguration(cfgPath string) (*KarmadaInitConfig, error) {
33+
var config *KarmadaInitConfig
34+
var err error
35+
36+
config, err = loadInitConfigurationFromFile(cfgPath)
37+
38+
return config, err
39+
}
40+
41+
// loadInitConfigurationFromFile reads the file at the specified path and converts it into an InitConfiguration.
42+
// It reads the file contents and then converts the bytes to an InitConfiguration.
43+
func loadInitConfigurationFromFile(cfgPath string) (*KarmadaInitConfig, error) {
44+
klog.V(1).Infof("loading configuration from %q", cfgPath)
45+
46+
b, err := os.ReadFile(cfgPath)
47+
if err != nil {
48+
return nil, fmt.Errorf("unable to read config from %q: %v", cfgPath, err)
49+
}
50+
gvkmap, err := ParseGVKYamlMap(b)
51+
if err != nil {
52+
return nil, err
53+
}
54+
55+
return documentMapToInitConfiguration(gvkmap)
56+
}
57+
58+
// ParseGVKYamlMap parses a single YAML document into a map of GroupVersionKind to byte slices.
59+
// This function is a simplified version that handles only a single YAML document.
60+
func ParseGVKYamlMap(yamlBytes []byte) (map[schema.GroupVersionKind][]byte, error) {
61+
gvkmap := make(map[schema.GroupVersionKind][]byte)
62+
63+
gvk, err := yamlserializer.DefaultMetaFactory.Interpret(yamlBytes)
64+
if err != nil {
65+
return nil, fmt.Errorf("failed to interpret YAML document: %w", err)
66+
}
67+
if len(gvk.Group) == 0 || len(gvk.Version) == 0 || len(gvk.Kind) == 0 {
68+
return nil, fmt.Errorf("invalid configuration for GroupVersionKind %+v: kind and apiVersion is mandatory information that must be specified", gvk)
69+
}
70+
gvkmap[*gvk] = yamlBytes
71+
72+
return gvkmap, nil
73+
}
74+
75+
// documentMapToInitConfiguration processes a map of GroupVersionKind to byte slices to extract the InitConfiguration.
76+
// It iterates over the map, checking for the "InitConfiguration" kind, group, and version, and unmarshals its content into an InitConfiguration object.
77+
func documentMapToInitConfiguration(gvkmap map[schema.GroupVersionKind][]byte) (*KarmadaInitConfig, error) {
78+
var initcfg *KarmadaInitConfig
79+
80+
gvks := make([]schema.GroupVersionKind, 0, len(gvkmap))
81+
for gvk := range gvkmap {
82+
gvks = append(gvks, gvk)
83+
}
84+
sort.Slice(gvks, func(i, j int) bool {
85+
return gvks[i].String() < gvks[j].String()
86+
})
87+
88+
for _, gvk := range gvks {
89+
fileContent := gvkmap[gvk]
90+
if gvk.Kind == "KarmadaInitConfig" {
91+
if gvk.Group != GroupName || gvk.Version != SchemeGroupVersion.Version {
92+
return nil, fmt.Errorf("invalid Group or Version: expected group %q and version %q, but got group %q and version %q", GroupName, SchemeGroupVersion.Version, gvk.Group, gvk.Version)
93+
}
94+
initcfg = &KarmadaInitConfig{}
95+
if err := yaml.Unmarshal(fileContent, initcfg); err != nil {
96+
return nil, err
97+
}
98+
}
99+
}
100+
101+
if initcfg == nil {
102+
return nil, fmt.Errorf("no KarmadaInitConfig kind was found in the YAML file")
103+
}
104+
105+
return initcfg, nil
106+
}

0 commit comments

Comments
 (0)