mirror of
https://github.com/rancher/k3k.git
synced 2026-08-23 22:36:17 +00:00
Refactor bootstrap data management (#869)
* Refactor bootstrap data management * Change errors from errors.New to fmt.Errorf Signed-off-by: galal-hussein <hussein.galal.ahmed.11@gmail.com>
This commit is contained in:
@@ -2,167 +2,68 @@ package bootstrap
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
|
||||
"github.com/rancher/k3k/pkg/apis/k3k.io/v1beta1"
|
||||
"github.com/rancher/k3k/pkg/controller"
|
||||
"github.com/rancher/k3k/pkg/k3s"
|
||||
)
|
||||
|
||||
var ErrServerNotReady = errors.New("server not ready")
|
||||
const (
|
||||
TLSDir = "/var/lib/rancher/k3s/server/tls/"
|
||||
)
|
||||
|
||||
type ControlRuntimeBootstrap struct {
|
||||
ServerCA content `json:"serverCA"`
|
||||
ServerCAKey content `json:"serverCAKey"`
|
||||
ClientCA content `json:"clientCA"`
|
||||
ClientCAKey content `json:"clientCAKey"`
|
||||
ETCDServerCA content `json:"etcdServerCA"`
|
||||
ETCDServerCAKey content `json:"etcdServerCAKey"`
|
||||
// Fetch requests bootstrap data from k3s using the token and decodes it,
|
||||
// to avoid double encoding when stored as secret.
|
||||
func Fetch(ctx context.Context, ip, token string) (*k3s.BootstrapData, error) {
|
||||
log := ctrl.LoggerFrom(ctx)
|
||||
log.V(1).Info("Fetching bootstrap data from K3s API")
|
||||
|
||||
return fetchFromK3sServer(ip, token)
|
||||
}
|
||||
|
||||
type content struct {
|
||||
Timestamp string
|
||||
Content string
|
||||
}
|
||||
|
||||
// Generate generates the bootstrap for the cluster:
|
||||
// 1- use the server token to get the bootstrap data from k3s
|
||||
// 2- save the bootstrap data as a secret
|
||||
func GenerateBootstrapData(ctx context.Context, cluster *v1beta1.Cluster, ip, token string) ([]byte, error) {
|
||||
bootstrap, err := requestBootstrap(token, ip)
|
||||
// SaveToSecret marshals the bootstrap data and stores it in a Secret owned by the cluster,
|
||||
// creating the Secret if it does not exist or updating it otherwise.
|
||||
func SaveToSecret(ctx context.Context, c client.Client, scheme *runtime.Scheme, cluster *v1beta1.Cluster, data *k3s.BootstrapData) error {
|
||||
bootstrapData, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to request bootstrap secret: %w", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := decodeBootstrap(bootstrap); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode bootstrap secret: %w", err)
|
||||
}
|
||||
|
||||
return json.Marshal(bootstrap)
|
||||
}
|
||||
|
||||
func requestBootstrap(token, serverIP string) (*ControlRuntimeBootstrap, error) {
|
||||
url := "https://" + serverIP + "/v1-k3s/server-bootstrap"
|
||||
|
||||
client := http.Client{
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
secret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: controller.SafeConcatNameWithPrefix(cluster.Name, "bootstrap"),
|
||||
Namespace: cluster.Namespace,
|
||||
},
|
||||
Timeout: 5 * time.Second,
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Add("Authorization", "Basic "+basicAuth("server", token))
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
if errors.Is(err, syscall.ECONNREFUSED) {
|
||||
return nil, ErrServerNotReady
|
||||
_, err = controllerutil.CreateOrUpdate(ctx, c, secret, func() error {
|
||||
if err := controllerutil.SetControllerReference(cluster, secret, scheme); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
secret.Data = map[string][]byte{
|
||||
"bootstrap": bootstrapData,
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
return nil
|
||||
})
|
||||
|
||||
var runtimeBootstrap ControlRuntimeBootstrap
|
||||
if err := json.NewDecoder(resp.Body).Decode(&runtimeBootstrap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &runtimeBootstrap, nil
|
||||
return err
|
||||
}
|
||||
|
||||
func basicAuth(username, password string) string {
|
||||
auth := username + ":" + password
|
||||
return base64.StdEncoding.EncodeToString([]byte(auth))
|
||||
}
|
||||
|
||||
func decodeBootstrap(bootstrap *ControlRuntimeBootstrap) error {
|
||||
// client-ca
|
||||
decoded, err := base64.StdEncoding.DecodeString(bootstrap.ClientCA.Content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bootstrap.ClientCA.Content = string(decoded)
|
||||
|
||||
// client-ca-key
|
||||
decoded, err = base64.StdEncoding.DecodeString(bootstrap.ClientCAKey.Content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bootstrap.ClientCAKey.Content = string(decoded)
|
||||
|
||||
// server-ca
|
||||
decoded, err = base64.StdEncoding.DecodeString(bootstrap.ServerCA.Content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bootstrap.ServerCA.Content = string(decoded)
|
||||
|
||||
// server-ca-key
|
||||
decoded, err = base64.StdEncoding.DecodeString(bootstrap.ServerCAKey.Content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bootstrap.ServerCAKey.Content = string(decoded)
|
||||
|
||||
// etcd-ca
|
||||
decoded, err = base64.StdEncoding.DecodeString(bootstrap.ETCDServerCA.Content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bootstrap.ETCDServerCA.Content = string(decoded)
|
||||
|
||||
// etcd-ca-key
|
||||
decoded, err = base64.StdEncoding.DecodeString(bootstrap.ETCDServerCAKey.Content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bootstrap.ETCDServerCAKey.Content = string(decoded)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func DecodedBootstrap(token, ip string) (*ControlRuntimeBootstrap, error) {
|
||||
bootstrap, err := requestBootstrap(token, ip)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := decodeBootstrap(bootstrap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return bootstrap, nil
|
||||
}
|
||||
|
||||
func GetFromSecret(ctx context.Context, client client.Client, cluster *v1beta1.Cluster) (*ControlRuntimeBootstrap, error) {
|
||||
// LoadFromSecret reads the bootstrap data of a certain cluster and returns the decoded content.
|
||||
func LoadFromSecret(ctx context.Context, client client.Client, cluster *v1beta1.Cluster) (*k3s.BootstrapData, error) {
|
||||
key := types.NamespacedName{
|
||||
Name: controller.SafeConcatNameWithPrefix(cluster.Name, "bootstrap"),
|
||||
Namespace: cluster.Namespace,
|
||||
@@ -178,9 +79,18 @@ func GetFromSecret(ctx context.Context, client client.Client, cluster *v1beta1.C
|
||||
return nil, errors.New("empty bootstrap")
|
||||
}
|
||||
|
||||
var bootstrap ControlRuntimeBootstrap
|
||||
var bootstrap k3s.BootstrapData
|
||||
|
||||
err := json.Unmarshal(bootstrapData, &bootstrap)
|
||||
|
||||
return &bootstrap, err
|
||||
}
|
||||
|
||||
func fetchFromK3sServer(serviceIP, token string) (*k3s.BootstrapData, error) {
|
||||
client := k3s.New(k3s.ClientConfig{
|
||||
ServerIP: serviceIP,
|
||||
Token: token,
|
||||
})
|
||||
|
||||
return k3s.GetServerBootstrap(client)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user