mirror of
https://github.com/edgelesssys/constellation.git
synced 2025-08-11 16:30:12 -04:00
cli: create backups for CRDs and their resources
These backups could be used in case an upgrade misbehaves after helm declared it as successful. The manual backups are required as helm-rollback won't touch custom resources and changes to CRDs delete resources of the old version.
This commit is contained in:
parent
afbd4a3dc1
commit
e7c7e35f51
11 changed files with 397 additions and 87 deletions
|
@ -15,28 +15,27 @@ import (
|
|||
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||
"github.com/edgelesssys/constellation/v2/internal/deploy/helm"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||
"github.com/spf13/afero"
|
||||
"golang.org/x/mod/semver"
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/cli"
|
||||
v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
// Client handles interaction with helm.
|
||||
type Client struct {
|
||||
config *action.Configuration
|
||||
crdClient *apiextensionsclient.Clientset
|
||||
log debugLog
|
||||
config *action.Configuration
|
||||
kubectl crdClient
|
||||
fs file.Handler
|
||||
log debugLog
|
||||
}
|
||||
|
||||
// NewClient returns a new initializes client for the namespace Client.
|
||||
func NewClient(kubeConfigPath, helmNamespace string, client *apiextensionsclient.Clientset, log debugLog) (*Client, error) {
|
||||
func NewClient(client crdClient, kubeConfigPath, helmNamespace string, log debugLog) (*Client, error) {
|
||||
settings := cli.New()
|
||||
settings.KubeConfig = kubeConfigPath // constants.AdminConfFilename
|
||||
|
||||
|
@ -45,7 +44,18 @@ func NewClient(kubeConfigPath, helmNamespace string, client *apiextensionsclient
|
|||
return nil, fmt.Errorf("initializing config: %w", err)
|
||||
}
|
||||
|
||||
return &Client{config: actionConfig, crdClient: client, log: log}, nil
|
||||
fileHandler := file.NewHandler(afero.NewOsFs())
|
||||
|
||||
kubeconfig, err := fileHandler.Read(kubeConfigPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading gce config: %w", err)
|
||||
}
|
||||
|
||||
if err := client.Initialize(kubeconfig); err != nil {
|
||||
return nil, fmt.Errorf("initializing kubectl: %w", err)
|
||||
}
|
||||
|
||||
return &Client{config: actionConfig, kubectl: client, fs: fileHandler, log: log}, nil
|
||||
}
|
||||
|
||||
// Upgrade runs a helm-upgrade on all deployments that are managed via Helm.
|
||||
|
@ -168,38 +178,6 @@ func (c *Client) GetValues(release string) (map[string]any, error) {
|
|||
return values, nil
|
||||
}
|
||||
|
||||
// ApplyCRD updates the given CRD by parsing it, querying it's version from the cluster and finally updating it.
|
||||
func (c *Client) ApplyCRD(ctx context.Context, rawCRD []byte) error {
|
||||
crd, err := parseCRD(rawCRD)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing crds: %w", err)
|
||||
}
|
||||
|
||||
clusterCRD, err := c.crdClient.ApiextensionsV1().CustomResourceDefinitions().Get(ctx, crd.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting crd: %w", err)
|
||||
}
|
||||
crd.ResourceVersion = clusterCRD.ResourceVersion
|
||||
_, err = c.crdClient.ApiextensionsV1().CustomResourceDefinitions().Update(ctx, crd, metav1.UpdateOptions{})
|
||||
return err
|
||||
}
|
||||
|
||||
// parseCRD takes a byte slice of data and tries to create a CustomResourceDefinition object from it.
|
||||
func parseCRD(crdString []byte) (*v1.CustomResourceDefinition, error) {
|
||||
sch := runtime.NewScheme()
|
||||
_ = scheme.AddToScheme(sch)
|
||||
_ = v1.AddToScheme(sch)
|
||||
obj, groupVersionKind, err := serializer.NewCodecFactory(sch).UniversalDeserializer().Decode(crdString, nil, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decoding crd: %w", err)
|
||||
}
|
||||
if groupVersionKind.Kind == "CustomResourceDefinition" {
|
||||
return obj.(*v1.CustomResourceDefinition), nil
|
||||
}
|
||||
|
||||
return nil, errors.New("parsed []byte, but did not find a CRD")
|
||||
}
|
||||
|
||||
// updateCRDs walks through the dependencies of the given chart and applies
|
||||
// the files in the dependencie's 'crds' folder.
|
||||
// This function is NOT recursive!
|
||||
|
@ -208,7 +186,7 @@ func (c *Client) updateCRDs(ctx context.Context, chart *chart.Chart) error {
|
|||
for _, crdFile := range dep.Files {
|
||||
if strings.HasPrefix(crdFile.Name, "crds/") {
|
||||
c.log.Debugf("Updating crd: %s", crdFile.Name)
|
||||
err := c.ApplyCRD(ctx, crdFile.Data)
|
||||
err := c.kubectl.ApplyCRD(ctx, crdFile.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -245,3 +223,10 @@ type debugLog interface {
|
|||
Debugf(format string, args ...any)
|
||||
Sync()
|
||||
}
|
||||
|
||||
type crdClient interface {
|
||||
Initialize(kubeconfig []byte) error
|
||||
ApplyCRD(ctx context.Context, rawCRD []byte) error
|
||||
GetCRDs(ctx context.Context) ([]apiextensionsv1.CustomResourceDefinition, error)
|
||||
GetCRs(ctx context.Context, gvr schema.GroupVersionResource) ([]unstructured.Unstructured, error)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue