hashicorp / terraform-provider-time

Utility provider that provides Time-Based Resources
https://registry.terraform.io/providers/hashicorp/time/latest
Mozilla Public License 2.0
96 stars 28 forks source link

time_rotating constantly re-creates resource after rfc3339 + rotation_xy has passed the first time. #44

Open m273d15 opened 3 years ago

m273d15 commented 3 years ago

Community Note

Terraform Version

Terraform v0.15.4
on darwin_amd64
+ provider registry.terraform.io/hashicorp/time v0.7.1

Affected Resource(s)

Terraform Configuration Files

resource "time_rotating" "test" {
  rfc3339          = "2021-06-20T12:43:13Z"
  rotation_minutes = 7
}

Debug Output

Output ``` > terraform apply -auto-approve time_rotating.test: Refreshing state... [id=2021-06-20T12:43:13Z] Note: Objects have changed outside of Terraform Terraform detected the following changes made outside of Terraform since the last "terraform apply": # time_rotating.test has been deleted - resource "time_rotating" "test" { - day = 20 -> null - hour = 12 -> null - id = "2021-06-20T12:43:13Z" -> null - minute = 43 -> null - month = 6 -> null - rfc3339 = "2021-06-20T12:43:13Z" -> null - rotation_minutes = 7 -> null - rotation_rfc3339 = "2021-06-20T12:50:13Z" -> null - second = 13 -> null - unix = 1624192993 -> null - year = 2021 -> null } Unless you have made equivalent changes to your configuration, or ignored the relevant attributes using ignore_changes, the following plan may include actions to undo or respond to these changes. ────────────────────────────────────────────────────────── Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # time_rotating.test will be created + resource "time_rotating" "test" { + day = (known after apply) + hour = (known after apply) + id = (known after apply) + minute = (known after apply) + month = (known after apply) + rfc3339 = "2021-06-20T12:43:13Z" + rotation_minutes = 7 + rotation_rfc3339 = (known after apply) + second = (known after apply) + unix = (known after apply) + year = (known after apply) } Plan: 1 to add, 0 to change, 0 to destroy. time_rotating.test: Creating... time_rotating.test: Creation complete after 0s [id=2021-06-20T12:43:13Z] Apply complete! Resources: 1 added, 0 changed, 0 destroyed. ```

Panic Output

Expected Behavior

short:

Actual Behavior

short:

Steps to Reproduce

With the example above it should be enough to call:

  1. terraform init
  2. terraform apply - initial creation
  3. terraform apply - re-creation

Important Factoids

References

Thank you for your work on Terraform ❤️

m273d15 commented 2 years ago

Can someone confirm this issue? Is this a bug or a lack of documentation? It does work as expected without the rfc3339.

carldjohnston commented 2 years ago

I'm experiencing this issue too.

My assumption was that the rfc3339 argument was setting the initial time for calculating the rotation time, after which the rotation_X argument would become the increment and the resource would be recreated every N rotation_X's time.

I can't see how the actual behaviour is useful.

davidcorrigan714 commented 1 year ago

This really needs to be fixed. I think what needs to happen is that the provider should compare the id to rotation_rfc3339 and not rotate if it's past it.

mikew3432 commented 1 year ago

At the moment the time_rotating doesn't work like a clock. "if timestamp()>rotation_rfc3339 then delete the resource" and then terraform says "the resource does not exist - create from code" and the provider says simply "rotation_rfc3339 = rfc3339 + rot_X rotation interval "

The rotation_rfc3339 calculation is too simple and with a fixed rfc3339 ends up constantly in the past, resulting in the Delete -> create identically on every Apply to infinity. Why not have some clock arithmetic, it could be a switch like clock_rotating = true

To illustrate what we want, I think,

if rfc3339 input is not null, if rfc3339 input in the past, we want this:

    v rfc3339                                   v id             v rotation_rfc3339
    |             |             |               |         x      |
                  ^+1rot_X      ^+2rot_X       ^+Nrot_X   ^timestamp()

if rfc3339 input in the future, we want this:

                      v id
                      v rfc3339     v rotation_rfc3339
|         x           |             |                    |
          ^timestamp()              ^+1rot_X

So could it be coded like this?

if (rfc3339 input)
  rfc3339 = rfc3339 input 
  if (rfc3339 in the past)
    id = timestamp() minus ( (timestamp() - rfc3339) MOD rot_X )
  else
    ' rfc3339 in the future
    id = rfc3339
  fi
else
  ' no rfc3339 input, use timestamp()
  rfc3339 = timestamp()
  id = rfc3339
fi
rotation_rfc3339 = id + rot_X
pkeecom commented 1 year ago

Why has this issue has not been fixed since Jun 28, 2021? How else can one create a rotating timestamp starting say 15 days from now to rotate every 30 days?

rfc3339 should be the base timestamp. It should only be used to start the rotation from.

faultymonk commented 1 year ago

BTW, I also ran into this bug...

The workaround requires that the rfc3339 is not defined and for the time_rotating when defining the resource. Instead seed the resource with the desired time via import.

Example command to obtain timestamps:

Example import command 12 hours in the future w/ daily rotation:

terraform import time_rotating.example_daily "$(gdate -u +%Y-%m-%dT%H:%M:%SZ --date="+12 hours"),,,1,,"

Looks like https://github.com/hashicorp/terraform-provider-time/pull/180 would resolve this issue.

SBGoods commented 1 year ago

Hi @m273d15 thanks for raising this issue, fixing this issue requires a significant rewrite in the resource's logic and will result in a breaking change for practitioners. We will consider this issue as part of a larger effort for a Time Provider major release in the future but we do not have a timeline for this at the moment.

Mike-Nahmias commented 7 months ago

I think another workaround is to only set rfc3339 on creation and then leave it null going forward. Seemed to work for me:

resource "time_static" "first_run_timestamp" {
  rfc3339 = plantimestamp()

  lifecycle {
    ignore_changes = [ rfc3339 ]
  }
}

resource "time_rotating" "test" {
  rfc3339 = time_static.first_run_timestamp.rfc3339 == plantimestamp() ? timeadd(plantimestamp(), "-5m") : null
  rotation_minutes = 10
}

Unfortunately time_static seems to have similar issues where when you set rfc3339 it will perpetually recreate it, which is why I ignore that attribute after creation.