-
Notifications
You must be signed in to change notification settings - Fork 65
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(cluster-backup): add apis for bsl
Signed-off-by: rajaSahil <[email protected]> feat(cluster-backup): add apis for bsl Signed-off-by: rajaSahil <[email protected]>
- Loading branch information
Showing
8 changed files
with
780 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
296 changes: 296 additions & 0 deletions
296
modules/api/pkg/ee/clusterbackup/backupstoragelocation/handler.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,296 @@ | ||
/* | ||
Copyright 2025 The Kubermatic Kubernetes Platform contributors. | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
package backupstoragelocation | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"net/http" | ||
|
||
"github.com/gorilla/mux" | ||
velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" | ||
|
||
apiv2 "k8c.io/dashboard/v2/pkg/api/v2" | ||
clusterbackup "k8c.io/dashboard/v2/pkg/ee/clusterbackup/backup" | ||
handlercommon "k8c.io/dashboard/v2/pkg/handler/common" | ||
"k8c.io/dashboard/v2/pkg/handler/v1/common" | ||
"k8c.io/dashboard/v2/pkg/handler/v2/cluster" | ||
"k8c.io/dashboard/v2/pkg/provider" | ||
kubermaticv1 "k8c.io/kubermatic/v2/pkg/apis/kubermatic/v1" | ||
|
||
corev1 "k8s.io/api/core/v1" | ||
apierrors "k8s.io/apimachinery/pkg/api/errors" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/types" | ||
"k8s.io/apiserver/pkg/storage/names" | ||
ctrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client" | ||
) | ||
|
||
const ( | ||
displayNameLabelKey = "bsl-display-name" | ||
credentialsSecretKeyName = "cloud-credentials" | ||
clusterIdLabelKey = "cluster-id" | ||
) | ||
|
||
// createBSLReq defines HTTP request for createBSL | ||
// swagger:parameters createClusterBackupStorageLocation | ||
type createBslReq struct { | ||
cluster.GetClusterReq | ||
|
||
// in: body | ||
// required: true | ||
Body BslBody | ||
} | ||
|
||
type BslBody struct { | ||
// Name of the cluster backup | ||
Name string `json:"name,omitempty"` | ||
// Spec of a Velero cluster backup | ||
Credentials apiv2.BackupCredentials `json:"credentials,omitempty"` | ||
BSLSpec velerov1.BackupStorageLocationSpec `json:"cbslSpec,omitempty"` | ||
} | ||
|
||
func DecodeCreateBSLReq(c context.Context, r *http.Request) (interface{}, error) { | ||
var req createBslReq | ||
cr, err := cluster.DecodeGetClusterReq(c, r) | ||
|
||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
req.GetClusterReq = cr.(cluster.GetClusterReq) | ||
|
||
if err = json.NewDecoder(r.Body).Decode(&req.Body); err != nil { | ||
return nil, err | ||
} | ||
return req, nil | ||
} | ||
|
||
func CreateBSLEndpoint(ctx context.Context, request interface{}, userInfoGetter provider.UserInfoGetter, projectProvider provider.ProjectProvider, privilegedProjectProvider provider.PrivilegedProjectProvider, settingsProvider provider.SettingsProvider) (interface{}, error) { | ||
if err := clusterbackup.IsClusterBackupEnabled(ctx, settingsProvider); err != nil { | ||
return nil, err | ||
} | ||
|
||
req := request.(createBslReq) | ||
client, err := handlercommon.GetClusterClientWithClusterID(ctx, userInfoGetter, projectProvider, privilegedProjectProvider, req.ProjectID, req.ClusterID) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
bslName := req.Body.Name | ||
bslSpec := req.Body.BSLSpec.DeepCopy() | ||
creds := req.Body.Credentials | ||
bsl := &velerov1.BackupStorageLocation{ | ||
Spec: *bslSpec, | ||
} | ||
|
||
bsl, err = createBSL(ctx, client, bslName, req.ProjectID, req.ClusterID, bsl, &creds) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return bsl, nil | ||
} | ||
|
||
type GetBslReq struct { | ||
cluster.GetClusterReq | ||
//in: path | ||
// required: true | ||
BslName string `json:"bsl_name"` | ||
} | ||
|
||
func DecodeGetBSLReq(c context.Context, r *http.Request) (interface{}, error) { | ||
var req GetBslReq | ||
|
||
cr, err := cluster.DecodeGetClusterReq(c, r) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
req.GetClusterReq = cr.(cluster.GetClusterReq) | ||
|
||
req.BslName = mux.Vars(r)["bsl_name"] | ||
if req.BslName == "" { | ||
return nil, fmt.Errorf("'bsl_name' parameter is required but was not provided") | ||
} | ||
return req, nil | ||
} | ||
|
||
func GetBSLEndpoint(ctx context.Context, request interface{}, userInfoGetter provider.UserInfoGetter, projectProvider provider.ProjectProvider, privilegedProjectProvider provider.PrivilegedProjectProvider, settingsProvider provider.SettingsProvider) (interface{}, error) { | ||
if err := clusterbackup.IsClusterBackupEnabled(ctx, settingsProvider); err != nil { | ||
return nil, err | ||
} | ||
|
||
req := request.(GetBslReq) | ||
|
||
client, err := handlercommon.GetClusterClientWithClusterID(ctx, userInfoGetter, projectProvider, privilegedProjectProvider, req.ProjectID, req.ClusterID) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
bsl := &velerov1.BackupStorageLocation{} | ||
if err := client.Get(ctx, types.NamespacedName{Name: req.BslName, Namespace: clusterbackup.UserClusterBackupNamespace}, bsl); err != nil { | ||
return nil, common.KubernetesErrorToHTTPError(err) | ||
} | ||
return bsl, nil | ||
} | ||
|
||
type listBslReq struct { | ||
cluster.GetClusterReq | ||
} | ||
|
||
func DecodeListBSLReq(c context.Context, r *http.Request) (interface{}, error) { | ||
var req listBslReq | ||
|
||
cr, err := cluster.DecodeGetClusterReq(c, r) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
req.GetClusterReq = cr.(cluster.GetClusterReq) | ||
return req, nil | ||
} | ||
|
||
func ListBSLEndpoint(ctx context.Context, request interface{}, userInfoGetter provider.UserInfoGetter, projectProvider provider.ProjectProvider, privilegedProjectProvider provider.PrivilegedProjectProvider, settingsProvider provider.SettingsProvider) (interface{}, error) { | ||
if err := clusterbackup.IsClusterBackupEnabled(ctx, settingsProvider); err != nil { | ||
return nil, err | ||
} | ||
|
||
req := request.(listBslReq) | ||
client, err := handlercommon.GetClusterClientWithClusterID(ctx, userInfoGetter, projectProvider, privilegedProjectProvider, req.ProjectID, req.ClusterID) | ||
if err != nil { | ||
return nil, err | ||
} | ||
bslList := &velerov1.BackupStorageLocationList{} | ||
|
||
if err := client.List(ctx, bslList, ctrlruntimeclient.InNamespace(clusterbackup.UserClusterBackupNamespace)); err != nil { | ||
return nil, common.KubernetesErrorToHTTPError(err) | ||
} | ||
|
||
return bslList, nil | ||
} | ||
|
||
type deleteBSLReq struct { | ||
cluster.GetClusterReq | ||
// in: body | ||
BslName string `json:"bsl_name"` | ||
} | ||
|
||
func DecodeDeleteBSLReq(c context.Context, r *http.Request) (interface{}, error) { | ||
var req deleteBSLReq | ||
cr, err := cluster.DecodeGetClusterReq(c, r) | ||
if err != nil { | ||
return nil, err | ||
} | ||
req.GetClusterReq = cr.(cluster.GetClusterReq) | ||
|
||
req.BslName = mux.Vars(r)["bsl_name"] | ||
if req.BslName == "" { | ||
return nil, fmt.Errorf("'bsl_name' parameter is required but was not provided") | ||
} | ||
return req, nil | ||
} | ||
|
||
func DeleteBSLEndpoint(ctx context.Context, request interface{}, userInfoGetter provider.UserInfoGetter, projectProvider provider.ProjectProvider, privilegedProjectProvider provider.PrivilegedProjectProvider, settingsProvider provider.SettingsProvider) (interface{}, error) { | ||
if err := clusterbackup.IsClusterBackupEnabled(ctx, settingsProvider); err != nil { | ||
return nil, err | ||
} | ||
|
||
req := request.(deleteBSLReq) | ||
|
||
client, err := handlercommon.GetClusterClientWithClusterID(ctx, userInfoGetter, projectProvider, privilegedProjectProvider, req.ProjectID, req.ClusterID) | ||
if err != nil { | ||
return nil, err | ||
} | ||
bsl := &velerov1.BackupStorageLocation{} | ||
if err := client.Get(ctx, types.NamespacedName{Name: req.BslName, Namespace: clusterbackup.UserClusterBackupNamespace}, bsl); err != nil { | ||
if apierrors.IsNotFound(err) { | ||
return nil, nil | ||
} | ||
return nil, err | ||
} | ||
if err := client.Delete(ctx, bsl); err != nil { | ||
return nil, common.KubernetesErrorToHTTPError(err) | ||
} | ||
return nil, nil | ||
} | ||
|
||
func createBSL(ctx context.Context, client ctrlruntimeclient.Client, bslName, projectID, clusterID string, bsl *velerov1.BackupStorageLocation, credentials *apiv2.BackupCredentials) (*velerov1.BackupStorageLocation, error) { | ||
bslFullName := fmt.Sprintf("%s-%s-%s", bslName, projectID, clusterID) | ||
secretName := fmt.Sprintf("credential-%s-", bslFullName) | ||
|
||
secret := &corev1.Secret{} | ||
if credentials != nil { | ||
secret = &corev1.Secret{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: names.SimpleNameGenerator.GenerateName(secretName), | ||
Namespace: clusterbackup.UserClusterBackupNamespace, | ||
Labels: getBSLLabels(bslFullName, projectID, clusterID), | ||
}, | ||
Data: map[string][]byte{ | ||
"accessKeyId": []byte(credentials.S3BackupCredentials.AccessKeyID), | ||
"secretAccessKey": []byte(credentials.S3BackupCredentials.SecretAccessKey), | ||
}, | ||
} | ||
} | ||
|
||
bsl.ObjectMeta = metav1.ObjectMeta{ | ||
Name: bslFullName, | ||
Namespace: clusterbackup.UserClusterBackupNamespace, | ||
Labels: getBSLLabels(bslName, projectID, clusterID), | ||
} | ||
|
||
if credentials != nil { | ||
bsl.Spec.Credential = &corev1.SecretKeySelector{ | ||
LocalObjectReference: corev1.LocalObjectReference{ | ||
Name: secret.Name, | ||
}, | ||
Key: credentialsSecretKeyName, | ||
} | ||
} | ||
|
||
if err := client.Create(ctx, bsl); err != nil { | ||
return nil, common.KubernetesErrorToHTTPError(err) | ||
} | ||
|
||
if credentials != nil { | ||
ownerReferences := []metav1.OwnerReference{ | ||
{ | ||
APIVersion: bsl.APIVersion, | ||
Kind: bsl.Kind, | ||
Name: bslFullName, | ||
UID: bsl.UID, | ||
}, | ||
} | ||
|
||
secret.ObjectMeta.OwnerReferences = ownerReferences | ||
|
||
if err := client.Create(ctx, secret); err != nil { | ||
return nil, err | ||
} | ||
} | ||
return bsl, nil | ||
} | ||
|
||
func getBSLLabels(displayName, projectID, clusterID string) map[string]string { | ||
return map[string]string{ | ||
kubermaticv1.ProjectIDLabelKey: projectID, | ||
clusterIdLabelKey: clusterID, | ||
displayNameLabelKey: displayName, | ||
} | ||
} |
Oops, something went wrong.