karmada/pkg/karmadactl/addons/init/enable_option.go

242 lines
8.0 KiB
Go

/*
Copyright 2022 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 init
import (
"context"
"fmt"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/klog/v2"
"k8s.io/utils/strings/slices"
"github.com/karmada-io/karmada/pkg/karmadactl/util"
"github.com/karmada-io/karmada/pkg/karmadactl/util/apiclient"
"github.com/karmada-io/karmada/pkg/util/names"
"github.com/karmada-io/karmada/pkg/version"
)
// CommandAddonsEnableOption options for addons list.
type CommandAddonsEnableOption struct {
GlobalCommandOptions
KarmadaSearchImage string
KarmadaSearchReplicas int32
KarmadaDeschedulerImage string
KarmadaDeschedulerReplicas int32
KarmadaMetricsAdapterImage string
KarmadaMetricsAdapterReplicas int32
KarmadaSchedulerEstimatorImage string
KarmadaEstimatorReplicas int32
KarmadaKubeClientSet *kubernetes.Clientset
ImageRegistry string
WaitComponentReadyTimeout int
WaitAPIServiceReadyTimeout int
MemberKubeConfig string
MemberContext string
HostClusterDomain string
}
var (
// DefaultKarmadaDeschedulerImage Karmada descheduler image
DefaultKarmadaDeschedulerImage string
// DefaultKarmadaSchedulerEstimatorImage Karmada scheduler-estimator image
DefaultKarmadaSchedulerEstimatorImage string
// DefaultKarmadaSearchImage Karmada search image
DefaultKarmadaSearchImage string
// DefaultKarmadaMetricsAdapterImage Karmada metrics adapter image
DefaultKarmadaMetricsAdapterImage string
karmadaRelease string
)
func init() {
releaseVer, err := version.ParseGitVersion(version.Get().GitVersion)
if err != nil {
klog.Infof("No default release version found. build version: %s", version.Get().String())
releaseVer = &version.ReleaseVersion{} // initialize to avoid panic
}
karmadaRelease = releaseVer.ReleaseVersion()
DefaultKarmadaDeschedulerImage = fmt.Sprintf("docker.io/karmada/karmada-descheduler:%s", releaseVer.ReleaseVersion())
DefaultKarmadaSchedulerEstimatorImage = fmt.Sprintf("docker.io/karmada/karmada-scheduler-estimator:%s", releaseVer.ReleaseVersion())
DefaultKarmadaSearchImage = fmt.Sprintf("docker.io/karmada/karmada-search:%s", releaseVer.ReleaseVersion())
DefaultKarmadaMetricsAdapterImage = fmt.Sprintf("docker.io/karmada/karmada-metrics-adapter:%s", releaseVer.ReleaseVersion())
}
// KarmadaDeschedulerImage get karmada descheduler image
func KarmadaDeschedulerImage(o *CommandAddonsEnableOption) string {
if o.ImageRegistry != "" && o.KarmadaDeschedulerImage == DefaultKarmadaDeschedulerImage {
return o.ImageRegistry + "/karmada-descheduler:" + karmadaRelease
}
return o.KarmadaDeschedulerImage
}
// KarmadaSchedulerEstimatorImage get karmada scheduler-estimator image
func KarmadaSchedulerEstimatorImage(o *CommandAddonsEnableOption) string {
if o.ImageRegistry != "" && o.KarmadaSchedulerEstimatorImage == DefaultKarmadaSchedulerEstimatorImage {
return o.ImageRegistry + "/karmada-scheduler-estimator:" + karmadaRelease
}
return o.KarmadaSchedulerEstimatorImage
}
// KarmadaSearchImage get karmada search image
func KarmadaSearchImage(o *CommandAddonsEnableOption) string {
if o.ImageRegistry != "" && o.KarmadaSearchImage == DefaultKarmadaSearchImage {
return o.ImageRegistry + "/karmada-search:" + karmadaRelease
}
return o.KarmadaSearchImage
}
// KarmadaMetricsAdapterImage get karmada metrics adapter image
func KarmadaMetricsAdapterImage(o *CommandAddonsEnableOption) string {
if o.ImageRegistry != "" && o.KarmadaMetricsAdapterImage == DefaultKarmadaMetricsAdapterImage {
return o.ImageRegistry + "/karmada-metrics-adapter:" + karmadaRelease
}
return o.KarmadaMetricsAdapterImage
}
// Complete the conditions required to be able to run enable.
func (o *CommandAddonsEnableOption) Complete() error {
err := o.GlobalCommandOptions.Complete()
if err != nil {
return err
}
o.KarmadaKubeClientSet, err = apiclient.NewClientSet(o.KarmadaRestConfig)
if err != nil {
return err
}
return nil
}
// Validate Check that there are enough conditions to run addon enable.
func (o *CommandAddonsEnableOption) Validate(args []string) error {
err := validAddonNames(args)
if err != nil {
return err
}
secretClient := o.KubeClientSet.CoreV1().Secrets(o.Namespace)
for _, addon := range getEnablingAddons(args) {
if addon.Name == names.KarmadaSchedulerEstimatorComponentName {
// estimator not rely on karmada config secret
continue
}
karmadaConfigSecretName := util.KarmadaConfigName(addon.Name)
_, err = secretClient.Get(context.TODO(), karmadaConfigSecretName, metav1.GetOptions{})
if err != nil {
if apierrors.IsNotFound(err) {
return fmt.Errorf("secrets `%s` is not found in namespace %s, please execute karmadactl init to deploy karmada first", karmadaConfigSecretName, o.Namespace)
}
}
}
if o.Cluster == "" {
if slices.Contains(args, names.KarmadaSchedulerEstimatorComponentName) {
return fmt.Errorf("member cluster is needed when enable karmada-scheduler-estimator,use `--cluster=member --member-kubeconfig /root/.kube/config --member-context member1` to enable karmada-scheduler-estimator")
}
} else {
if !slices.Contains(args, names.KarmadaSchedulerEstimatorComponentName) && !slices.Contains(args, "all") {
return fmt.Errorf("cluster is needed only when enable karmada-scheduler-estimator or enable all")
}
if o.MemberKubeConfig == "" {
return fmt.Errorf("member config is needed when enable karmada-scheduler-estimator,use `--cluster=member --member-kubeconfig /root/.kube/member.config --member-context member1` to enable karmada-scheduler-estimator")
}
// Check member kubeconfig and context is valid
memberConfig, err := apiclient.RestConfig(o.MemberContext, o.MemberKubeConfig)
if err != nil {
return fmt.Errorf("failed to get member cluster config. error: %v", err)
}
memberKubeClient := kubernetes.NewForConfigOrDie(memberConfig)
_, err = memberKubeClient.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
if err != nil {
return fmt.Errorf("failed to get nodes from cluster %s with member-kubeconfig and member-context. error: %v, Please check the Role or ClusterRole of the serviceAccount in your member-kubeconfig", o.Cluster, err)
}
_, err = memberKubeClient.CoreV1().Pods(metav1.NamespaceAll).List(context.TODO(), metav1.ListOptions{})
if err != nil {
return fmt.Errorf("failed to get pods from cluster %s with member-kubeconfig and member-context. error: %v, Please check the Role or ClusterRole of the serviceAccount in your member-kubeconfig", o.Cluster, err)
}
}
return nil
}
// Run start enable Karmada addons
func (o *CommandAddonsEnableOption) Run(args []string) error {
// enable addons
for name, addon := range getEnablingAddons(args) {
klog.Infof("Start to enable addon %s", name)
if err := addon.Enable(o); err != nil {
klog.Errorf("Install addon %s failed", name)
return err
}
klog.Infof("Successfully enable addon %s", name)
}
return nil
}
// validAddonNames valid whether addon names is supported now
func validAddonNames(addonNames []string) error {
if len(addonNames) == 0 {
return fmt.Errorf("addonNames must be not be null")
}
for _, addonName := range addonNames {
if addonName == "all" {
continue
}
_, ok := Addons[addonName]
if !ok {
return fmt.Errorf("addon %s is not be supported now", addonName)
}
}
return nil
}
func getEnablingAddons(addonNames []string) map[string]*Addon {
var enablingAddons = map[string]*Addon{}
for _, name := range addonNames {
if name == "all" {
enablingAddons = Addons
break
}
if addon := Addons[name]; addon != nil {
enablingAddons[name] = addon
}
}
return enablingAddons
}