Skip to content

Commit 569c801

Browse files
committed
feat(backingimage): backing image clone and encryption support
ref: longhorn/longhorn 7051 Signed-off-by: Jack Lin <[email protected]>
1 parent 55b8b80 commit 569c801

22 files changed

+440
-34
lines changed

api/backingimage.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ func (s *Server) BackingImageCreate(rw http.ResponseWriter, req *http.Request) e
5252
return err
5353
}
5454

55-
bi, err := s.m.CreateBackingImage(input.Name, input.ExpectedChecksum, input.SourceType, input.Parameters)
55+
bi, err := s.m.CreateBackingImage(input.Name, input.ExpectedChecksum, input.SourceType, input.Parameters, input.Secret, input.SecretNamespace)
5656
if err != nil {
5757
return errors.Wrapf(err, "failed to create backing image %v from source type %v with parameters %+v", input.Name, input.SourceType, input.Parameters)
5858
}

api/backupbackingimage.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,14 @@ func (s *Server) BackupBackingImageDelete(w http.ResponseWriter, req *http.Reque
4040
}
4141

4242
func (s *Server) BackupBackingImageRestore(w http.ResponseWriter, req *http.Request) error {
43+
var input BackingImageRestoreInput
44+
apiContext := api.GetApiContext(req)
45+
if err := apiContext.Read(&input); err != nil {
46+
return err
47+
}
48+
4349
backupBackingImageName := mux.Vars(req)["name"]
44-
if err := s.m.RestoreBackupBackingImage(backupBackingImageName); err != nil {
50+
if err := s.m.RestoreBackupBackingImage(backupBackingImageName, input.Secret, input.SecretNamespace); err != nil {
4551
return errors.Wrapf(err, "failed to restore backup backing image '%s'", backupBackingImageName)
4652
}
4753
return nil

api/model.go

+15-1
Original file line numberDiff line numberDiff line change
@@ -268,13 +268,21 @@ type BackingImage struct {
268268
Size int64 `json:"size"`
269269
CurrentChecksum string `json:"currentChecksum"`
270270

271+
Secret string `json:"secret"`
272+
SecretNamespace string `json:"secretNamespace"`
273+
271274
DeletionTimestamp string `json:"deletionTimestamp"`
272275
}
273276

274277
type BackingImageCleanupInput struct {
275278
Disks []string `json:"disks"`
276279
}
277280

281+
type BackingImageRestoreInput struct {
282+
Secret string `json:"secret"`
283+
SecretNamespace string `json:"secretNamespace"`
284+
}
285+
278286
type AttachInput struct {
279287
HostID string `json:"hostId"`
280288
DisableFrontend bool `json:"disableFrontend"`
@@ -668,6 +676,7 @@ func NewSchema() *client.Schemas {
668676

669677
schemas.AddType("backingImageDiskFileStatus", longhorn.BackingImageDiskFileStatus{})
670678
schemas.AddType("backingImageCleanupInput", BackingImageCleanupInput{})
679+
schemas.AddType("backingImageRestoreInput", BackingImageRestoreInput{})
671680

672681
attachmentSchema(schemas.AddType("attachment", Attachment{}))
673682
volumeAttachmentSchema(schemas.AddType("volumeAttachment", VolumeAttachment{}))
@@ -798,7 +807,9 @@ func backupBackingImageSchema(backupBackingImage *client.Schema) {
798807
backupBackingImage.ResourceMethods = []string{"GET", "DELETE"}
799808

800809
backupBackingImage.ResourceActions = map[string]client.Action{
801-
"backupBackingImageRestore": {},
810+
"backupBackingImageRestore": {
811+
Input: "backingImageRestoreInput",
812+
},
802813
}
803814
}
804815

@@ -2004,6 +2015,9 @@ func toBackingImageResource(bi *longhorn.BackingImage, apiContext *api.ApiContex
20042015
Size: bi.Status.Size,
20052016
CurrentChecksum: bi.Status.Checksum,
20062017

2018+
Secret: bi.Spec.Secret,
2019+
SecretNamespace: bi.Spec.SecretNamespace,
2020+
20072021
DeletionTimestamp: deletionTimestamp,
20082022
}
20092023
res.Actions = map[string]string{

client/generated_backing_image.go

+4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ type BackingImage struct {
1919

2020
Parameters map[string]string `json:"parameters,omitempty" yaml:"parameters,omitempty"`
2121

22+
Secret string `json:"secret,omitempty" yaml:"secret,omitempty"`
23+
24+
SecretNamespace string `json:"secretNamespace,omitempty" yaml:"secretNamespace,omitempty"`
25+
2226
Size int64 `json:"size,omitempty" yaml:"size,omitempty"`
2327

2428
SourceType string `json:"sourceType,omitempty" yaml:"source_type,omitempty"`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package client
2+
3+
const (
4+
BACKING_IMAGE_RESTORE_INPUT_TYPE = "backingImageRestoreInput"
5+
)
6+
7+
type BackingImageRestoreInput struct {
8+
Resource `yaml:"-"`
9+
10+
Secret string `json:"secret,omitempty" yaml:"secret,omitempty"`
11+
12+
SecretNamespace string `json:"secretNamespace,omitempty" yaml:"secret_namespace,omitempty"`
13+
}
14+
15+
type BackingImageRestoreInputCollection struct {
16+
Collection
17+
Data []BackingImageRestoreInput `json:"data,omitempty"`
18+
client *BackingImageRestoreInputClient
19+
}
20+
21+
type BackingImageRestoreInputClient struct {
22+
rancherClient *RancherClient
23+
}
24+
25+
type BackingImageRestoreInputOperations interface {
26+
List(opts *ListOpts) (*BackingImageRestoreInputCollection, error)
27+
Create(opts *BackingImageRestoreInput) (*BackingImageRestoreInput, error)
28+
Update(existing *BackingImageRestoreInput, updates interface{}) (*BackingImageRestoreInput, error)
29+
ById(id string) (*BackingImageRestoreInput, error)
30+
Delete(container *BackingImageRestoreInput) error
31+
}
32+
33+
func newBackingImageRestoreInputClient(rancherClient *RancherClient) *BackingImageRestoreInputClient {
34+
return &BackingImageRestoreInputClient{
35+
rancherClient: rancherClient,
36+
}
37+
}
38+
39+
func (c *BackingImageRestoreInputClient) Create(container *BackingImageRestoreInput) (*BackingImageRestoreInput, error) {
40+
resp := &BackingImageRestoreInput{}
41+
err := c.rancherClient.doCreate(BACKING_IMAGE_RESTORE_INPUT_TYPE, container, resp)
42+
return resp, err
43+
}
44+
45+
func (c *BackingImageRestoreInputClient) Update(existing *BackingImageRestoreInput, updates interface{}) (*BackingImageRestoreInput, error) {
46+
resp := &BackingImageRestoreInput{}
47+
err := c.rancherClient.doUpdate(BACKING_IMAGE_RESTORE_INPUT_TYPE, &existing.Resource, updates, resp)
48+
return resp, err
49+
}
50+
51+
func (c *BackingImageRestoreInputClient) List(opts *ListOpts) (*BackingImageRestoreInputCollection, error) {
52+
resp := &BackingImageRestoreInputCollection{}
53+
err := c.rancherClient.doList(BACKING_IMAGE_RESTORE_INPUT_TYPE, opts, resp)
54+
resp.client = c
55+
return resp, err
56+
}
57+
58+
func (cc *BackingImageRestoreInputCollection) Next() (*BackingImageRestoreInputCollection, error) {
59+
if cc != nil && cc.Pagination != nil && cc.Pagination.Next != "" {
60+
resp := &BackingImageRestoreInputCollection{}
61+
err := cc.client.rancherClient.doNext(cc.Pagination.Next, resp)
62+
resp.client = cc.client
63+
return resp, err
64+
}
65+
return nil, nil
66+
}
67+
68+
func (c *BackingImageRestoreInputClient) ById(id string) (*BackingImageRestoreInput, error) {
69+
resp := &BackingImageRestoreInput{}
70+
err := c.rancherClient.doById(BACKING_IMAGE_RESTORE_INPUT_TYPE, id, resp)
71+
if apiError, ok := err.(*ApiError); ok {
72+
if apiError.StatusCode == 404 {
73+
return nil, nil
74+
}
75+
}
76+
return resp, err
77+
}
78+
79+
func (c *BackingImageRestoreInputClient) Delete(container *BackingImageRestoreInput) error {
80+
return c.rancherClient.doResourceDelete(BACKING_IMAGE_RESTORE_INPUT_TYPE, &container.Resource)
81+
}

client/generated_client.go

+2
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ type RancherClient struct {
5757
InstanceManager InstanceManagerOperations
5858
BackingImageDiskFileStatus BackingImageDiskFileStatusOperations
5959
BackingImageCleanupInput BackingImageCleanupInputOperations
60+
BackingImageRestoreInput BackingImageRestoreInputOperations
6061
Attachment AttachmentOperations
6162
VolumeAttachment VolumeAttachmentOperations
6263
Volume VolumeOperations
@@ -138,6 +139,7 @@ func constructClient(rancherBaseClient *RancherBaseClientImpl) *RancherClient {
138139
client.InstanceManager = newInstanceManagerClient(client)
139140
client.BackingImageDiskFileStatus = newBackingImageDiskFileStatusClient(client)
140141
client.BackingImageCleanupInput = newBackingImageCleanupInputClient(client)
142+
client.BackingImageRestoreInput = newBackingImageRestoreInputClient(client)
141143
client.Attachment = newAttachmentClient(client)
142144
client.VolumeAttachment = newVolumeAttachmentClient(client)
143145
client.Volume = newVolumeClient(client)

controller/backing_image_controller.go

+32
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,15 @@ func (bic *BackingImageController) handleBackingImageDataSource(bi *longhorn.Bac
401401
if err != nil {
402402
return err
403403
}
404+
405+
// For clone, we choose the same node and disk as the source backing image
406+
if bi.Spec.SourceType == longhorn.BackingImageDataSourceTypeClone {
407+
readyNode, readyDiskName, err = bic.findReadyNodeAndDiskForClone(bi)
408+
if err != nil {
409+
return nil
410+
}
411+
}
412+
404413
foundReadyDisk = true
405414
readyNodeID = readyNode.Name
406415
readyDiskUUID = readyNode.Status.DiskStatus[readyDiskName].DiskUUID
@@ -532,6 +541,15 @@ func (bic *BackingImageController) handleBackingImageDataSource(bi *longhorn.Bac
532541
if err != nil {
533542
return err
534543
}
544+
545+
// For clone, we choose the same node and disk as the source backing image
546+
if bi.Spec.SourceType == longhorn.BackingImageDataSourceTypeClone {
547+
readyNode, readyDiskName, err = bic.findReadyNodeAndDiskForClone(bi)
548+
if err != nil {
549+
return nil
550+
}
551+
}
552+
535553
bids.Spec.NodeID = readyNode.Name
536554
bids.Spec.DiskUUID = readyNode.Status.DiskStatus[readyDiskName].DiskUUID
537555
bids.Spec.DiskPath = readyNode.Spec.Disks[readyDiskName].Path
@@ -854,3 +872,17 @@ func (bic *BackingImageController) enqueueBackingImageForReplica(obj interface{}
854872
func (bic *BackingImageController) isResponsibleFor(bi *longhorn.BackingImage) bool {
855873
return isControllerResponsibleFor(bic.controllerID, bic.ds, bi.Name, "", bi.Status.OwnerID)
856874
}
875+
876+
// For cloning, we choose the same node and disk as the source backing image
877+
func (bic *BackingImageController) findReadyNodeAndDiskForClone(bi *longhorn.BackingImage) (*longhorn.Node, string, error) {
878+
sourceBackingImageName := bi.Spec.SourceParameters[longhorn.DataSourceTypeCloneParameterBackingImage]
879+
sourceBackingImage, err := bic.ds.GetBackingImageRO(sourceBackingImageName)
880+
if err != nil {
881+
return nil, "", fmt.Errorf("failed to get source backing image %v during cloning", sourceBackingImageName)
882+
}
883+
readyNode, readyDiskName, err := bic.ds.GetOneBackingImageReadyNodeDisk(sourceBackingImage)
884+
if err != nil {
885+
return nil, "", fmt.Errorf("failed to find one ready source backing image %v during cloning", sourceBackingImageName)
886+
}
887+
return readyNode, readyDiskName, nil
888+
}

controller/backing_image_data_source_controller.go

+80-3
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,17 @@ func (c *BackingImageDataSourceController) syncBackingImage(bids *longhorn.Backi
405405
}
406406
}
407407

408+
// Only copy the secret to spec if it is to encrypt other backing image
409+
// because we use spec secret to check if it is encrypted.
410+
if isEncryptionRequire(bi) {
411+
if bi.Spec.SourceParameters[longhorn.DataSourceTypeCloneParameterSecret] != "" {
412+
bi.Spec.Secret = bi.Spec.SourceParameters[longhorn.DataSourceTypeCloneParameterSecret]
413+
}
414+
if bi.Spec.SourceParameters[longhorn.DataSourceTypeCloneParameterSecretNamespace] != "" {
415+
bi.Spec.SecretNamespace = bi.Spec.SourceParameters[longhorn.DataSourceTypeCloneParameterSecretNamespace]
416+
}
417+
}
418+
408419
return nil
409420
}
410421

@@ -669,7 +680,11 @@ func (c *BackingImageDataSourceController) generateBackingImageDataSourcePodMani
669680
"--source-type", string(bids.Spec.SourceType),
670681
}
671682

672-
if err := c.prepareRunningParameters(bids); err != nil {
683+
bids.Status.RunningParameters = bids.Spec.Parameters
684+
if err := c.prepareRunningParametersForClone(bids); err != nil {
685+
return nil, err
686+
}
687+
if err := c.prepareRunningParametersForExport(bids); err != nil {
673688
return nil, err
674689
}
675690
for key, value := range bids.Status.RunningParameters {
@@ -679,6 +694,21 @@ func (c *BackingImageDataSourceController) generateBackingImageDataSourcePodMani
679694
cmd = append(cmd, "--checksum", bids.Spec.Checksum)
680695
}
681696

697+
if bids.Spec.SourceType == longhorn.BackingImageDataSourceTypeClone && secretExists(bids) {
698+
699+
credential, err := c.ds.GetEncryptionSecret(
700+
bids.Spec.Parameters[longhorn.DataSourceTypeCloneParameterSecretNamespace],
701+
bids.Spec.Parameters[longhorn.DataSourceTypeCloneParameterSecret],
702+
)
703+
if err != nil {
704+
return nil, err
705+
}
706+
707+
for key, value := range credential {
708+
cmd = append(cmd, "--credential", fmt.Sprintf("%s=%s", key, value))
709+
}
710+
}
711+
682712
if bids.Spec.SourceType == longhorn.BackingImageDataSourceTypeRestore {
683713
var credential map[string]string
684714
backupTarget, err := c.ds.GetBackupTargetRO(types.DefaultBackupTargetName)
@@ -745,6 +775,14 @@ func (c *BackingImageDataSourceController) generateBackingImageDataSourcePodMani
745775
Name: "disk-path",
746776
MountPath: bimtypes.DiskPathInContainer,
747777
},
778+
{
779+
Name: "host-dev",
780+
MountPath: "/dev",
781+
},
782+
{
783+
Name: "host-proc",
784+
MountPath: "/host/proc", // we use this to enter the host namespace
785+
},
748786
},
749787
Env: []corev1.EnvVar{
750788
{
@@ -770,6 +808,22 @@ func (c *BackingImageDataSourceController) generateBackingImageDataSourcePodMani
770808
},
771809
},
772810
},
811+
{
812+
Name: "host-dev",
813+
VolumeSource: corev1.VolumeSource{
814+
HostPath: &corev1.HostPathVolumeSource{
815+
Path: "/dev",
816+
},
817+
},
818+
},
819+
{
820+
Name: "host-proc",
821+
VolumeSource: corev1.VolumeSource{
822+
HostPath: &corev1.HostPathVolumeSource{
823+
Path: "/proc",
824+
},
825+
},
826+
},
773827
},
774828
NodeName: bids.Spec.NodeID,
775829
RestartPolicy: corev1.RestartPolicyNever,
@@ -802,8 +856,21 @@ func (c *BackingImageDataSourceController) generateBackingImageDataSourcePodMani
802856
return podSpec, nil
803857
}
804858

805-
func (c *BackingImageDataSourceController) prepareRunningParameters(bids *longhorn.BackingImageDataSource) error {
806-
bids.Status.RunningParameters = bids.Spec.Parameters
859+
func (c *BackingImageDataSourceController) prepareRunningParametersForClone(bids *longhorn.BackingImageDataSource) error {
860+
if bids.Spec.SourceType != longhorn.BackingImageDataSourceTypeClone {
861+
return nil
862+
}
863+
864+
sourceBackingImageName := bids.Spec.Parameters[longhorn.DataSourceTypeCloneParameterBackingImage]
865+
sourceBackingImage, err := c.ds.GetBackingImageRO(sourceBackingImageName)
866+
if err != nil {
867+
return err
868+
}
869+
bids.Status.RunningParameters[longhorn.DataSourceTypeCloneParameterBackingImageUUID] = sourceBackingImage.Status.UUID
870+
return nil
871+
}
872+
873+
func (c *BackingImageDataSourceController) prepareRunningParametersForExport(bids *longhorn.BackingImageDataSource) error {
807874
if bids.Spec.SourceType != longhorn.BackingImageDataSourceTypeExportFromVolume {
808875
return nil
809876
}
@@ -1173,3 +1240,13 @@ func (m *BackingImageDataSourceMonitor) sync() {
11731240
func (c *BackingImageDataSourceController) isResponsibleFor(bids *longhorn.BackingImageDataSource) bool {
11741241
return isControllerResponsibleFor(c.controllerID, c.ds, bids.Name, bids.Spec.NodeID, bids.Status.OwnerID)
11751242
}
1243+
1244+
func isEncryptionRequire(bi *longhorn.BackingImage) bool {
1245+
encryptionType := bimtypes.EncryptionType(bi.Spec.SourceParameters[longhorn.DataSourceTypeCloneParameterEncryption])
1246+
return bi.Spec.SourceType == longhorn.BackingImageDataSourceTypeClone && encryptionType == bimtypes.EncryptionTypeEncrypt
1247+
}
1248+
1249+
func secretExists(bids *longhorn.BackingImageDataSource) bool {
1250+
return bids.Spec.Parameters[longhorn.DataSourceTypeCloneParameterSecretNamespace] != "" &&
1251+
bids.Spec.Parameters[longhorn.DataSourceTypeCloneParameterSecret] != ""
1252+
}

controller/share_manager_controller.go

+7-8
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import (
2525
clientset "k8s.io/client-go/kubernetes"
2626
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
2727

28-
"github.com/longhorn/longhorn-manager/csi"
2928
"github.com/longhorn/longhorn-manager/csi/crypto"
3029
"github.com/longhorn/longhorn-manager/datastore"
3130
"github.com/longhorn/longhorn-manager/engineapi"
@@ -906,16 +905,16 @@ func (c *ShareManagerController) createShareManagerPod(sm *longhorn.ShareManager
906905
return nil, err
907906
}
908907

909-
cryptoKey = string(secret.Data[csi.CryptoKeyValue])
908+
cryptoKey = string(secret.Data[types.CryptoKeyValue])
910909
if len(cryptoKey) == 0 {
911-
return nil, fmt.Errorf("missing %v in secret for encrypted RWX volume %v", csi.CryptoKeyValue, volume.Name)
910+
return nil, fmt.Errorf("missing %v in secret for encrypted RWX volume %v", types.CryptoKeyValue, volume.Name)
912911
}
913912
cryptoParams = crypto.NewEncryptParams(
914-
string(secret.Data[csi.CryptoKeyProvider]),
915-
string(secret.Data[csi.CryptoKeyCipher]),
916-
string(secret.Data[csi.CryptoKeyHash]),
917-
string(secret.Data[csi.CryptoKeySize]),
918-
string(secret.Data[csi.CryptoPBKDF]))
913+
string(secret.Data[types.CryptoKeyProvider]),
914+
string(secret.Data[types.CryptoKeyCipher]),
915+
string(secret.Data[types.CryptoKeyHash]),
916+
string(secret.Data[types.CryptoKeySize]),
917+
string(secret.Data[types.CryptoPBKDF]))
919918
}
920919

921920
manifest := c.createPodManifest(sm, annotations, tolerations, affinity, imagePullPolicy, nil, registrySecret,

0 commit comments

Comments
 (0)