Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
533 changes: 533 additions & 0 deletions docs/references/HCPEtcdBackup/HCPEtcdBackup-implementation.md

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ require (
github.com/kubernetes-csi/external-snapshotter/client/v8 v8.4.0
github.com/onsi/gomega v1.39.1
github.com/openshift/hive/apis v0.0.0-20241220022629-3f49f26197ff
github.com/openshift/hypershift/api v0.0.0-20260317154635-8eaac177f1b0
github.com/openshift/hypershift/api v0.0.0-20260410203959-783f7956d4f9
github.com/sirupsen/logrus v1.9.4
github.com/vmware-tanzu/velero v1.14.0
k8s.io/api v0.35.3
Expand All @@ -26,7 +26,7 @@ require (
github.com/google/gnostic-models v0.7.0 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/openshift/api v0.0.0-20260120150926-4c643a652d54 // indirect
github.com/openshift/api v0.0.0-20260304122341-cf5d8996109f // indirect
github.com/openshift/custom-resource-status v1.1.3-0.20220503160415-f2fdb4999d87 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.23.2 // indirect
Expand Down
12 changes: 6 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -192,23 +192,23 @@ github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9k
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
github.com/onsi/ginkgo/v2 v2.28.0 h1:Rrf+lVLmtlBIKv6KrIGJCjyY8N36vDVcutbGJkyqjJc=
github.com/onsi/ginkgo/v2 v2.28.0/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo=
github.com/onsi/ginkgo/v2 v2.28.1 h1:S4hj+HbZp40fNKuLUQOYLDgZLwNUVn19N3Atb98NCyI=
github.com/onsi/ginkgo/v2 v2.28.1/go.mod h1:CLtbVInNckU3/+gC8LzkGUb9oF+e8W8TdUsxPwvdOgE=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
github.com/onsi/gomega v1.39.1 h1:1IJLAad4zjPn2PsnhH70V4DKRFlrCzGBNrNaru+Vf28=
github.com/onsi/gomega v1.39.1/go.mod h1:hL6yVALoTOxeWudERyfppUcZXjMwIMLnuSfruD2lcfg=
github.com/openshift/api v0.0.0-20260120150926-4c643a652d54 h1:Gm81lfkiXFgg/N0x90WsplERGF2NqYjK0vd8YY/aFpU=
github.com/openshift/api v0.0.0-20260120150926-4c643a652d54/go.mod h1:d5uzF0YN2nQQFA0jIEWzzOZ+edmo6wzlGLvx5Fhz4uY=
github.com/openshift/api v0.0.0-20260304122341-cf5d8996109f h1:M8y0oBq/KRkuSNFlUMQRAn2MrXJh1mzTCFgbLpPWQbM=
github.com/openshift/api v0.0.0-20260304122341-cf5d8996109f/go.mod h1:d5uzF0YN2nQQFA0jIEWzzOZ+edmo6wzlGLvx5Fhz4uY=
github.com/openshift/custom-resource-status v1.1.3-0.20220503160415-f2fdb4999d87 h1:cHyxR+Y8rAMT6m1jQCaYGRwikqahI0OjjUDhFNf3ySQ=
github.com/openshift/custom-resource-status v1.1.3-0.20220503160415-f2fdb4999d87/go.mod h1:DB/Mf2oTeiAmVVX1gN+NEqweonAPY0TKUwADizj8+ZA=
github.com/openshift/hive/apis v0.0.0-20241220022629-3f49f26197ff h1:6C1z4xMAruyeiTFGqahxNDpI1cXPCjpaFeIeIodty08=
github.com/openshift/hive/apis v0.0.0-20241220022629-3f49f26197ff/go.mod h1:1vBNCcWNpQyFCz83PWYT/lHUFJ9ost2t5FijHElh6gQ=
github.com/openshift/hypershift/api v0.0.0-20260317154635-8eaac177f1b0 h1:x5DgHyFXF9zpgTH4JYDpBhQnASEIj6z1WXdeHl1DGAI=
github.com/openshift/hypershift/api v0.0.0-20260317154635-8eaac177f1b0/go.mod h1:eYDwJzXCU+0HO9DdvhBAA143z7woIJ5dV71TaJXIkgk=
github.com/openshift/hypershift/api v0.0.0-20260410203959-783f7956d4f9 h1:mVzLud8ewZi1W8dnV37L+eY9IJsoFfpkASgPvDRM61Q=
github.com/openshift/hypershift/api v0.0.0-20260410203959-783f7956d4f9/go.mod h1:mC9+bqb81FmG924VOO+7P0ofKQgvY1SdPhFp0oJ9U44=
github.com/openshift/velero v0.10.2-0.20260323170432-5ef912f438f6 h1:nWv9+tEi34VMFMOziVZLOKvn3yXDqk68ODXlzpYU2S0=
github.com/openshift/velero v0.10.2-0.20260323170432-5ef912f438f6/go.mod h1:YKHPM2FpS+5WrcciMi5j4bo/JMnXXrR1M+j1DoTA26U=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
Expand Down
4 changes: 4 additions & 0 deletions pkg/common/scheme.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
veleroapiv1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
veleroapiv2alpha1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1"
corev1 "k8s.io/api/core/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/runtime"
)

Expand Down Expand Up @@ -35,6 +36,9 @@ func init() {
if err := hive.AddToScheme(CustomScheme); err != nil {
errs = append(errs, err)
}
if err := apiextensionsv1.AddToScheme(CustomScheme); err != nil {
errs = append(errs, err)
}

if len(errs) > 0 {
panic(errs)
Expand Down
22 changes: 22 additions & 0 deletions pkg/common/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ const (

// Integration with Hypershift, more info here: https://github.com/openshift/hypershift/pull/6195
HostedClusterRestoredFromBackupAnnotation string = "hypershift.openshift.io/restored-from-backup"
// Etcd snapshot URL annotation: set during backup so the restore plugin can read it
// (Velero strips status from items during restore, so we persist it as an annotation)
EtcdSnapshotURLAnnotation string = "hypershift.openshift.io/etcd-snapshot-url"

// hypershift/cluster-api kinds
HostedClusterKind string = "HostedCluster"
Expand All @@ -25,6 +28,25 @@ const (
PersistentVolumeClaimKind string = "PersistentVolumeClaim"
ClusterDeploymentKind string = "ClusterDeployment"
DataVolumeKind string = "DataVolume"
HCPEtcdBackupKind string = "HCPEtcdBackup"

// Default HyperShift Operator namespace
DefaultHONamespace string = "hypershift"
// ConfigMap key to override the HO namespace
ConfigKeyHONamespace string = "hoNamespace"

// Etcd backup method configuration
ConfigKeyEtcdBackupMethod string = "etcdBackupMethod"
EtcdBackupMethodVolume string = "volumeSnapshot"
EtcdBackupMethodEtcdSnapshot string = "etcdSnapshot"

// Velero annotation to exclude specific volumes from backup
BackupVolumesExcludesAnnotation string = "backup.velero.io/backup-volumes-excludes"
// Etcd data volume name in the StatefulSet pod
EtcdDataVolumeName string = "data"
// Etcd PVC name prefix (StatefulSet pattern: {volumeName}-{stsName}-{index})
EtcdPVCPrefix string = "data-etcd-"

)

var (
Expand Down
27 changes: 27 additions & 0 deletions pkg/common/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package common

import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
Expand Down Expand Up @@ -163,6 +164,32 @@ func GetHCPNamespace(name, namespace string) string {
return fmt.Sprintf("%s-%s", namespace, name)
}

// GetHostedCluster finds the HostedCluster that owns the HCP by deriving
// its namespace and name from the HCP namespace convention: {hc-namespace}-{hc-name}.
func GetHostedCluster(ctx context.Context, c crclient.Client, includedNamespaces []string, hcpNamespace string) (*hyperv1.HostedCluster, error) {
var errs []error
for _, ns := range includedNamespaces {
if ns == hcpNamespace {
continue
}
hcList := &hyperv1.HostedClusterList{}
if err := c.List(ctx, hcList, crclient.InNamespace(ns)); err != nil {
errs = append(errs, fmt.Errorf("list HostedClusters in namespace %s: %w", ns, err))
continue
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
for i := range hcList.Items {
hc := &hcList.Items[i]
if GetHCPNamespace(hc.Name, hc.Namespace) == hcpNamespace {
return hc, nil
}
}
}
if len(errs) > 0 {
return nil, errors.Join(errs...)
}
return nil, nil
}

// ShouldEndPluginExecution checks if the plugin should end execution by verifying if the required
// Hypershift resources (HostedControlPlane and HostedCluster) exist in the cluster.
// Returns true if the plugin should end execution (i.e., if this is not a Hypershift cluster).
Expand Down
28 changes: 28 additions & 0 deletions pkg/common/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,34 @@ func TestShouldEndPluginExecution(t *testing.T) {
}
}

func TestGetHostedCluster(t *testing.T) {
scheme := runtime.NewScheme()
_ = hyperv1.AddToScheme(scheme)

t.Run("When GetHostedCluster runs with a HostedCluster matching HCP namespace, It Should return that cluster", func(t *testing.T) {
g := NewWithT(t)
hc := &hyperv1.HostedCluster{
ObjectMeta: metav1.ObjectMeta{Name: "my-cluster", Namespace: "clusters"},
}
c := fake.NewClientBuilder().WithScheme(scheme).WithObjects(hc).Build()

result, err := GetHostedCluster(context.TODO(), c, []string{"clusters", "clusters-my-cluster"}, "clusters-my-cluster")
g.Expect(err).NotTo(HaveOccurred())
g.Expect(result).NotTo(BeNil())
g.Expect(result.Name).To(Equal("my-cluster"))
g.Expect(result.Namespace).To(Equal("clusters"))
})

t.Run("When GetHostedCluster runs with no HostedClusters in client, It Should return nil", func(t *testing.T) {
g := NewWithT(t)
c := fake.NewClientBuilder().WithScheme(scheme).Build()

result, err := GetHostedCluster(context.TODO(), c, []string{"clusters", "clusters-my-cluster"}, "clusters-my-cluster")
g.Expect(err).NotTo(HaveOccurred())
g.Expect(result).To(BeNil())
})
}

func TestCRDExists(t *testing.T) {
scheme := runtime.NewScheme()
_ = hyperv1.AddToScheme(scheme)
Expand Down
Loading