Skip to content

Commit

Permalink
[2.7] backup/restore extensions
Browse files Browse the repository at this point in the history
go fmt
  • Loading branch information
nickwsuse committed Apr 9, 2024
1 parent af2c4e7 commit edc5621
Show file tree
Hide file tree
Showing 4 changed files with 267 additions and 4 deletions.
223 changes: 223 additions & 0 deletions extensions/charts/backuprestore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
package charts

import (
"context"
"fmt"
"testing"
"time"

bv1 "github.com/rancher/backup-restore-operator/pkg/apis/resources.cattle.io/v1"
catalogv1 "github.com/rancher/rancher/pkg/apis/catalog.cattle.io/v1"
"github.com/rancher/shepherd/clients/rancher"
"github.com/rancher/shepherd/clients/rancher/catalog"
v1 "github.com/rancher/shepherd/clients/rancher/v1"
"github.com/rancher/shepherd/extensions/defaults"
kubenamespaces "github.com/rancher/shepherd/extensions/kubeapi/namespaces"
"github.com/rancher/shepherd/extensions/namespaces"
"github.com/rancher/shepherd/pkg/api/steve/catalog/types"
"github.com/rancher/shepherd/pkg/wait"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kwait "k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apimachinery/pkg/watch"
)

const (
backupChartNamespace = "cattle-resources-system"
backupChartName = "rancher-backup"
)

// InstallRancherBackupChart is a helper function that installs the Rancher Backups chart.
func InstallRancherBackupChart(client *rancher.Client, installOptions *InstallOptions, rancherBackupOpts *RancherBackupOpts, withStorage bool) error {
serverSetting, err := client.Management.Setting.ByID(serverURLSettingID)
if err != nil {
return err
}

backupChartInstallActionPayload := &payloadOpts{
InstallOptions: *installOptions,
Name: backupChartName,
Namespace: backupChartNamespace,
Host: serverSetting.Value,
}

chartInstallAction := newBackupChartInstallAction(backupChartInstallActionPayload, false, rancherBackupOpts)

if withStorage {
chartInstallAction = newBackupChartInstallAction(backupChartInstallActionPayload, true, rancherBackupOpts)
}

catalogClient, err := client.GetClusterCatalogClient(installOptions.ClusterID)
if err != nil {
return err
}

client.Session.RegisterCleanupFunc(func() error {
defaultChartUninstallAction := newChartUninstallAction()

err = catalogClient.UninstallChart(backupChartName, backupChartNamespace, defaultChartUninstallAction)
if err != nil {
return err
}

watchAppInterface, err := catalogClient.Apps(backupChartNamespace).Watch(context.TODO(), metav1.ListOptions{
FieldSelector: metadataName + backupChartName,
TimeoutSeconds: &defaults.WatchTimeoutSeconds,
})
if err != nil {
return err
}

err = wait.WatchWait(watchAppInterface, func(event watch.Event) (ready bool, err error) {
if event.Type == watch.Error {
return false, fmt.Errorf("there was an error uninstalling rancher backup chart")
} else if event.Type == watch.Deleted {
return true, nil
}
return false, nil
})
if err != nil {
return err
}

steveclient, err := client.Steve.ProxyDownstream(installOptions.ClusterID)
if err != nil {
return err
}

namespaceClient := steveclient.SteveType(namespaces.NamespaceSteveType)

namespace, err := namespaceClient.ByID(backupChartNamespace)
if err != nil {
return err
}

err = namespaceClient.Delete(namespace)
if err != nil {
return err
}

adminClient, err := rancher.NewClient(client.RancherConfig.AdminToken, client.Session)
if err != nil {
return err
}
adminDynamicClient, err := adminClient.GetDownStreamClusterClient(installOptions.ClusterID)
if err != nil {
return err
}
adminNamespaceResource := adminDynamicClient.Resource(kubenamespaces.NamespaceGroupVersionResource).Namespace("")

watchNamespaceInterface, err := adminNamespaceResource.Watch(context.TODO(), metav1.ListOptions{
FieldSelector: metadataName + backupChartNamespace,
TimeoutSeconds: &defaults.WatchTimeoutSeconds,
})

if err != nil {
return err
}

return wait.WatchWait(watchNamespaceInterface, func(event watch.Event) (ready bool, err error) {
if event.Type == watch.Deleted {
return true, nil
}
return false, nil
})
})

err = catalogClient.InstallChart(chartInstallAction, catalog.RancherChartRepo)
if err != nil {
return err
}

watchAppInterface, err := catalogClient.Apps(backupChartNamespace).Watch(context.TODO(), metav1.ListOptions{
FieldSelector: metadataName + backupChartName,
TimeoutSeconds: &defaults.WatchTimeoutSeconds,
})
if err != nil {
return err
}

err = wait.WatchWait(watchAppInterface, func(event watch.Event) (ready bool, err error) {
app := event.Object.(*catalogv1.App)

state := app.Status.Summary.State
if state == string(catalogv1.StatusDeployed) {
return true, nil
}
return false, nil
})
if err != nil {
return err
}
return nil
}

