-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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 RetainOnDelete resource option #8746
Conversation
@@ -1,5 +1,7 @@ | |||
### Improvements | |||
|
|||
- [sdk] - Add `RetainOnDelete` as a resource option. |
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.
Open to name suggestions if anyone thinks RetainOnDelete isn't clear enough. Another idea was "OrphanOnDelete".
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 prefer RetainOnDelete over OrphanOnDelete.
Note this also aligns with CloudFormation’s similar feature: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-deletionpolicy.html. ARM and Terraform don’t have this feature.
Were there other candidate options we should consider?
pkg/resource/deploy/import.go
Outdated
@@ -166,7 +166,7 @@ func (i *importer) getOrCreateStackResource(ctx context.Context) (resource.URN, | |||
typ, name := resource.RootStackType, fmt.Sprintf("%s-%s", projectName, stackName) | |||
urn := resource.NewURN(stackName, projectName, "", typ, tokens.QName(name)) | |||
state := resource.NewState(typ, urn, false, false, "", resource.PropertyMap{}, nil, "", false, false, nil, nil, "", | |||
nil, false, nil, nil, nil, "") | |||
nil, false, nil, nil, nil, "", false) |
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.
Should probably add a way to set retainOnDelete for imports, does that make sense?
Further thinking about this I'm wondering if this should be more closely related to our current External concept? Maybe that should be a tristate of "managed", "external", "managed but used elsewhere so don't delete"? |
Codecov Report
@@ Coverage Diff @@
## master #8746 +/- ##
==========================================
- Coverage 59.36% 59.34% -0.02%
==========================================
Files 643 637 -6
Lines 99898 97776 -2122
Branches 1420 1387 -33
==========================================
- Hits 59309 58030 -1279
+ Misses 37206 36468 -738
+ Partials 3383 3278 -105
Continue to review full report at Codecov.
|
Will be reviewing this tomorrow. There's a bit of conflict that crept in also FYI. |
I'm going to see about working in @AaronFriel idea of different delete behaviors so probably makes sense to hold off for now. I'll give you a nudge when I think it's ready again. |
pkg/resource/deploy/step.go
Outdated
} else if s.old.DeleteBehaviour == resource.DeleteBehaviourDrop { | ||
// Deleting a "drop on delete" is a no-op as the user has explicitly asked us to not delete the resource. | ||
// But we want to diag warn that Pulumi has stopped tracking this resource | ||
msg := fmt.Sprintf("Reference to %s dropped, Pulumi has not deleted this resource", s.old.ID) |
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.
Open to better wording on this message. Should it be even more clear about the cost implications?
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.
Yup this may be a bit tricky. "Pulumi dropped the reference to %s is no longer tracking it, but it remains provisioned in the cloud"? Except it's not necessarily cloud. "remains provisioned?"
const ( | ||
DeleteBehaviourDelete DeleteBehaviour = DeleteBehaviour(pulumirpc.DeleteBehaviour_DELETE) | ||
DeleteBehaviourDrop = DeleteBehaviour(pulumirpc.DeleteBehaviour_DROP) | ||
DeleteBehaviourProtect = DeleteBehaviour(pulumirpc.DeleteBehaviour_PROTECT) |
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.
Protect here is the same as the protect flag. We could with 4.0 remove the protect bool flag and just use this. Also scope here to add a new Protect mode that doesn't even allow replacement (the current protect will allow resources to be replaced, just not fully deleted). Then again maybe we should split this behavior flag into DeleteBehaviour (i.e. what to do for a Delete) and ReplaceBehaviour (what to do for a Replace)? Like would it be valid to say delete replacements but drop the final delete? Protect against replacements but allow the final delete?
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.
Ah so it's exact same. Er, should we just use protect flag? I'm a bit too conservative perhaps - use what's out there already without changing.
Let's not merge this until we finalize the discussion in #7747. Let me know if I need to help drive to conclusion. |
pkg/resource/deploy/step.go
Outdated
@@ -337,26 +337,34 @@ func (s *DeleteStep) Logical() bool { return !s.replacing } | |||
func (s *DeleteStep) Apply(preview bool) (resource.Status, StepCompleteFunc, error) { | |||
// Refuse to delete protected resources (unless we're replacing them in | |||
// which case we will of checked protect elsewhere) | |||
if !s.replacing && s.old.Protect { | |||
if !s.replacing && (s.old.Protect || s.old.DeleteBehaviour == resource.DeleteBehaviourProtect) { |
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.
Repeating a qn here - Protect and DeleteBehaviorProtect seem awfully similar, are they different somehow that we want both?
pkg/resource/deploy/step.go
Outdated
@@ -337,26 +337,34 @@ func (s *DeleteStep) Logical() bool { return !s.replacing } | |||
func (s *DeleteStep) Apply(preview bool) (resource.Status, StepCompleteFunc, error) { | |||
// Refuse to delete protected resources (unless we're replacing them in | |||
// which case we will of checked protect elsewhere) | |||
if !s.replacing && s.old.Protect { | |||
if !s.replacing && (s.old.Protect || s.old.DeleteBehaviour == resource.DeleteBehaviourProtect) { | |||
return resource.StatusOK, nil, fmt.Errorf("unable to delete resource %q\n"+ | |||
"as it is currently marked for protection. To unprotect the resource, "+ |
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.
Does the message to "remove protect
flag" still apply if the cause is DeleteBehaviorProtect?
Any docs refs on this concept? |
Looked through the code, implementation is fine, just wondering about the UX on these option design, left some questions. |
sdk/python/lib/pulumi/resource.py
Outdated
@@ -482,6 +482,11 @@ class ResourceOptions: | |||
require replacement instead of update only if `"*"` is passed. | |||
""" | |||
|
|||
retain_on_delete: Optional[bool] | |||
""" | |||
Option to not actually delete resources from providers when the engine calls Delete. |
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.
Nit: try to explaining this in terms of what the user gets not how it's implemented. Taking a shot at it:
"If set to True, deletions retain the resource it the cloud instead of deleting it. Pulumi simply stops tracking the deleted resource."
@@ -322,6 +322,7 @@ func SerializeResource(res *resource.State, enc config.Encrypter, showSecrets bo | |||
Aliases: res.Aliases, | |||
ImportID: res.ImportID, | |||
SequenceNumber: res.SequenceNumber, | |||
RetainOnDelete: res.RetainOnDelete, |
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.
So.. this means the flag is to be stored in the state file correct?
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.
Has to be. Does mean if you deploy with an old CLI it will reset back to false, but we can't get this information from anywhere else for Deletes.
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.
Hmm okay I'd think for downgrading CLI we could potentially even accept "undefined behavior" here, but your'e saying even then we still need it stored here.
Also curious for my understanding - when we extend ResourceV3, the state backends like "service" just store the extra data, the schema is fully controlled by the code here? We don't need more changes?
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.
LGTM with nits and final question on whether statefile persist of the flag should be avoided or not (see the ticket).
* Plumb in basics of retainOnDelete * Add test * Make test pass * Add to changelog * Add to API list * lint * Add semicolon * Fix Infof call * Fix method call * new delete mode work * cleanup * protectTest * Fix up test * Fix replace * Fix up test * Warn on drop * lint * Change to just a bool flag * Regenerate proto * Rework to just a bool flag with no error * Remove old comment * Fix C# typo * rm extra space * Add missing semicolon * Reformat python * False typo * Fix typo in js function name * Reword docs * lint * Read doesn't need retainOnDelete
Description
Adds a new resource option "RetainOnDelete" which if true skips calling into the provider to actually delete the resource. Instead the engine simply drops it from our state file (or replaces it in the state file in the case of replaces). Users must be aware that this "leaks" cloud resources as Pulumi is no longer tracking them, but that is the whole point of this option.
Fixes #7747 & #8450
Checklist