2022-09-05 03:06:08 -04:00
|
|
|
/*
|
|
|
|
Copyright (c) Edgeless Systems GmbH
|
|
|
|
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
*/
|
|
|
|
|
2022-03-22 11:03:15 -04:00
|
|
|
package kubectl
|
|
|
|
|
|
|
|
import (
|
2022-07-18 06:28:02 -04:00
|
|
|
"context"
|
2022-03-22 11:03:15 -04:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
|
2022-09-21 07:47:57 -04:00
|
|
|
"github.com/edgelesssys/constellation/v2/internal/kubernetes"
|
2022-07-18 06:28:02 -04:00
|
|
|
corev1 "k8s.io/api/core/v1"
|
2022-03-22 11:03:15 -04:00
|
|
|
"k8s.io/cli-runtime/pkg/resource"
|
|
|
|
)
|
|
|
|
|
|
|
|
// ErrKubeconfigNotSet is the error value returned by Kubectl.Apply when SetKubeconfig was not called first.
|
|
|
|
var ErrKubeconfigNotSet = errors.New("kubeconfig not set")
|
|
|
|
|
|
|
|
// Client wraps marshable k8s resources into resource.Info fields and applies them in a cluster.
|
|
|
|
type Client interface {
|
|
|
|
// ApplyOneObject applies a k8s resource similar to kubectl apply.
|
|
|
|
ApplyOneObject(info *resource.Info, forceConflicts bool) error
|
|
|
|
// GetObjects converts resources into prepared info fields for use in ApplyOneObject.
|
2022-08-29 08:30:20 -04:00
|
|
|
GetObjects(resources kubernetes.Marshaler) ([]*resource.Info, error)
|
2022-07-18 06:28:02 -04:00
|
|
|
CreateConfigMap(ctx context.Context, configMap corev1.ConfigMap) error
|
2022-07-28 10:07:29 -04:00
|
|
|
AddTolerationsToDeployment(ctx context.Context, tolerations []corev1.Toleration, name string, namespace string) error
|
|
|
|
AddNodeSelectorsToDeployment(ctx context.Context, selectors map[string]string, name string, namespace string) error
|
2022-08-04 10:15:52 -04:00
|
|
|
// WaitForCRD waits for the given CRD to be established.
|
|
|
|
WaitForCRD(ctx context.Context, crd string) error
|
2022-11-04 07:36:26 -04:00
|
|
|
ListAllNamespaces(ctx context.Context) (*corev1.NamespaceList, error)
|
2022-03-22 11:03:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// clientGenerator can generate new clients from a kubeconfig.
|
|
|
|
type clientGenerator interface {
|
|
|
|
NewClient(kubeconfig []byte) (Client, error)
|
|
|
|
}
|
|
|
|
|
2022-04-26 05:22:21 -04:00
|
|
|
// Kubectl implements kubernetes.Apply interface and acts like the Kubernetes "kubectl" tool.
|
2022-03-22 11:03:15 -04:00
|
|
|
type Kubectl struct {
|
|
|
|
clientGenerator
|
|
|
|
kubeconfig []byte
|
|
|
|
}
|
|
|
|
|
|
|
|
// New creates a new kubectl using the real clientGenerator.
|
|
|
|
func New() *Kubectl {
|
|
|
|
return &Kubectl{
|
|
|
|
clientGenerator: &generator{},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Apply will apply the given resources using server-side-apply.
|
2022-08-29 08:30:20 -04:00
|
|
|
func (k *Kubectl) Apply(resources kubernetes.Marshaler, forceConflicts bool) error {
|
2022-03-22 11:03:15 -04:00
|
|
|
if k.kubeconfig == nil {
|
|
|
|
return ErrKubeconfigNotSet
|
|
|
|
}
|
|
|
|
client, err := k.clientGenerator.NewClient(k.kubeconfig)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// convert marshaler object into []*resource.info
|
|
|
|
infos, err := client.GetObjects(resources)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// apply each object, one by one
|
|
|
|
for i, resource := range infos {
|
|
|
|
if err := client.ApplyOneObject(resource, forceConflicts); err != nil {
|
2022-09-05 10:50:22 -04:00
|
|
|
return fmt.Errorf("kubectl apply of object %v/%v: %w", i+1, len(infos), err)
|
2022-03-22 11:03:15 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetKubeconfig will store the kubeconfig to generate Clients using the clientGenerator later.
|
|
|
|
func (k *Kubectl) SetKubeconfig(kubeconfig []byte) {
|
|
|
|
k.kubeconfig = kubeconfig
|
|
|
|
}
|
2022-07-18 06:28:02 -04:00
|
|
|
|
2022-11-09 09:57:54 -05:00
|
|
|
// CreateConfigMap creates the provided configmap.
|
2022-07-18 06:28:02 -04:00
|
|
|
func (k *Kubectl) CreateConfigMap(ctx context.Context, configMap corev1.ConfigMap) error {
|
|
|
|
client, err := k.clientGenerator.NewClient(k.kubeconfig)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-11-04 07:36:26 -04:00
|
|
|
return client.CreateConfigMap(ctx, configMap)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ListAllNamespaces returns all namespaces in the cluster.
|
|
|
|
func (k *Kubectl) ListAllNamespaces(ctx context.Context) (*corev1.NamespaceList, error) {
|
|
|
|
client, err := k.clientGenerator.NewClient(k.kubeconfig)
|
2022-07-18 06:28:02 -04:00
|
|
|
if err != nil {
|
2022-11-04 07:36:26 -04:00
|
|
|
return nil, err
|
2022-07-18 06:28:02 -04:00
|
|
|
}
|
|
|
|
|
2022-11-04 07:36:26 -04:00
|
|
|
return client.ListAllNamespaces(ctx)
|
2022-07-18 06:28:02 -04:00
|
|
|
}
|
2022-07-14 15:15:31 -04:00
|
|
|
|
2022-11-09 09:57:54 -05:00
|
|
|
// AddTolerationsToDeployment adds [K8s tolerations] to the deployment, identified
|
|
|
|
// by name and namespace.
|
|
|
|
//
|
|
|
|
// [K8s tolerations]: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/
|
2022-07-28 10:07:29 -04:00
|
|
|
func (k *Kubectl) AddTolerationsToDeployment(ctx context.Context, tolerations []corev1.Toleration, name string, namespace string) error {
|
2022-07-14 15:15:31 -04:00
|
|
|
client, err := k.clientGenerator.NewClient(k.kubeconfig)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-07-28 10:07:29 -04:00
|
|
|
if err = client.AddTolerationsToDeployment(ctx, tolerations, name, namespace); err != nil {
|
2022-07-14 15:15:31 -04:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-11-09 09:57:54 -05:00
|
|
|
// AddNodeSelectorsToDeployment adds [K8s selectors] to the deployment, identified
|
|
|
|
// by name and namespace.
|
|
|
|
//
|
|
|
|
// [K8s selectors]: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/
|
2022-07-28 10:07:29 -04:00
|
|
|
func (k *Kubectl) AddNodeSelectorsToDeployment(ctx context.Context, selectors map[string]string, name string, namespace string) error {
|
2022-07-26 04:10:34 -04:00
|
|
|
client, err := k.clientGenerator.NewClient(k.kubeconfig)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-07-28 10:07:29 -04:00
|
|
|
if err = client.AddNodeSelectorsToDeployment(ctx, selectors, name, namespace); err != nil {
|
2022-07-26 04:10:34 -04:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2022-08-04 10:15:52 -04:00
|
|
|
|
2022-11-09 09:57:54 -05:00
|
|
|
// WaitForCRDs waits for a list of CRDs to be established.
|
2022-08-04 10:15:52 -04:00
|
|
|
func (k *Kubectl) WaitForCRDs(ctx context.Context, crds []string) error {
|
|
|
|
client, err := k.clientGenerator.NewClient(k.kubeconfig)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, crd := range crds {
|
|
|
|
err = client.WaitForCRD(ctx, crd)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|