// newBackupChartInstallAction is a private helper function that returns chart install action with backup and payload options.
func newBackupChartInstallAction(p *payloadOpts, withStorage bool, rancherBackupOpts *RancherBackupOpts) *types.ChartInstallAction {
// If BRO is installed without any storage options selected, then only the basic chart install options are sent
backupValues := map[string]interface{}{}
if withStorage {
backupValues = map[string]any{
"s3": map[string]any{
"bucketName": rancherBackupOpts.BucketName,
"credentialSecretName": rancherBackupOpts.CredentialSecretName,
"credentialSecretNamespace": rancherBackupOpts.CredentialSecretNamespace,
"enabled": rancherBackupOpts.Enabled,
"endpoint": rancherBackupOpts.Endpoint,
"folder": rancherBackupOpts.Folder,
"region": rancherBackupOpts.Region,
},
}
}
chartInstall := newChartInstall(p.Name, p.InstallOptions.Version, p.InstallOptions.ClusterID, p.InstallOptions.ClusterName, p.Host, rancherChartsName, p.ProjectID, p.DefaultRegistry, backupValues)
chartInstallCRD := newChartInstall(p.Name+"-crd", p.InstallOptions.Version, p.InstallOptions.ClusterID, p.InstallOptions.ClusterName, p.Host, rancherChartsName, p.ProjectID, p.DefaultRegistry, nil)
chartInstalls := []types.ChartInstall{*chartInstallCRD, *chartInstall}

chartInstallAction := newChartInstallAction(p.Namespace, p.ProjectID, chartInstalls)

return chartInstallAction
}

func VerifyBackupCompleted(t *testing.T, client *rancher.Client, steveType string, backup *v1.SteveAPIObject) (ready bool, err error) {
err = kwait.Poll(2*time.Second, 3*time.Minute, func() (done bool, err error) {
backupObj, err := client.Steve.SteveType(steveType).ByID(backup.ID)
backupStatus := &bv1.BackupStatus{}
error := v1.ConvertToK8sType(backupObj.Status, backupStatus)
require.NoError(t, error)
if err != nil {
return false, err
}
for _, condition := range backupStatus.Conditions {
if condition.Type == "Ready" && condition.Status == corev1.ConditionTrue {
ready = true
logrus.Infof("Backup is completed!")
}
}
return ready, nil
})
require.NoError(t, err)
return ready, nil
}

func VerifyRestoreCompleted(t *testing.T, client *rancher.Client, steveType string, restore *v1.SteveAPIObject) (ready bool, err error) {
err = kwait.Poll(2*time.Second, 20*time.Minute, func() (done bool, err error) {
restoreObj, err := client.Steve.SteveType(steveType).ByID(restore.ID)
restoreStatus := &bv1.RestoreStatus{}
error := v1.ConvertToK8sType(restoreObj.Status, restoreStatus)
require.NoError(t, error)
if err != nil {
return false, err
}
for _, condition := range restoreStatus.Conditions {
if condition.Type == "Ready" && condition.Status == corev1.ConditionTrue {
ready = true
logrus.Infof("Restore is completed!")
}
}
return ready, nil
})
require.NoError(t, err)
return ready, nil
}
26 changes: 26 additions & 0 deletions extensions/charts/backuprestoreconfig.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package charts

const (
ConfigurationFileKey = "broInput"
)

