Describe the bug
Setting default_ttl = "Infinite" and max_ttl = "Infinite" on env0_project_policy does not produce an infinite TTL. Environments silently inherit the organization TTL policy instead.
The provider converts "Infinite" to an empty string in the update payload:
// env0/resource_project_policy.go (Update)
if payload.DefaultTtl == INFINITE { payload.DefaultTtl = "" }
if payload.MaxTtl == INFINITE { payload.MaxTtl = "" }
But both fields are tagged omitempty, so the empty string is dropped from the request body entirely (it is not serialized as JSON null):
// client/project_policy.go
type PolicyUpdatePayload struct {
...
MaxTtl string `json:"maxTtl,omitempty"`
DefaultTtl string `json:"defaultTtl,omitempty"`
...
}
The PUT /policies API documents that infinite requires an explicit null ("Must be 6-h, 12-h, 1-d, 3-d, 1-w, 2-w, 1-M, inherit, or explicit null which means infinite"). Since omitempty yields an absent field rather than null, infinite is never set. This is most visible when both TTLs are Infinite, because the PUT then carries no TTL fields at all and the API applies the inherited org policy.
To Reproduce
- Have an organization TTL policy set (e.g.
max_ttl = "4-w").
- Apply the following:
resource "env0_project_policy" "example" {
project_id = data.env0_project.example.id
default_ttl = "Infinite"
max_ttl = "Infinite"
}
- Create an environment in that project - it gets a 4-week TTL and is auto-destroyed, instead of never being destroyed.
Expected behavior
Infinite should result in maxTtl: null / defaultTtl: null in the request body, so the API applies an infinite (never-destroy) TTL.
Provider Version
1.31.4 (also reproduces on 1.30.x)
Screenshots
N/A
Additional context
The unit test in resource_project_policy_test.go only covers max_ttl = "Infinite" alongside a concrete default_ttl = "7-h", and asserts the Go payload MaxTtl: "" at the mock boundary - it never verifies the serialized JSON nor the both-Infinite case, so the Infinite → "" → omitted round-trip passes the mock but breaks against the real API.
Suggested fix: make MaxTtl/DefaultTtl in PolicyUpdatePayload pointers (*string) and set them to nil for Infinite so they serialize as explicit null (or drop omitempty and send null). Add coverage for both fields set to Infinite.
Workaround: map Infinite to a large finite TTL (e.g. 120-M) in the caller's Terraform.
Describe the bug
Setting
default_ttl = "Infinite"andmax_ttl = "Infinite"onenv0_project_policydoes not produce an infinite TTL. Environments silently inherit the organization TTL policy instead.The provider converts
"Infinite"to an empty string in the update payload:But both fields are tagged
omitempty, so the empty string is dropped from the request body entirely (it is not serialized as JSONnull):The
PUT /policiesAPI documents that infinite requires an explicitnull("Must be6-h,12-h,1-d,3-d,1-w,2-w,1-M,inherit, or explicitnullwhich means infinite"). Sinceomitemptyyields an absent field rather thannull, infinite is never set. This is most visible when both TTLs areInfinite, because the PUT then carries no TTL fields at all and the API applies the inherited org policy.To Reproduce
max_ttl = "4-w").Expected behavior
Infiniteshould result inmaxTtl: null/defaultTtl: nullin the request body, so the API applies an infinite (never-destroy) TTL.Provider Version
1.31.4 (also reproduces on 1.30.x)
Screenshots
N/A
Additional context
The unit test in
resource_project_policy_test.goonly coversmax_ttl = "Infinite"alongside a concretedefault_ttl = "7-h", and asserts the Go payloadMaxTtl: ""at the mock boundary - it never verifies the serialized JSON nor the both-Infinitecase, so theInfinite → "" → omittedround-trip passes the mock but breaks against the real API.Suggested fix: make
MaxTtl/DefaultTtlinPolicyUpdatePayloadpointers (*string) and set them tonilforInfiniteso they serialize as explicitnull(or dropomitemptyand sendnull). Add coverage for both fields set toInfinite.Workaround: map
Infiniteto a large finite TTL (e.g.120-M) in the caller's Terraform.