karmada/operator/pkg/tasks/init/upload.go

265 lines
7.4 KiB
Go

package tasks
import (
"errors"
"fmt"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
"k8s.io/klog/v2"
"github.com/karmada-io/karmada/operator/pkg/certs"
"github.com/karmada-io/karmada/operator/pkg/constants"
"github.com/karmada-io/karmada/operator/pkg/util"
"github.com/karmada-io/karmada/operator/pkg/util/apiclient"
"github.com/karmada-io/karmada/operator/pkg/workflow"
)
// NewUploadKubeconfigTask init a task to upload karmada kubeconfig and
// all of karmada certs to secret
func NewUploadKubeconfigTask() workflow.Task {
return workflow.Task{
Name: "upload-config",
RunSubTasks: true,
Run: runUploadKubeconfig,
Tasks: []workflow.Task{
{
Name: "UploadAdminKubeconfig",
Run: runUploadAdminKubeconfig,
},
},
}
}
func runUploadKubeconfig(r workflow.RunData) error {
data, ok := r.(InitData)
if !ok {
return errors.New("upload-config task invoked with an invalid data struct")
}
klog.V(4).InfoS("[upload-config] Running task", "karmada", klog.KObj(data))
return nil
}
func runUploadAdminKubeconfig(r workflow.RunData) error {
data, ok := r.(InitData)
if !ok {
return errors.New("UploadAdminKubeconfig task invoked with an invalid data struct")
}
var endpoint string
switch data.Components().KarmadaAPIServer.ServiceType {
case corev1.ServiceTypeClusterIP:
apiserverName := util.KarmadaAPIServerName(data.GetName())
endpoint = fmt.Sprintf("https://%s.%s.svc.cluster.local:%d", apiserverName, data.GetNamespace(), constants.KarmadaAPIserverListenClientPort)
case corev1.ServiceTypeNodePort:
service, err := apiclient.GetService(data.RemoteClient(), util.KarmadaAPIServerName(data.GetName()), data.GetNamespace())
if err != nil {
return err
}
nodePort := getNodePortFromAPIServerService(service)
endpoint = fmt.Sprintf("https://%s:%d", data.ControlplaneAddress(), nodePort)
}
kubeconfig, err := buildKubeConfigFromSpec(data, endpoint)
if err != nil {
return err
}
configBytes, err := clientcmd.Write(*kubeconfig)
if err != nil {
return err
}
err = apiclient.CreateOrUpdateSecret(data.RemoteClient(), &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: data.GetNamespace(),
Name: util.AdminKubeconfigSercretName(data.GetName()),
Labels: constants.KarmadaOperatorLabel,
},
Data: map[string][]byte{"config": configBytes},
})
if err != nil {
return fmt.Errorf("failed to create secret of kubeconfig, err: %w", err)
}
// store rest config to RunData.
config, err := clientcmd.RESTConfigFromKubeConfig(configBytes)
if err != nil {
return err
}
data.SetControlplaneConifg(config)
klog.V(2).InfoS("[UploadAdminKubeconfig] Successfully created secret of karmada apiserver kubeconfig", "karmada", klog.KObj(data))
return nil
}
func getNodePortFromAPIServerService(service *corev1.Service) int32 {
var nodePort int32
if service.Spec.Type == corev1.ServiceTypeNodePort {
for _, port := range service.Spec.Ports {
if port.Name != "client" {
continue
}
nodePort = port.NodePort
}
}
return nodePort
}
func buildKubeConfigFromSpec(data InitData, serverURL string) (*clientcmdapi.Config, error) {
ca := data.GetCert(constants.CaCertAndKeyName)
if ca == nil {
return nil, errors.New("unable build karmada admin kubeconfig, CA cert is empty")
}
cc := certs.KarmadaCertClient()
if err := mutateCertConfig(data, cc); err != nil {
return nil, fmt.Errorf("error when mutate cert altNames for %s, err: %w", cc.Name, err)
}
client, err := certs.CreateCertAndKeyFilesWithCA(cc, ca.CertData(), ca.KeyData())
if err != nil {
return nil, fmt.Errorf("failed to generate karmada apiserver client certificate for kubeconfig, err: %w", err)
}
return util.CreateWithCerts(
serverURL,
constants.ClusterName,
constants.UserName,
ca.CertData(),
client.KeyData(),
client.CertData(),
), nil
}
// NewUploadCertsTask init a Upload-Certs task
func NewUploadCertsTask() workflow.Task {
return workflow.Task{
Name: "Upload-Certs",
Run: runUploadCerts,
RunSubTasks: true,
Tasks: []workflow.Task{
{
Name: "Upload-KarmadaCert",
Run: runUploadKarmadaCert,
},
{
Name: "Upload-EtcdCert",
Run: runUploadEtcdCert,
},
{
Name: "Upload-WebHookCert",
Run: runUploadWebHookCert,
},
},
}
}
func runUploadCerts(r workflow.RunData) error {
data, ok := r.(InitData)
if !ok {
return errors.New("upload-certs task invoked with an invalid data struct")
}
klog.V(4).InfoS("[upload-certs] Running upload-certs task", "karmada", klog.KObj(data))
if len(data.CertList()) == 0 {
return errors.New("there is no certs in store, please reload crets to store")
}
return nil
}
func runUploadKarmadaCert(r workflow.RunData) error {
data, ok := r.(InitData)
if !ok {
return errors.New("upload-KarmadaCert task invoked with an invalid data struct")
}
certList := data.CertList()
certsData := make(map[string][]byte, len(certList))
for _, c := range certList {
certsData[c.KeyName()] = c.KeyData()
certsData[c.CertName()] = c.CertData()
}
err := apiclient.CreateOrUpdateSecret(data.RemoteClient(), &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: util.KarmadaCertSecretName(data.GetName()),
Namespace: data.GetNamespace(),
Labels: constants.KarmadaOperatorLabel,
},
Data: certsData,
})
if err != nil {
return fmt.Errorf("failed to upload karmada cert to secret, err: %w", err)
}
klog.V(2).InfoS("[upload-KarmadaCert] Successfully uploaded karmada certs to secret", "karmada", klog.KObj(data))
return nil
}
func runUploadEtcdCert(r workflow.RunData) error {
data, ok := r.(InitData)
if !ok {
return errors.New("upload-etcdCert task invoked with an invalid data struct")
}
ca := data.GetCert(constants.EtcdCaCertAndKeyName)
server := data.GetCert(constants.EtcdServerCertAndKeyName)
client := data.GetCert(constants.EtcdClientCertAndKeyName)
err := apiclient.CreateOrUpdateSecret(data.RemoteClient(), &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: data.GetNamespace(),
Name: util.EtcdCertSecretName(data.GetName()),
Labels: constants.KarmadaOperatorLabel,
},
Data: map[string][]byte{
ca.CertName(): ca.CertData(),
ca.KeyName(): ca.KeyData(),
server.CertName(): server.CertData(),
server.KeyName(): server.KeyData(),
client.CertName(): client.CertData(),
client.KeyName(): client.KeyData(),
},
})
if err != nil {
return fmt.Errorf("failed to upload etcd certs to secret, err: %w", err)
}
klog.V(2).InfoS("[upload-etcdCert] Successfully uploaded etcd certs to secret", "karmada", klog.KObj(data))
return nil
}
func runUploadWebHookCert(r workflow.RunData) error {
data, ok := r.(InitData)
if !ok {
return errors.New("upload-webhookCert task invoked with an invalid data struct")
}
cert := data.GetCert(constants.KarmadaCertAndKeyName)
err := apiclient.CreateOrUpdateSecret(data.RemoteClient(), &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: util.WebhookCertSecretName(data.GetName()),
Namespace: data.GetNamespace(),
Labels: constants.KarmadaOperatorLabel,
},
Data: map[string][]byte{
"tls.key": cert.KeyData(),
"tls.crt": cert.CertData(),
},
})
if err != nil {
return fmt.Errorf("failed to upload webhook certs to secret, err: %w", err)
}
klog.V(2).InfoS("[upload-webhookCert] Successfully uploaded webhook certs to secret", "karmada", klog.KObj(data))
return nil
}