Skip to content

Commit

Permalink
Add config list
Browse files Browse the repository at this point in the history
Signed-off-by: tiansuo114 <[email protected]>
  • Loading branch information
tiansuo114 committed Oct 11, 2024
1 parent 21467f8 commit 8b5e785
Show file tree
Hide file tree
Showing 7 changed files with 332 additions and 7 deletions.
18 changes: 11 additions & 7 deletions pkg/karmadactl/cmdinit/cmdinit.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,32 +114,36 @@ func NewCmdInit(parentCommand string) *cobra.Command {
},
}
flags := cmd.Flags()

cmdInitParentCommand := fmt.Sprintf("%s %s", parentCommand, "addons")
cmd.AddCommand(NewCmdShowImages(cmdInitParentCommand))

flags.StringVarP(&opts.ImageRegistry, "private-image-registry", "", "", "Private image registry where pull images from. If set, all required images will be downloaded from it, it would be useful in offline installation scenarios. In addition, you still can use --kube-image-registry to specify the registry for Kubernetes's images.")
flags.StringVarP(&opts.ImagePullPolicy, "image-pull-policy", "", string(corev1.PullIfNotPresent), "The image pull policy for all Karmada components container. One of Always, Never, IfNotPresent. Defaults to IfNotPresent.")
flags.StringSliceVar(&opts.PullSecrets, "image-pull-secrets", nil, "Image pull secrets are used to pull images from the private registry, could be secret list separated by comma (e.g '--image-pull-secrets PullSecret1,PullSecret2', the secrets should be pre-settled in the namespace declared by '--namespace')")
// kube image registry
flags.StringVarP(&opts.KubeImageMirrorCountry, "kube-image-mirror-country", "", "", "Country code of the kube image registry to be used. For Chinese mainland users, set it to cn")
flags.StringVarP(&opts.KubeImageRegistry, "kube-image-registry", "", "", "Kube image registry. For Chinese mainland users, you may use local gcr.io mirrors such as registry.cn-hangzhou.aliyuncs.com/google_containers to override default kube image registry")
flags.StringVar(&opts.KubeImageTag, "kube-image-tag", "v1.30.4", "Choose a specific Kubernetes version for the control plane.")
flags.StringVar(&opts.KubeImageTag, "kube-image-tag", cmdinitoptions.DefaultKubeImageTag, "Choose a specific Kubernetes version for the control plane.")
// cert
flags.StringVar(&opts.ExternalIP, "cert-external-ip", "", "the external IP of Karmada certificate (e.g 192.168.1.2,172.16.1.2)")
flags.StringVar(&opts.ExternalDNS, "cert-external-dns", "", "the external DNS of Karmada certificate (e.g localhost,localhost.com)")
flags.DurationVar(&opts.CertValidity, "cert-validity-period", cert.Duration365d, "the validity period of Karmada certificate (e.g 8760h0m0s, that is 365 days)")
flags.StringVarP(&opts.CaCertFile, "ca-cert-file", "", "", "The root CA certificate file which will be used to issue new certificates for Karmada components. If not set, a new self-signed root CA certificate will be generated. This must be used together with --ca-key-file.")
flags.StringVarP(&opts.CaKeyFile, "ca-key-file", "", "", "The root CA private key file which will be used to issue new certificates for Karmada components. If not set, a new self-signed root CA key will be generated. This must be used together with --ca-cert-file.")
// Kubernetes
flags.StringVarP(&opts.Namespace, "namespace", "n", "karmada-system", "Kubernetes namespace")
flags.StringVarP(&opts.Namespace, "namespace", "n", cmdinitoptions.DefaultKarmadaKubeNamespace, "Kubernetes namespace")
flags.StringVar(&opts.StorageClassesName, "storage-classes-name", "", "Kubernetes StorageClasses Name")
flags.StringVar(&opts.KubeConfig, "kubeconfig", "", "absolute path to the kubeconfig file")
flags.StringVar(&opts.Context, "context", "", "The name of the kubeconfig context to use")
flags.StringVar(&opts.HostClusterDomain, "host-cluster-domain", options.DefaultHostClusterDomain, "The cluster domain of karmada host cluster. (e.g. --host-cluster-domain=host.karmada)")
// etcd
flags.StringVarP(&opts.EtcdStorageMode, "etcd-storage-mode", "", "hostPath",
flags.StringVarP(&opts.EtcdStorageMode, "etcd-storage-mode", "", cmdinitoptions.DefaultEtcdStorageMode,
fmt.Sprintf("etcd data storage mode(%s). value is PVC, specify --storage-classes-name", strings.Join(kubernetes.SupportedStorageMode(), ",")))
flags.StringVarP(&opts.EtcdImage, "etcd-image", "", "", "etcd image")
flags.StringVarP(&opts.EtcdInitImage, "etcd-init-image", "", kubernetes.DefaultInitImage, "etcd init container image")
flags.Int32VarP(&opts.EtcdReplicas, "etcd-replicas", "", 1, "etcd replica set, cluster 3,5...singular")
flags.StringVarP(&opts.EtcdHostDataPath, "etcd-data", "", "/var/lib/karmada-etcd", "etcd data path,valid in hostPath mode.")
flags.StringVarP(&opts.EtcdHostDataPath, "etcd-data", "", cmdinitoptions.DefaultEtcdHostDataPath, "etcd data path,valid in hostPath mode.")
flags.StringVarP(&opts.EtcdNodeSelectorLabels, "etcd-node-selector-labels", "", "", "the labels used for etcd pod to select nodes, valid in hostPath mode, and with each label separated by a comma. ( e.g. --etcd-node-selector-labels karmada.io/etcd=true,kubernetes.io/os=linux)")
flags.StringVarP(&opts.EtcdPersistentVolumeSize, "etcd-pvc-size", "", "5Gi", "etcd data path,valid in pvc mode.")
flags.StringVar(&opts.ExternalEtcdCACertPath, "external-etcd-ca-cert-path", "", "The path of CA certificate of the external etcd cluster in pem format.")
Expand All @@ -150,9 +154,9 @@ func NewCmdInit(parentCommand string) *cobra.Command {
// karmada
flags.StringVar(&opts.CRDs, "crds", kubernetes.DefaultCrdURL, "Karmada crds resource.(local file e.g. --crds /root/crds.tar.gz)")
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")
flags.StringVarP(&opts.KarmadaPkiPath, "karmada-pki", "", "/etc/karmada/pki", "Karmada pki path. Karmada cert files")
flags.Int32VarP(&opts.KarmadaAPIServerNodePort, "port", "p", cmdinitoptions.DefaultKarmadaAPIServerNodePort, "Karmada apiserver service node port")
flags.StringVarP(&opts.KarmadaDataPath, "karmada-data", "d", cmdinitoptions.DefaultKarmadaDataPath, "Karmada data path. kubeconfig cert and crds files")
flags.StringVarP(&opts.KarmadaPkiPath, "karmada-pki", "", cmdinitoptions.DefaultKarmadaPkiPath, "Karmada pki path. Karmada cert files")
flags.StringVarP(&opts.KarmadaAPIServerImage, "karmada-apiserver-image", "", "", "Kubernetes apiserver image")
flags.Int32VarP(&opts.KarmadaAPIServerReplicas, "karmada-apiserver-replicas", "", 1, "Karmada apiserver replica set")
flags.StringVarP(&opts.KarmadaSchedulerImage, "karmada-scheduler-image", "", kubernetes.DefaultKarmadaSchedulerImage, "Karmada scheduler image")
Expand Down
30 changes: 30 additions & 0 deletions pkg/karmadactl/cmdinit/kubernetes/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -744,3 +744,33 @@ func generateServerURL(serverIP string, nodePort int32) (string, error) {
func SupportedStorageMode() []string {
return []string{etcdStorageModeEmptyDir, etcdStorageModeHostPath, etcdStorageModePVC}
}

// NewDefaultCommandInitOption returns a CommandInitOption with default values
func NewDefaultCommandInitOption() *CommandInitOption {
return &CommandInitOption{
ImagePullPolicy: string(corev1.PullIfNotPresent),
KubeImageTag: options.DefaultKubeImageTag,
Namespace: options.DefaultKarmadaKubeNamespace,
EtcdInitImage: DefaultInitImage,
CRDs: DefaultCrdURL,
KarmadaSchedulerImage: DefaultKarmadaSchedulerImage,
KarmadaControllerManagerImage: DefaultKarmadaControllerManagerImage,
KarmadaWebhookImage: DefaultKarmadaWebhookImage,
KarmadaAggregatedAPIServerImage: DefaultKarmadaAggregatedAPIServerImage,
}
}

// GenerateControlPlaneImages generated control plane all the image list
func (i *CommandInitOption) GenerateControlPlaneImages() []string {
images := []string{
i.kubeAPIServerImage(),
i.kubeControllerManagerImage(),
i.etcdImage(),
i.etcdInitImage(),
i.karmadaSchedulerImage(),
i.karmadaControllerManagerImage(),
i.karmadaWebhookImage(),
i.karmadaAggregatedAPIServerImage(),
}
return images
}
17 changes: 17 additions & 0 deletions pkg/karmadactl/cmdinit/options/global.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,20 @@ const (
// WaitComponentReadyTimeout wait component ready time
WaitComponentReadyTimeout = 120
)

const (
// DefaultKubeImageTag is the default tag for the Kubernetes image used by the Karmada control plane.
DefaultKubeImageTag = "v1.30.4"
// DefaultKarmadaKubeNamespace is the default namespace in which the Karmada control plane components will be deployed.
DefaultKarmadaKubeNamespace = "karmada-system"
// DefaultKarmadaAPIServerNodePort is the default node port used by the Karmada API server.
DefaultKarmadaAPIServerNodePort = 32443
// DefaultEtcdStorageMode is the default storage mode for etcd data. The default mode is 'hostPath'.
DefaultEtcdStorageMode = "hostPath"
// DefaultEtcdHostDataPath is the default file system path where etcd data is stored when the storage mode is 'hostPath'.
DefaultEtcdHostDataPath = "/var/lib/karmada-etcd"
// DefaultKarmadaDataPath is the default directory where Karmada stores its configuration, certificates, and other data.
DefaultKarmadaDataPath = "/etc/karmada"
// DefaultKarmadaPkiPath is the default directory where Karmada stores its PKI (Public Key Infrastructure) files.
DefaultKarmadaPkiPath = "/etc/karmada/pki"
)
41 changes: 41 additions & 0 deletions pkg/karmadactl/cmdinit/show/list_option.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
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 show

import (
"fmt"
"os"

"github.com/karmada-io/karmada/pkg/karmadactl/cmdinit/kubernetes"
)

// CommandConfigImageOption options for components
type CommandConfigImageOption struct {
InitOption *kubernetes.CommandInitOption
}

// Run generates and prints the required control plane images.
func (o *CommandConfigImageOption) Run() error {
imageList := o.InitOption.GenerateControlPlaneImages()
for _, image := range imageList {
_, err := fmt.Fprintf(os.Stdout, "%s\n", image)
if err != nil {
return err
}
}
return nil
}
84 changes: 84 additions & 0 deletions pkg/karmadactl/cmdinit/show/list_option_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
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 show_test

import (
"io"
"os"
"testing"

"github.com/stretchr/testify/assert"

"github.com/karmada-io/karmada/pkg/karmadactl/cmdinit/kubernetes"
"github.com/karmada-io/karmada/pkg/karmadactl/cmdinit/show"
)

// Helper function to capture stdout
func captureOutput(f func()) string {
old := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w

f()

w.Close()
os.Stdout = old
out, _ := io.ReadAll(r)
return string(out)
}

func TestCommandConfigImageOption(t *testing.T) {
tests := []struct {
name string
privateImageRegistry string
expectedImages []string
}{
{
name: "Default execution",
privateImageRegistry: "",
expectedImages: kubernetes.NewDefaultCommandInitOption().GenerateControlPlaneImages(),
},
{
name: "With Private Image Registry",
privateImageRegistry: "myregistry.com",
expectedImages: func() []string {
opts := kubernetes.NewDefaultCommandInitOption()
opts.ImageRegistry = "myregistry.com"
return opts.GenerateControlPlaneImages()
}(),
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
opts := show.CommandConfigImageOption{
InitOption: kubernetes.NewDefaultCommandInitOption(),
}
opts.InitOption.ImageRegistry = tt.privateImageRegistry
assert.NotNil(t, opts.InitOption)

output := captureOutput(func() {
err := opts.Run()
assert.NoError(t, err)
})

for _, image := range tt.expectedImages {
assert.Contains(t, output, image)
}
})
}
}
68 changes: 68 additions & 0 deletions pkg/karmadactl/cmdinit/show_images.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
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 cmdinit

import (
"fmt"

"github.com/spf13/cobra"
"k8s.io/kubectl/pkg/util/templates"

"github.com/karmada-io/karmada/pkg/karmadactl/cmdinit/kubernetes"
configInit "github.com/karmada-io/karmada/pkg/karmadactl/cmdinit/show"
)

var (
showImagesLong = templates.LongDesc(`
Shows information about the images required for Karmada deployment.
This command lists all the images needed to deploy and run Karmada, including
images for the API server, controller manager, scheduler, and other components.`)

showImagesExample = templates.Examples(`
# List all required images
%[1]s init show-images
# List all required images from a specific private image registry
%[1]s init show-images --private-image-registry registry.cn-hangzhou.aliyuncs.com/google_containers
# List all required images with a specific country code for the image registry
%[1]s init show-images --kube-image-country cn`)
)

// NewCmdShowImages creates a new show-images command
func NewCmdShowImages(parentCommand string) *cobra.Command {
opts := configInit.CommandConfigImageOption{
InitOption: kubernetes.NewDefaultCommandInitOption(),
}
cmd := &cobra.Command{
Use: "show-images",
Short: "List the images required for Karmada deployment",
Long: showImagesLong,
Example: fmt.Sprintf(showImagesExample, parentCommand),
RunE: func(_ *cobra.Command, _ []string) error {
return opts.Run()
},
SilenceUsage: true,
DisableFlagsInUseLine: true,
}

cmd.Flags().StringVarP(&opts.InitOption.ImageRegistry, "private-image-registry", "", "", "Private image registry to pull images from. If set, all required images will be downloaded from it, useful for offline installation scenarios.")
cmd.Flags().StringVarP(&opts.InitOption.KubeImageMirrorCountry, "kube-image-country", "", "", "The country code of the image registry, such as 'global' or 'cn'.")

return cmd
}
81 changes: 81 additions & 0 deletions test/e2e/karmadactl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1222,6 +1222,87 @@ var _ = ginkgo.Describe("Karmadactl options testing", func() {
})
})

