certimate/internal/deployer/k8s_secret.go
2024-10-28 11:15:08 +08:00

144 lines
3.8 KiB
Go

package deployer
import (
"context"
"encoding/json"
"fmt"
"strings"
corev1 "k8s.io/api/core/v1"
k8sMetaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"github.com/usual2970/certimate/internal/domain"
"github.com/usual2970/certimate/internal/pkg/utils/x509"
)
type K8sSecretDeployer struct {
option *DeployerOption
infos []string
}
func NewK8sSecretDeployer(option *DeployerOption) (Deployer, error) {
return &K8sSecretDeployer{
option: option,
infos: make([]string, 0),
}, nil
}
func (d *K8sSecretDeployer) GetID() string {
return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
}
func (d *K8sSecretDeployer) GetInfo() []string {
return d.infos
}
func (d *K8sSecretDeployer) Deploy(ctx context.Context) error {
access := &domain.KubernetesAccess{}
if err := json.Unmarshal([]byte(d.option.Access), access); err != nil {
return err
}
client, err := d.createClient(access)
if err != nil {
return err
}
d.infos = append(d.infos, toStr("kubeClient create success.", nil))
namespace := getDeployString(d.option.DeployConfig, "namespace")
if namespace == "" {
namespace = "default"
}
secretName := getDeployString(d.option.DeployConfig, "secretName")
if secretName == "" {
return fmt.Errorf("k8s secret name is empty")
}
secretDataKeyForCrt := getDeployString(d.option.DeployConfig, "secretDataKeyForCrt")
if secretDataKeyForCrt == "" {
namespace = "tls.crt"
}
secretDataKeyForKey := getDeployString(d.option.DeployConfig, "secretDataKeyForKey")
if secretDataKeyForKey == "" {
namespace = "tls.key"
}
certificate, err := x509.ParseCertificateFromPEM(d.option.Certificate.Certificate)
if err != nil {
return fmt.Errorf("failed to parse certificate: %w", err)
}
secretPayload := corev1.Secret{
TypeMeta: k8sMetaV1.TypeMeta{
Kind: "Secret",
APIVersion: "v1",
},
ObjectMeta: k8sMetaV1.ObjectMeta{
Name: secretName,
Annotations: map[string]string{
"certimate/domains": d.option.Domain,
"certimate/alt-names": strings.Join(certificate.DNSNames, ","),
"certimate/common-name": certificate.Subject.CommonName,
"certimate/issuer-organization": strings.Join(certificate.Issuer.Organization, ","),
},
},
Type: corev1.SecretType("kubernetes.io/tls"),
}
secretPayload.Data = make(map[string][]byte)
secretPayload.Data[secretDataKeyForCrt] = []byte(d.option.Certificate.Certificate)
secretPayload.Data[secretDataKeyForKey] = []byte(d.option.Certificate.PrivateKey)
// 获取 Secret 实例
_, err = client.CoreV1().Secrets(namespace).Get(context.TODO(), secretName, k8sMetaV1.GetOptions{})
if err != nil {
_, err = client.CoreV1().Secrets(namespace).Create(context.TODO(), &secretPayload, k8sMetaV1.CreateOptions{})
if err != nil {
return fmt.Errorf("failed to create k8s secret: %w", err)
} else {
d.infos = append(d.infos, toStr("Certificate has been created in K8s Secret", nil))
return nil
}
}
// 更新 Secret 实例
_, err = client.CoreV1().Secrets(namespace).Update(context.TODO(), &secretPayload, k8sMetaV1.UpdateOptions{})
if err != nil {
return fmt.Errorf("failed to update k8s secret: %w", err)
}
d.infos = append(d.infos, toStr("Certificate has been updated to K8s Secret", nil))
return nil
}
func (d *K8sSecretDeployer) createClient(access *domain.KubernetesAccess) (*kubernetes.Clientset, error) {
var config *rest.Config
var err error
if access.KubeConfig == "" {
config, err = rest.InClusterConfig()
} else {
kubeConfig, err := clientcmd.NewClientConfigFromBytes([]byte(access.KubeConfig))
if err != nil {
return nil, err
}
config, err = kubeConfig.ClientConfig()
}
if err != nil {
return nil, err
}
client, err := kubernetes.NewForConfig(config)
if err != nil {
return nil, err
}
return client, nil
}