Skip to content

Commit 0c2a6d5

Browse files
authored
fix: properly deal with saved storage secrets (#37)
1 parent 14d554c commit 0c2a6d5

File tree

3 files changed

+101
-18
lines changed

3 files changed

+101
-18
lines changed

pkg/rclone/controllerserver.go

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import (
1515
csicommon "github.com/kubernetes-csi/drivers/pkg/csi-common"
1616
)
1717

18+
const secretAnnotationName = "csi-rclone.dev/secretName"
19+
1820
type controllerServer struct {
1921
*csicommon.DefaultControllerServer
2022
RcloneOps Operations
@@ -80,21 +82,34 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
8082

8183
// See https://github.com/kubernetes-csi/external-provisioner/blob/v5.1.0/pkg/controller/controller.go#L75
8284
// on how parameters from the persistent volume are parsed
83-
// We have to pass these into the context so that the node server can use them
84-
secretName, nameFound := req.Parameters["csi.storage.k8s.io/provisioner-secret-name"]
85-
secretNs, nsFound := req.Parameters["csi.storage.k8s.io/provisioner-secret-namespace"]
85+
// We have to pass the secret name and namespace into the context so that the node server can use them
86+
// The external provisioner uses the secret name and namespace but it does not pass them into the request,
87+
// so we read the PVC here to extract them ourselves because we may need them in the node server for decoding secrets.
88+
pvcName, pvcNameFound := req.Parameters["csi.storage.k8s.io/pvc/name"]
89+
pvcNamespace, pvcNamespaceFound := req.Parameters["csi.storage.k8s.io/pvc/namespace"]
90+
if !pvcNameFound || !pvcNamespaceFound {
91+
return nil, status.Error(codes.FailedPrecondition, "The PVC name and/or namespace are not present in the create volume request parameters.")
92+
}
8693
volumeContext := map[string]string{}
87-
if nameFound && nsFound {
94+
if len(req.GetSecrets()) > 0 {
95+
pvc, err := getPVC(ctx, pvcNamespace, pvcName)
96+
if err != nil {
97+
return nil, err
98+
}
99+
secretName, secretNameFound := pvc.Annotations[secretAnnotationName]
100+
if !secretNameFound {
101+
return nil, status.Error(codes.FailedPrecondition, "The secret name is not present in the PVC annotations.")
102+
}
88103
volumeContext["secretName"] = secretName
89-
volumeContext["secretNamespace"] = secretNs
104+
volumeContext["secretNamespace"] = pvcNamespace
90105
} else {
91106
// This is here for compatibility reasons before this update the secret name was equal to the PVC
92-
volumeContext["secretName"] = req.Parameters["csi.storage.k8s.io/pvc/name"]
93-
volumeContext["secretNamespace"] = req.Parameters["csi.storage.k8s.io/pvc/namespace"]
107+
volumeContext["secretName"] = pvcName
108+
volumeContext["secretNamespace"] = pvcNamespace
94109
}
95110
return &csi.CreateVolumeResponse{
96111
Volume: &csi.Volume{
97-
VolumeId: volumeName,
112+
VolumeId: volumeName,
98113
VolumeContext: volumeContext,
99114
},
100115
}, nil

pkg/rclone/nodeserver.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,20 @@ func getSecret(ctx context.Context, namespace, name string) (*v1.Secret, error)
162162
return cs.CoreV1().Secrets(namespace).Get(ctx, name, metav1.GetOptions{})
163163
}
164164

165+
func getPVC(ctx context.Context, namespace, name string) (*v1.PersistentVolumeClaim, error) {
166+
cs, err := kube.GetK8sClient()
167+
if err != nil {
168+
return nil, err
169+
}
170+
if namespace == "" {
171+
return nil, fmt.Errorf("Failed to read PVC with K8s client because namespace is blank")
172+
}
173+
if name == "" {
174+
return nil, fmt.Errorf("Failed to read PVC with K8s client because name is blank")
175+
}
176+
return cs.CoreV1().PersistentVolumeClaims(namespace).Get(ctx, name, metav1.GetOptions{})
177+
}
178+
165179
func validatePublishVolumeRequest(req *csi.NodePublishVolumeRequest) error {
166180
if req.GetVolumeId() == "" {
167181
return status.Error(codes.InvalidArgument, "empty volume id")

test/sanity_test.go

Lines changed: 64 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"google.golang.org/grpc"
1717
"google.golang.org/grpc/credentials/insecure"
1818
v1 "k8s.io/api/core/v1"
19+
"k8s.io/apimachinery/pkg/api/resource"
1920
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2021
"k8s.io/client-go/kubernetes"
2122
)
@@ -170,14 +171,42 @@ provider=AWS`},
170171
cfg.SecretsFile = "testdata/secrets.yaml"
171172
cfg.TargetPath = mntDir
172173
cfg.StagingPath = stageDir
174+
kubeClient.CoreV1().Secrets("csi-rclone").Create(context.Background(), &v1.Secret{
175+
ObjectMeta: metav1.ObjectMeta{Name: "pvc-secret", Namespace: "csi-rclone"},
176+
StringData: map[string]string{
177+
"remote": "my-s3",
178+
"remotePath": "giab/",
179+
"secretKey": "cw_0x689RpI-jtRR7oE8h_eQsKImvJapLeSbXpwF4e4=",
180+
"configData": `[my-s3]
181+
type=<sensitive>
182+
provider=AWS`},
183+
Type: "Opaque",
184+
}, metav1.CreateOptions{})
185+
className := "csi-rclone-secret-annotation"
186+
kubeClient.CoreV1().PersistentVolumeClaims("csi-rclone").Create(context.Background(), &v1.PersistentVolumeClaim{
187+
ObjectMeta: metav1.ObjectMeta{
188+
Name: "some-pvc", Namespace: "csi-rclone",
189+
Annotations: map[string]string{"csi-rclone.dev/secretName": "pvc-secret"},
190+
},
191+
Spec: v1.PersistentVolumeClaimSpec{
192+
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
193+
Resources: v1.ResourceRequirements{
194+
Requests: v1.ResourceList{"storage": resource.MustParse("1Gi")},
195+
},
196+
StorageClassName: &className,
197+
},
198+
}, metav1.CreateOptions{})
173199
cfg.TestVolumeParameters = map[string]string{
174-
"csi.storage.k8s.io/pvc/namespace": "csi-rclone",
175-
"csi.storage.k8s.io/pvc/name": "some-pvc",
176-
"csi.storage.k8s.io/node-publish-secret-namespace": "csi-rclone",
177-
"csi.storage.k8s.io/node-publish-secret-name": "test-pvc",
200+
"csi.storage.k8s.io/pvc/namespace": "csi-rclone",
201+
"csi.storage.k8s.io/pvc/name": "some-pvc",
178202
}
179203
})
180204

205+
AfterEach(func() {
206+
kubeClient.CoreV1().Secrets("csi-rclone").Delete(context.Background(), "pvc-secret", metav1.DeleteOptions{})
207+
kubeClient.CoreV1().PersistentVolumeClaims("csi-rclone").Delete(context.Background(), "some-pvc", metav1.DeleteOptions{})
208+
})
209+
181210
AfterAll(func() {
182211
testCtx.Finalize()
183212
})
@@ -194,26 +223,51 @@ provider=AWS`},
194223
BeforeEach(func() {
195224
mntDir, stageDir := getMountDirs()
196225
kubeClient.CoreV1().Secrets("csi-rclone").Create(context.Background(), &v1.Secret{
197-
ObjectMeta: metav1.ObjectMeta{Name: "test-pvc-secrets", Namespace: "csi-rclone"},
226+
ObjectMeta: metav1.ObjectMeta{Name: "pvc-secret", Namespace: "csi-rclone"},
227+
StringData: map[string]string{
228+
"remote": "my-s3",
229+
"remotePath": "giab/",
230+
"secretKey": "cw_0x689RpI-jtRR7oE8h_eQsKImvJapLeSbXpwF4e4=",
231+
"configData": `[my-s3]
232+
type=<sensitive>
233+
provider=AWS`},
234+
Type: "Opaque",
235+
}, metav1.CreateOptions{})
236+
kubeClient.CoreV1().Secrets("csi-rclone").Create(context.Background(), &v1.Secret{
237+
ObjectMeta: metav1.ObjectMeta{Name: "pvc-secret-secrets", Namespace: "csi-rclone"},
198238
StringData: map[string]string{"type": "gAAAAABK-fBwYcjuQgctfZknI2ko2uLqj6DRzRa7kFTKnWm_nkjwGWGTai5eyhNXlp6_6QjeTC7B8IWvhBsvG1Q6Zk2eDYDVQg=="},
199239
Type: "Opaque",
200240
}, metav1.CreateOptions{})
241+
className := "csi-rclone-secret-annotation"
242+
kubeClient.CoreV1().PersistentVolumeClaims("csi-rclone").Create(context.Background(), &v1.PersistentVolumeClaim{
243+
ObjectMeta: metav1.ObjectMeta{
244+
Name: "some-pvc", Namespace: "csi-rclone",
245+
Annotations: map[string]string{"csi-rclone.dev/secretName": "pvc-secret"},
246+
},
247+
Spec: v1.PersistentVolumeClaimSpec{
248+
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
249+
Resources: v1.ResourceRequirements{
250+
Requests: v1.ResourceList{"storage": resource.MustParse("1Gi")},
251+
},
252+
StorageClassName: &className,
253+
},
254+
}, metav1.CreateOptions{})
201255

202256
*cfg = sanity.NewTestConfig()
203257
cfg.Address = endpoint
204258
cfg.SecretsFile = "testdata/secrets.yaml"
205259
cfg.TargetPath = mntDir
206260
cfg.StagingPath = stageDir
207261
cfg.TestVolumeParameters = map[string]string{
208-
"csi.storage.k8s.io/pvc/namespace": "csi-rclone",
209-
"csi.storage.k8s.io/pvc/name": "some-pvc",
210-
"csi.storage.k8s.io/node-publish-secret-namespace": "csi-rclone",
211-
"csi.storage.k8s.io/node-publish-secret-name": "test-pvc",
262+
"csi.storage.k8s.io/pvc/namespace": "csi-rclone",
263+
"csi.storage.k8s.io/pvc/name": "some-pvc",
212264
}
213265
})
214266

215267
AfterEach(func() {
216-
kubeClient.CoreV1().Secrets("csi-rclone").Delete(context.Background(), "test-pvc-secrets", metav1.DeleteOptions{})
268+
kubeClient.CoreV1().Secrets("csi-rclone").Delete(context.Background(), "pvc-secret", metav1.DeleteOptions{})
269+
kubeClient.CoreV1().Secrets("csi-rclone").Delete(context.Background(), "pvc-secret-secrets", metav1.DeleteOptions{})
270+
kubeClient.CoreV1().PersistentVolumeClaims("csi-rclone").Delete(context.Background(), "some-pvc", metav1.DeleteOptions{})
217271
})
218272

219273
AfterAll(func() {

0 commit comments

Comments
 (0)