var _ = ginkgo.Describe("Karmadactl show init images testing", func() {
var (
timeout = 10 * time.Second
)

ginkgo.Context("Karmadactl init images list", func() {
var (
cmdArgs []string
expected string
)

ginkgo.It("list images without any flags", func() {
cmdArgs = []string{"init", "show-images"}
expected = `registry.k8s.io/kube-apiserver:v1.30.4
registry.k8s.io/kube-controller-manager:v1.30.4
registry.k8s.io/etcd:3.5.13-0
docker.io/alpine:3.19.1
docker.io/karmada/karmada-scheduler:v0.0.0-master
docker.io/karmada/karmada-controller-manager:v0.0.0-master
docker.io/karmada/karmada-webhook:v0.0.0-master
docker.io/karmada/karmada-aggregated-apiserver:v0.0.0-master`

cmd := framework.NewKarmadactlCommand(kubeconfig, "", karmadactlPath, "", timeout, cmdArgs...)
output, err := cmd.ExecOrDie()
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
gomega.Expect(strings.Contains(output, expected)).Should(gomega.BeTrue(), "Output: %s, Expected: %s", output, expected)
})

ginkgo.It("list images with private-image-registry flag", func() {
cmdArgs = []string{"init", "show-images", "--private-image-registry=registry.k8s.io2"}
expected = `registry.k8s.io2/kube-apiserver:v1.30.4
registry.k8s.io2/kube-controller-manager:v1.30.4
registry.k8s.io2/etcd:3.5.13-0
registry.k8s.io2/alpine:3.19.1
registry.k8s.io2/karmada-scheduler:v0.0.0-master
registry.k8s.io2/karmada-controller-manager:v0.0.0-master
registry.k8s.io2/karmada-webhook:v0.0.0-master
registry.k8s.io2/karmada-aggregated-apiserver:v0.0.0-master`

cmd := framework.NewKarmadactlCommand(kubeconfig, "", karmadactlPath, "", timeout, cmdArgs...)
output, err := cmd.ExecOrDie()
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
gomega.Expect(strings.Contains(output, expected)).Should(gomega.BeTrue(), "Output: %s, Expected: %s", output, expected)
})

ginkgo.It("list images with kube-image-country flag", func() {
cmdArgs = []string{"init", "show-images", "--kube-image-country=cn"}
expected = `registry.cn-hangzhou.aliyuncs.com/google_containers/kube-apiserver:v1.30.4
registry.cn-hangzhou.aliyuncs.com/google_containers/kube-controller-manager:v1.30.4
registry.cn-hangzhou.aliyuncs.com/google_containers/etcd:3.5.13-0
docker.io/alpine:3.19.1
docker.io/karmada/karmada-scheduler:v0.0.0-master
docker.io/karmada/karmada-controller-manager:v0.0.0-master
docker.io/karmada/karmada-webhook:v0.0.0-master
docker.io/karmada/karmada-aggregated-apiserver:v0.0.0-master`

cmd := framework.NewKarmadactlCommand(kubeconfig, "", karmadactlPath, "", timeout, cmdArgs...)
output, err := cmd.ExecOrDie()
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
gomega.Expect(strings.Contains(output, expected)).Should(gomega.BeTrue(), "Output: %s, Expected: %s", output, expected)
})
})

ginkgo.Context("Karmadactl init images list with invalid flags", func() {
var (
cmdArgs []string
expected string
)

ginkgo.It("invalid flag --unknown-flag", func() {
cmdArgs = []string{"init", "show-images", "--unknown-flag"}
expected = "unknown flag"

cmd := framework.NewKarmadactlCommand(kubeconfig, "", karmadactlPath, "", timeout, cmdArgs...)
_, err := cmd.ExecOrDie()
gomega.Expect(err).Should(gomega.HaveOccurred())
gomega.Expect(strings.Contains(err.Error(), expected)).Should(gomega.BeTrue(), "Stderr: %s, Expected: %s", err.Error(), expected)
})
})
})

var _ = framework.SerialDescribe("Karmadactl taint testing", ginkgo.Labels{NeedCreateCluster}, func() {
ginkgo.Context("Test karmadactl taint command with different effects", func() {
var (
Expand Down

0 comments on commit 8b5e785

Please sign in to comment.