-
Notifications
You must be signed in to change notification settings - Fork 377
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add deletion secret as annotation to content #165
Conversation
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: xing-yang The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
/assign @msau42 |
Hi @msau42 If this PR is on the right track, I'll fix the unit tests. |
snapshotterCredentials, err := getCredentials(ctrl.client, snapshotterSecretRef) | ||
if err != nil { | ||
return nil, err | ||
} | ||
driverName, snapshotID, creationTime, size, readyToUse, err = ctrl.handler.CreateSnapshot(snapshot, volume, class.Parameters, snapshotterCredentials) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we calling CreateSnapshot here? Shouldn't the snapshot already exist at this point?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is to check the status of snapshot if uploading is needed after the snapshot is cut. We call CreateSnapshot for dynamic creation case because ListSnapshots is not required and CreateSnapshot is idempotent so it will just return status of the same snapshot.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This sounds non-ideal. Usually Create operations have lower QPS limits than Get/List
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We cannot rely on Get/List because ListSnapshots is optional and many drivers don't support that. We just fix a bug recently to check that capability.
Note that for static binding, we will call GetSnapshotStatus (which call ListSnapshots CSI function) because CreateSnapshot is not invoked for static binding. Even in that case, we can only use that if ListSnapshots is supported by the driver. Since it is pre-provisioned, we just assume the snapshot is valid and ready if driver didn't implement ListSnapshots.
For dynamic creation, we can't assume the snapshot is ready. We have to check the status. Note that in the CSI spec, we specified that "calling CreateSnapshot repeatedly is the preferred way to check if the processing is complete".
ListSnapshots SHALL return with current information regarding the snapshots on the storage system. When processing is complete, the ready_to_use parameter of the snapshot from ListSnapshots SHALL become true. The downside of calling ListSnapshots is that ListSnapshots will not return a gRPC error code if an error occurs during the processing. So calling CreateSnapshot repeatedly is the preferred way to check if the processing is complete.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think using CreateSnapshot when we really want GetSnapshot is ideal for the following reasons:
- Most cloud providers have lower QPS limits for create/update/delete calls vs get/list calls. Plugins may also have different concurrency control for read-only operations vs write operations. Repeatedly calling CreateSnapshot can cause scaling issues in the future
- Metrics reporting also gets skewed and misrepresented. All CreateSnapshot operations get tracked as "create" even though that's not necessary the case. We cannot distinguish between errors and latency of real create operations vs get operations afterwards. For example, get operations are usually faster than create operations, but since we probably call CreateSnapshot with the "get" intention more often, that's going to cause the latency metric to skew more towards the faster "get" latencies.
Anyway, this is not strictly related to this particular PR, I think we should open a issue to track this as a future enhancement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, sounds like we need a different issue to track this for further discussion. Will need to bring this up to CSI community too if we don't want to suggest CO to call CreateSnapshot. Many drivers couldn't support ListSnapshots so the only way to find out the status is to call CreateSnapshot. I remember @jingxu97 strongly suggested to use CreateSnapshot so that we can get gRPC code when this was implemented at that time. Initially we used ListSnapshots to find status for all, but I was asked to use CreateSnapshot when CSI went GA (I submitted that PR right after the Shanghai KubeCon last year: #61). Anyway let's discuss it in a different issue.
|
||
// Set AnnSecretRefName and AnnSecretRefNamespace | ||
if snapshotterSecretRef != nil { | ||
if !metav1.HasAnnotation(snapshotContent.ObjectMeta, AnnSecretRefName) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would the annotation already exist? We are creating a new snapshotContent object every time
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, we don't need this check. I'll remove it.
|
||
snapshotterSecretRef := &v1.SecretReference{} | ||
|
||
if annSecretName != "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if one of these is empty?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If annotations exist but they don't have valid secret name/namespace, we should just error out. I'll fix this.
pkg/controller/util.go
Outdated
|
||
// Annotation for secret name and namespace will be added to the content | ||
// and used at snapshot content deletion time. | ||
AnnSecretRefName = "snapshot.storage.kubernetes.io/secret-name" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would call this "deletion-secret" because it's only used for deletion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
pkg/controller/util_test.go
Outdated
@@ -70,7 +69,7 @@ func TestGetSecretReference(t *testing.T) { | |||
expectRef: nil, | |||
expectErr: true, | |||
}, | |||
"template - valid": { | |||
/*"template - valid": { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
change to an invalid case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need fix the unit tests.
Address review comments, but still need to fix unit tests. |
@@ -687,6 +692,17 @@ func (ctrl *csiSnapshotController) createSnapshotOperation(snapshot *crdv1.Volum | |||
DeletionPolicy: class.DeletionPolicy, | |||
}, | |||
} | |||
|
|||
// Set AnnDeletionSecretRefName and AnnDeletionSecretRefNamespace | |||
if !metav1.HasAnnotation(snapshotContent.ObjectMeta, AnnDeletionSecretRefName) && snapshotterSecretRef != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't think we need to check if the object has the annotation already because we create it immediately above this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed this check. Still checking if snapshotterSecretRef != nil
as we ran into a nil pointer without it.
|
||
snapshotterSecretRef := &v1.SecretReference{} | ||
|
||
if annDeletionSecretName == "" || annDeletionSecretNamespace == "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unit test for this case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated unit test 1-4 to use emptyNamespaceSecretAnnotations()
@@ -1287,6 +1288,27 @@ func secret() *v1.Secret { | |||
} | |||
} | |||
|
|||
func secretAnnotations() map[string]string { | |||
return map[string]string{ | |||
AnnDeletionSecretRefName: "secret", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we actually need to create the secret API object somewhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it is on line 1280.
pkg/controller/framework_test.go
Outdated
} | ||
} | ||
|
||
func invalidSecretAnnotations() map[string]string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this used anywhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually it is missing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed this.
pkg/controller/framework_test.go
Outdated
} | ||
} | ||
|
||
func emptySecretAnnotations() map[string]string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not what I would have guessed "empty" to mean. This is more like the secret doesn't exist?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case, emptysecret
exists, but it does not have any data inside. I'll update the name to emptyDataSecretAnnotations
to be more clear.
(I'm helping Xing with these tests)
Addressed feedback and squashed to one commit. @msau42 |
initialContents: newContentArray("content1-4", invalidSecretClass, "sid1-4", "vuid1-4", "volume1-4", "snapuid1-4", "snap1-4", &deletePolicy, nil, nil, true), | ||
expectedContents: newContentArray("content1-4", invalidSecretClass, "sid1-4", "vuid1-4", "volume1-4", "snapuid1-4", "snap1-4", &deletePolicy, nil, nil, true), | ||
initialContents: newContentArray("content1-4", invalidSecretClass, "sid1-4", "vuid1-4", "volume1-4", "snapuid1-4", "snap1-4", &deletePolicy, nil, nil, true, emptyNamespaceSecretAnnotations()), | ||
expectedContents: newContentArray("content1-4", invalidSecretClass, "sid1-4", "vuid1-4", "volume1-4", "snapuid1-4", "snap1-4", &deletePolicy, nil, nil, true, emptyNamespaceSecretAnnotations()), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a test case for if a secret is specified, but it doesn't exist? To mirror the external-provisioner scenario, I believe we continue and pass no secret to the Delete call, and it's up to the plugin to fail if they need it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unless I'm missing something, it seems that the current behavior for the snapshotter is to fail in this case:
https://github.com/kubernetes-csi/external-snapshotter/blob/master/pkg/controller/snapshot_controller.go#L748-L751
If we want to mirror the provisioner behavior, I can fix that (and add a test case) in this PR or another one
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ggriffiths I missed the provisioner behavior in this case. Please go ahead to fix it and add a test case. Thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed and added deletion test 1-16
for continuing deletion when secret does not exist. Also added a test for creation failing in this case: 7-10
4a39a89
to
e647a80
Compare
/lgtm |
…hub-links-ref-to-master-to-HEAD docs: make github links reference HEAD instead of master
What type of PR is this?
/kind feature
What this PR does / why we need it:
This PR adds deletion secret as annotation to volume snapshot content.
It also disallows annotation in template for snapshotter-secret-name.
Which issue(s) this PR fixes:
Fixes #161 #155
Special notes for your reviewer:
Does this PR introduce a user-facing change?: