Skip to content

Commit

Permalink
Support multiple label selection ability in EtcdNodeSelectorLabels
Browse files Browse the repository at this point in the history
Signed-off-by: tiansuo114 <[email protected]>

fix

Signed-off-by: tiansuo114 <[email protected]>

add test

Signed-off-by: tiansuo114 <[email protected]>

11

Signed-off-by: tiansuo114 <[email protected]>

fix

Signed-off-by: tiansuo114 <[email protected]>

fix

Signed-off-by: tiansuo114 <[email protected]>

fix

Signed-off-by: tiansuo114 <[email protected]>

fix

Signed-off-by: tiansuo114 <[email protected]>

fix

Signed-off-by: tiansuo114 <[email protected]>

Add executable permission to hack/cli-testing-init-with-config.sh

Signed-off-by: tiansuo114 <[email protected]>
  • Loading branch information
tiansuo114 committed Oct 16, 2024
1 parent 6e41d9b commit 44a25dc
Show file tree
Hide file tree
Showing 13 changed files with 1,503 additions and 4 deletions.
8 changes: 7 additions & 1 deletion .github/workflows/cli.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ jobs:
with:
go-version-file: go.mod

- name: run karmadactl init use config file test
run: |
export CLUSTER_VERSION=kindest/node:${{ matrix.k8s }}
# Run custom test for workload configuration deployment
hack/cli-testing-init-with-config.sh
- name: run karmadactl init test
run: |
export CLUSTER_VERSION=kindest/node:${{ matrix.k8s }}
Expand All @@ -48,7 +54,7 @@ jobs:
export KUBECONFIG=${HOME}/karmada/karmada-apiserver.config
GO111MODULE=on go install github.com/onsi/ginkgo/v2/ginkgo
ginkgo -v --race --trace -p --focus="[BasicPropagation] propagation testing deployment propagation testing" ./test/e2e/
- name: export logs
- name: export logs
if: always()
run: |
export ARTIFACTS_PATH=${{ github.workspace }}/karmadactl-test-logs/${{ matrix.k8s }}/
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ require (
github.com/onsi/ginkgo/v2 v2.17.2
github.com/onsi/gomega v1.33.1
github.com/opensearch-project/opensearch-go v1.1.0
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.18.0
github.com/spf13/cobra v1.8.0
github.com/spf13/pflag v1.0.5
Expand Down Expand Up @@ -132,7 +133,6 @@ require (
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.46.0 // indirect
Expand Down
157 changes: 157 additions & 0 deletions hack/cli-testing-init-with-config.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
#!/usr/bin/env bash
# Copyright 2024 The Karmada Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

set -o errexit
set -o nounset
set -o pipefail

# This script starts a local karmada control plane with karmadactl and with a certain number of clusters joined.
# This script depends on utils in: ${REPO_ROOT}/hack/util.sh
# 1. used by developer to setup develop environment quickly.
# 2. used by e2e testing to setup test environment automatically.

REPO_ROOT=$(dirname "${BASH_SOURCE[0]}")/..
source "${REPO_ROOT}"/hack/util.sh

# variable define
KUBECONFIG_PATH=${KUBECONFIG_PATH:-"${HOME}/.kube"}
HOST_CLUSTER_NAME=${HOST_CLUSTER_NAME:-"karmada-config-host"}
MEMBER_CLUSTER_1_NAME=${MEMBER_CLUSTER_1_NAME:-"config-member1"}
MEMBER_CLUSTER_2_NAME=${MEMBER_CLUSTER_2_NAME:-"config-member2"}
CLUSTER_VERSION=${CLUSTER_VERSION:-"${DEFAULT_CLUSTER_VERSION}"}
BUILD_PATH=${BUILD_PATH:-"_output/bin/linux/amd64"}
CONFIG_FILE_PATH=${CONFIG_FILE_PATH:-"/tmp/karmada-config.yaml"}

# install kind and kubectl
kind_version=v0.22.0
echo -n "Preparing: 'kind' existence check - "
if util::cmd_exist kind; then
echo "passed"
else
echo "not pass"
util::install_tools "sigs.k8s.io/kind" $kind_version
fi
# get arch name and os name in bootstrap
BS_ARCH=$(go env GOARCH)
BS_OS=$(go env GOOS)
# check arch and os name before installing
util::install_environment_check "${BS_ARCH}" "${BS_OS}"
echo -n "Preparing: 'kubectl' existence check - "
if util::cmd_exist kubectl; then
echo "passed"
else
echo "not pass"
util::install_kubectl "" "${BS_ARCH}" "${BS_OS}"
fi

# prepare the newest crds
echo "Prepare the newest crds"
cd charts/karmada/
cp -r _crds crds
tar -zcvf ../../crds.tar.gz crds
cd -

# make images
export VERSION="latest"
export REGISTRY="docker.io/karmada"
make images GOOS="linux" --directory="${REPO_ROOT}"

# make karmadactl binary
make karmadactl

# create host/member1/member2 cluster
echo "Start create clusters..."
hack/create-cluster.sh ${HOST_CLUSTER_NAME} ${KUBECONFIG_PATH}/${HOST_CLUSTER_NAME}.config > /dev/null 2>&1 &
hack/create-cluster.sh ${MEMBER_CLUSTER_1_NAME} ${KUBECONFIG_PATH}/${MEMBER_CLUSTER_1_NAME}.config > /dev/null 2>&1 &
hack/create-cluster.sh ${MEMBER_CLUSTER_2_NAME} ${KUBECONFIG_PATH}/${MEMBER_CLUSTER_2_NAME}.config > /dev/null 2>&1 &

# wait cluster ready
echo "Wait clusters ready..."
util::wait_file_exist ${KUBECONFIG_PATH}/${HOST_CLUSTER_NAME}.config 300
util::wait_context_exist ${HOST_CLUSTER_NAME} ${KUBECONFIG_PATH}/${HOST_CLUSTER_NAME}.config 300
kubectl wait --for=condition=Ready nodes --all --timeout=800s --kubeconfig=${KUBECONFIG_PATH}/${HOST_CLUSTER_NAME}.config
util::wait_nodes_taint_disappear 800 ${KUBECONFIG_PATH}/${HOST_CLUSTER_NAME}.config

util::wait_file_exist ${KUBECONFIG_PATH}/${MEMBER_CLUSTER_1_NAME}.config 300
util::wait_context_exist "${MEMBER_CLUSTER_1_NAME}" ${KUBECONFIG_PATH}/${MEMBER_CLUSTER_1_NAME}.config 300
kubectl wait --for=condition=Ready nodes --all --timeout=800s --kubeconfig=${KUBECONFIG_PATH}/${MEMBER_CLUSTER_1_NAME}.config
util::wait_nodes_taint_disappear 800 ${KUBECONFIG_PATH}/${MEMBER_CLUSTER_1_NAME}.config

util::wait_file_exist ${KUBECONFIG_PATH}/${MEMBER_CLUSTER_2_NAME}.config 300
util::wait_context_exist "${MEMBER_CLUSTER_2_NAME}" ${KUBECONFIG_PATH}/${MEMBER_CLUSTER_2_NAME}.config 300
kubectl wait --for=condition=Ready nodes --all --timeout=800s --kubeconfig=${KUBECONFIG_PATH}/${MEMBER_CLUSTER_2_NAME}.config
util::wait_nodes_taint_disappear 800 ${KUBECONFIG_PATH}/${MEMBER_CLUSTER_2_NAME}.config

# load components images to kind cluster
kind load docker-image "${REGISTRY}/karmada-controller-manager:${VERSION}" --name="${HOST_CLUSTER_NAME}"
kind load docker-image "${REGISTRY}/karmada-scheduler:${VERSION}" --name="${HOST_CLUSTER_NAME}"
kind load docker-image "${REGISTRY}/karmada-webhook:${VERSION}" --name="${HOST_CLUSTER_NAME}"
kind load docker-image "${REGISTRY}/karmada-aggregated-apiserver:${VERSION}" --name="${HOST_CLUSTER_NAME}"

# Ensure the parent directory of CONFIG_FILE_PATH exists
CONFIG_DIR=$(dirname "${CONFIG_FILE_PATH}")
if [ ! -d "${CONFIG_DIR}" ]; then
echo "Creating directory ${CONFIG_DIR}..."
mkdir -p "${CONFIG_DIR}"
fi

# build Karmada init configuration file
CONFIG_TEMPLATE=$(cat <<EOF
apiVersion: config.karmada.io/v1alpha1
kind: KarmadaInitConfig
metadata:
name: karmada-init
spec:
hostCluster:
kubeconfig: "${KUBECONFIG_PATH}/${HOST_CLUSTER_NAME}.config" # kubeconfig 路径
components:
karmadaControllerManager:
imageRepository: "${REGISTRY}/karmada-controller-manager" # karmada-controller-manager 镜像仓库
imageTag: "${VERSION}" # 镜像版本
karmadaScheduler:
imageRepository: "${REGISTRY}/karmada-scheduler" # karmada-scheduler 镜像仓库
imageTag: "${VERSION}" # 镜像版本
karmadaWebhook:
imageRepository: "${REGISTRY}/karmada-webhook" # karmada-webhook 镜像仓库
imageTag: "${VERSION}" # 镜像版本
karmadaAggregatedAPIServer:
imageRepository: "${REGISTRY}/karmada-aggregated-apiserver" # karmada-aggregated-apiserver 镜像仓库
imageTag: "${VERSION}" # 镜像版本
karmadaDataPath: "${HOME}/karmada" # 数据存储路径
karmadaPkiPath: "${HOME}/karmada/pki" # PKI 存储路径
karmadaCrds: "./crds.tar.gz" # CRDs 文件路径
EOF
)

filled_config=$(echo "$CONFIG_TEMPLATE" | sed \
-e "s|\${KUBECONFIG_PATH}|${KUBECONFIG_PATH}|g" \
-e "s|\${HOST_CLUSTER_NAME}|${HOST_CLUSTER_NAME}|g" \
-e "s|\${REGISTRY}|${REGISTRY}|g" \
-e "s|\${VERSION}|${VERSION}|g" \
-e "s|\${HOME}|${HOME}|g")

echo "$filled_config" > $CONFIG_FILE_PATH

echo "Karmada init config file generated at $CONFIG_FILE_PATH"

# init Karmada control plane
echo "Start init karmada control plane..."
${BUILD_PATH}/karmadactl init --config=${CONFIG_FILE_PATH}

# join cluster
echo "Join member clusters..."
${BUILD_PATH}/karmadactl --kubeconfig ${HOME}/karmada/karmada-apiserver.config join ${MEMBER_CLUSTER_1_NAME} --cluster-kubeconfig=${KUBECONFIG_PATH}/${MEMBER_CLUSTER_1_NAME}.config
${BUILD_PATH}/karmadactl --kubeconfig ${HOME}/karmada/karmada-apiserver.config join ${MEMBER_CLUSTER_2_NAME} --cluster-kubeconfig=${KUBECONFIG_PATH}/${MEMBER_CLUSTER_2_NAME}.config
kubectl wait --for=condition=Ready clusters --all --timeout=800s --kubeconfig=${HOME}/karmada/karmada-apiserver.config
1 change: 1 addition & 0 deletions hack/verify-license.sh
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ missing_license_header_files="$($ADDLICENSE_BIN \
-ignore "**/*.yml" \
-ignore "**/*.json" \
-ignore ".idea/**" \
-ignore ".git/**"
.)" || true

if [[ "$missing_license_header_files" ]]; then
Expand Down
2 changes: 2 additions & 0 deletions pkg/karmadactl/cmdinit/cmdinit.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ func NewCmdInit(parentCommand string) *cobra.Command {
flags.StringVar(&opts.ExternalEtcdKeyPrefix, "external-etcd-key-prefix", "", "The key prefix to be configured to kube-apiserver through --etcd-prefix.")
// karmada
flags.StringVar(&opts.CRDs, "crds", kubernetes.DefaultCrdURL, "Karmada crds resource.(local file e.g. --crds /root/crds.tar.gz)")
flags.StringVar(&opts.KarmadaInitFilePath, "config", "", "Karmada init file path")
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.")
flags.Int32VarP(&opts.KarmadaAPIServerNodePort, "port", "p", 32443, "Karmada apiserver service node port")
flags.StringVarP(&opts.KarmadaDataPath, "karmada-data", "d", "/etc/karmada", "Karmada data path. kubeconfig cert and crds files")
Expand All @@ -166,6 +167,7 @@ func NewCmdInit(parentCommand string) *cobra.Command {
flags.StringVarP(&opts.KarmadaAggregatedAPIServerImage, "karmada-aggregated-apiserver-image", "", kubernetes.DefaultKarmadaAggregatedAPIServerImage, "Karmada aggregated apiserver image")
flags.Int32VarP(&opts.KarmadaAggregatedAPIServerReplicas, "karmada-aggregated-apiserver-replicas", "", 1, "Karmada aggregated apiserver replica set")
flags.IntVarP(&opts.WaitComponentReadyTimeout, "wait-component-ready-timeout", "", cmdinitoptions.WaitComponentReadyTimeout, "Wait for karmada component ready timeout. 0 means wait forever")

return cmd
}

Expand Down
111 changes: 111 additions & 0 deletions pkg/karmadactl/cmdinit/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
Copyright 2024 The Karmada Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package config

import (
"fmt"
"os"
"sort"

"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/runtime/schema"
yamlserializer "k8s.io/apimachinery/pkg/runtime/serializer/yaml"
"k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/klog/v2"
)

// LoadInitConfiguration loads the InitConfiguration from the specified file path.
// It delegates the actual loading to the loadInitConfigurationFromFile function.
func LoadInitConfiguration(cfgPath string) (*KarmadaInitConfig, error) {
var config *KarmadaInitConfig
var err error

config, err = loadInitConfigurationFromFile(cfgPath)

return config, err
}

// loadInitConfigurationFromFile reads the file at the specified path and converts it into an InitConfiguration.
// It reads the file contents and then converts the bytes to an InitConfiguration.
func loadInitConfigurationFromFile(cfgPath string) (*KarmadaInitConfig, error) {
klog.V(1).Infof("loading configuration from %q", cfgPath)

b, err := os.ReadFile(cfgPath)
if err != nil {
return nil, fmt.Errorf("unable to read config from %q: %v", cfgPath, err)
}

return BytesToInitConfiguration(b)
}

// BytesToInitConfiguration parses the given byte slice into an InitConfiguration.
// It first converts the bytes to a map of GroupVersionKind to bytes, then processes this map to extract the InitConfiguration.
func BytesToInitConfiguration(b []byte) (*KarmadaInitConfig, error) {
gvkmap, err := ParseGVKYamlMap(b)
if err != nil {
return nil, err
}

return documentMapToInitConfiguration(gvkmap)
}

// ParseGVKYamlMap parses a single YAML document into a map of GroupVersionKind to byte slices.
// This function is a simplified version that handles only a single YAML document.
func ParseGVKYamlMap(yamlBytes []byte) (map[schema.GroupVersionKind][]byte, error) {
gvkmap := make(map[schema.GroupVersionKind][]byte)

gvk, err := yamlserializer.DefaultMetaFactory.Interpret(yamlBytes)
if err != nil {
return nil, errors.Wrap(err, "failed to interpret YAML document")
}
if len(gvk.Group) == 0 || len(gvk.Version) == 0 || len(gvk.Kind) == 0 {
return nil, errors.Errorf("invalid configuration for GroupVersionKind %+v: kind and apiVersion is mandatory information that must be specified", gvk)
}
gvkmap[*gvk] = yamlBytes

return gvkmap, nil
}

// documentMapToInitConfiguration processes a map of GroupVersionKind to byte slices to extract the InitConfiguration.
// It iterates over the map, looking for the "InitConfiguration" kind and unmarshals its content into an InitConfiguration object.
func documentMapToInitConfiguration(gvkmap map[schema.GroupVersionKind][]byte) (*KarmadaInitConfig, error) {
var initcfg *KarmadaInitConfig

gvks := make([]schema.GroupVersionKind, 0, len(gvkmap))
for gvk := range gvkmap {
gvks = append(gvks, gvk)
}
sort.Slice(gvks, func(i, j int) bool {
return gvks[i].String() < gvks[j].String()
})

for _, gvk := range gvks {
fileContent := gvkmap[gvk]
if gvk.Kind == "KarmadaInitConfig" {
initcfg = &KarmadaInitConfig{}
if err := yaml.Unmarshal(fileContent, initcfg); err != nil {
return nil, err
}
}
}

if initcfg == nil {
return nil, fmt.Errorf("no KarmadaInitConfig kind was found in the YAML file")
}

return initcfg, nil
}
Loading

0 comments on commit 44a25dc

Please sign in to comment.