type Config struct {
BackupName string `json:"backupName" yaml:"backupName"`
S3BucketName string `json:"s3BucketName" yaml:"s3BucketName"`
S3FolderName string `json:"s3FolderName" yaml:"s3FolderName"`
S3Region string `json:"s3Region" yaml:"s3Region"`
S3Endpoint string `json:"s3Endpoint" yaml:"s3Endpoint"`
VolumeName string `json:"volumeName" yaml:"volumeName"`
CredentialSecretName string `json:"credentialSecretName" yaml:"credentialSecretName"`
CredentialSecretNamespace string `json:"credentialSecretNamespace" yaml:"credentialSecretNamespace"`
EndpointCA string `json:"endpointCA" yaml:"endpointCA"`
ResourceSetName string `json:"resourceSetName" yaml:"resourceSetName"`
EncryptionConfigSecretName string `json:"encryptionConfigSecretName" yaml:"encryptionConfigSecretName"`
Schedule string `json:"schedule" yaml:"schedule"`
TlsSkipVerify bool
Prune bool
RetentionCount int64
DeleteTimoutSeconds int
AccessKey string `json:"accessKey" yaml:"accessKey"`
SecretKey string `json:"secretKey" yaml:"secretKey"`
}
21 changes: 17 additions & 4 deletions extensions/charts/charts.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const (
serverURLSettingID = "server-url"
rancherChartsName = "rancher-charts"
active = "active"
metadataName = "metadata.name="
)

// InstallOptions is a struct of the required options to install a chart.
Expand Down Expand Up @@ -71,6 +72,18 @@ type RancherLoggingOpts struct {
AdditionalLoggingSources bool
}

// RancherBackupOpts is a struct of the required options to install Rancher Backups with desired chart values.
type RancherBackupOpts struct {
VolumeName string
BucketName string
CredentialSecretName string
CredentialSecretNamespace string
Enabled bool
Endpoint string
Folder string
Region string
}

// GetChartCaseEndpointResult is a struct that GetChartCaseEndpoint helper function returns.
// It contains the boolean for healthy response and the request body.
type GetChartCaseEndpointResult struct {
Expand Down Expand Up @@ -149,7 +162,7 @@ func WatchAndWaitDeployments(client *rancher.Client, clusterID, namespace string

for _, deployment := range deploymentList {
watchAppInterface, err := adminDeploymentResource.Watch(context.TODO(), metav1.ListOptions{
FieldSelector: "metadata.name=" + deployment.Name,
FieldSelector: metadataName + deployment.Name,
TimeoutSeconds: &defaults.WatchTimeoutSeconds,
})
if err != nil {
Expand Down Expand Up @@ -189,7 +202,7 @@ func WatchAndWaitDeploymentForAnnotation(client *rancher.Client, clusterID, name
adminDeploymentResource := adminDynamicClient.Resource(deployments.DeploymentGroupVersionResource).Namespace(namespace)

watchAppInterface, err := adminDeploymentResource.Watch(context.TODO(), metav1.ListOptions{
FieldSelector: "metadata.name=" + deploymentName,
FieldSelector: metadataName + deploymentName,
TimeoutSeconds: &defaults.WatchTimeoutSeconds,
})
if err != nil {
Expand Down Expand Up @@ -249,7 +262,7 @@ func WatchAndWaitDaemonSets(client *rancher.Client, clusterID, namespace string,

for _, daemonSet := range daemonSetList {
watchAppInterface, err := adminDaemonSetResource.Watch(context.TODO(), metav1.ListOptions{
FieldSelector: "metadata.name=" + daemonSet.Name,
FieldSelector: metadataName + daemonSet.Name,
TimeoutSeconds: &defaults.WatchTimeoutSeconds,
})
if err != nil {
Expand Down Expand Up @@ -307,7 +320,7 @@ func WatchAndWaitStatefulSets(client *rancher.Client, clusterID, namespace strin

for _, statefulSet := range statefulSetList {
watchAppInterface, err := adminStatefulSetResource.Watch(context.TODO(), metav1.ListOptions{
FieldSelector: "metadata.name=" + statefulSet.Name,
FieldSelector: metadataName + statefulSet.Name,
TimeoutSeconds: &defaults.WatchTimeoutSeconds,
})
if err != nil {
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ require (
github.com/pkg/sftp v1.13.5
github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.52.0
github.com/rancher/apiserver v0.0.0-20240207153521-4e102cf0d07b
github.com/rancher/backup-restore-operator v1.2.1
github.com/rancher/eks-operator v1.2.2
github.com/rancher/fleet/pkg/apis v0.0.0-20230901075223-437edb7091f5
github.com/rancher/gke-operator v1.1.6
Expand Down

0 comments on commit edc5621

Please sign in to comment.