Skip to content
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

Automate recreation of future dates prior to the date #131

Open
Noel-Jones opened this issue Oct 17, 2022 · 2 comments
Open

Automate recreation of future dates prior to the date #131

Noel-Jones opened this issue Oct 17, 2022 · 2 comments
Labels
enhancement New feature or request

Comments

@Noel-Jones
Copy link
Contributor

Community Note

  • Please vote on this issue by adding a 👍 reaction to the original issue to help the community and maintainers prioritize this request
  • Please do not leave "+1" or other comments that do not add relevant new information or questions, they generate extra noise for issue followers and do not help prioritize the request
  • If you are interested in working on this issue or have submitted a pull request, please leave a comment

Description

I would like a time resource that produces a future date that is recalcuated after a period that is prior to that date. This would assist with creation of resources that should have an expiry date and allow them to be automatically updated prior to expiry (and if put in a keyvault then they can be fetched by the application transparently). This would mean that if terraform is run between the recreate time and the expiry date then the resource will be recreated.

This can be done using two resources now:

  resource "time_rotating" "data_alert_webhook_expiry" {
    rotation_months = 11
  }
  resource "time_offset" "data_alert_webhook_expiry" {
    base_rfc3339 = time_rotating.data_alert_webhook_expiry.rfc3339
    offset_months = 12
  }

But it would be tidier to have one resource as the above does not check that the rotation occurs before the expiry date.

New or Affected Resource(s)

  • time_offset

    resource "time_offset" "data_alert_webhook_expiry" {
      offset_months = 12
      recreate_after_months = 11
    }
    

Potential Terraform Configuration

There would need to be a recreate_after_xxx attributes of each offset_xxx attribute. Like the offset, the recreate_after would be relative to the base time.

References

none

@Noel-Jones Noel-Jones added the enhancement New feature or request label Oct 17, 2022
@bflad
Copy link
Contributor

bflad commented Oct 17, 2022

Thanks for raising this, @Noel-Jones 👍

Over in the tls_locally_signed_cert resource and tls_self_signed_cert resource, they include an optional early_renewal_hours attribute. This seems like a reasonable precedent for this type of functionality in a Terraform resource. Another proposal option in this case would then be potentially supporting something similar in the time_rotating resource, rather than introducing rotation capabilities within the time_offset resource since that could introduce confusion about when to use either one:

resource "time_rotating" "example" {
  early_rotation_months = 1
  rotation_months       = 12
}

This would requiring saving another computed attribute, such as early_rotation_rfc3339, that the resource could use to trigger automatic rotation instead of rotation_rfc3339 (if early_* are not defined, then early_rotation_rfc3339 would equal rotation_rfc3339).

One challenge we have in this area is that this type of logic could be subjective. For example, what should happen if you subtract a month from March 31st, is it February 28/29 or March 2? The Go standard library (time.Time).AddDate() method normalizes to the latter when wrapping days on a calendar month. This isn't necessarily a new problem in this provider, however this might make potentially strange behavior more apparent.

That being said, you may be able to approximate date manipulation functionality using the existing timeadd() configuration function without the extra resource to handle the additional month offset from rotation:

terraform {
  required_providers {
    time = {
      source  = "hashicorp/time"
      version = "0.9.0"
    }
  }
}

resource "time_rotating" "eleven_months" {
  rotation_months = 11
}

output "one_year" {
  # Cannot specify 1 month directly, so approximate with 30 days:
  # 24 hours * 30 days = 720 hours
  value = timeadd(time_rotating.eleven_months.rotation_rfc3339, "720h")
}

Would something like this also be an acceptable solution in your case?

Of course it'd be great if Terraform supported something like a "dateadd()" function to handle calendar-based date manipulation since the timeadd() function is tailored for exact time manipulation (based on Go standard library time.Duration functionality). I don't see any feature requests upstream for this particular functionality upstream though and the date normalization concerns are even more valid upstream since Terraform core is much more sensitive to competing implementations and compatibility promises.

We could consider adding something like a time_dateadd data source to this provider in the meantime, which is the recommended ecosystem solution until provider-defined functions are possible, although that doesn't necessarily helpful for reducing the configuration as its just swapping the offset resource for a data source:

data "time_dateadd" "one_year" {
  rfc3339 = time_rotating.example.rfc3339
  years   = 1
}

It'd be interesting if we could do something like:

resource "time_rotating" "one_year_with_one_month_early_replacement" {
  rotation_months = 12

  lifecycle {
    replace_triggered_by = [
      timecmp(self.rotation_rfc3339, timeadd(self.rotation_rfc3339, "-720h")),
    ]
  }
}

However, those replace_triggered_by elements are intended to be references to other resources/attributes which can be detected as being updated during a plan, instead of values directly:

$ terraform apply

│ Error: Invalid reference in replace_triggered_by expression

│   on main.tf line 15, in resource "time_rotating" "one_year_with_one_month_early_replacement":
│   15:       timecmp(self.rotation_rfc3339, timeadd(self.rotation_rfc3339, "-720h")),

│ Only resources, count.index, and each.key may be used in replace_triggered_by.

Supporting similar functionality that automatically triggers replacement if any of the elements are true would be interesting, such as:

resource "time_rotating" "one_year_with_one_month_early_replacement" {
  rotation_months = 12

  lifecycle {
    # replace_when is a theoretical lifecycle addition that is not supported today.
    replace_when = [
      timecmp(self.rotation_rfc3339, timeadd(self.rotation_rfc3339, "-720h")) > 0,
    ]
  }
}

Which is something that could also be proposed upstream.

@Noel-Jones
Copy link
Contributor Author

Thanks for the well considered response Brian. I agree that it could be added to the existing rotating provider. I'd also note that the timing logic is already somewhat subjective since the resource currently add months and so on but it is a point well made.

I'm finding it hard to thik how to explain how the early_rotation_months would be used, maybe recreate_after_months = 11 would be more intuitive. However, like you I came up with the implementation using timeadd per your second example; rotate after 11 months and use timeadd to create the 12 month expiry date. This does suffer from the expiry date being somewhat random with the hours being an approximation as you note. It is also up to the coder to ensure that the expiry date expressed in hours is greater than the rotation period expressed possibly in another unit. But it works and does not require change.

I'm inclined to cancel this request given the alternative solution. I wonder if it is worth documenting this as a use case?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants