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

fix(constraints): ensure constraints are non-null #556

Merged
merged 1 commit into from
Aug 28, 2024

Conversation

jack-w-shaw
Copy link
Member

@jack-w-shaw jack-w-shaw commented Aug 22, 2024

Description

Fixes: #466

This bug is two-sided, stemming from the fact that Juju does not distinguish between null constraints and empty-string constraints. However, null is not considered by terraform to be a computed value, whereas the empty string is.

  1. Since the constraints schema marks constraints as a computed field, this means if we insert null into state it will be considered unknown & will need to be set on the next Update call. Terraform signals this by passing value <unknown> for state.Constraints through to Update, which is unequal to <null>

  2. Our provider detects a difference between <null> and <unknown>, so attempts to set the constraints on the application to "". This fails for subordinates

Resolve this by:

  1. Since we know that subordinate applications cannot have constraints, on Create or Read we can easily 'compute' the constraints ourselves as "" instead of leaving as null.

  2. Changing the condition we set application constraints to not differentiate between <null>, <unknown> and "".

Type of change

  • Bug fix (non-breaking change which fixes an issue)

Environment

  • Juju controller version: 3.4.5

  • Terraform version: v1.9.5

QA steps

Manual QA steps should be done to test this PR.

provider juju {}

resource "juju_application" "telegraf" {
  name  = "telegraf"
  model = "test"

    charm {
    name     = "telegraf"
    revision = 75
  }

  units = 0
}
$ juju bootstrap lxd
$ juju add-model test

$ juju deploy ubuntu
$ juju deploy telegraf --revision=72 --channel=latest/stable
$ juju relate ubuntu telegraf

# Wait for active/idle status on the two charms

$ terraform init
$ terraform import juju_application.telegraf test:telegraf
$ terraform apply
juju_application.telegraf: Refreshing state... [id=m:telegraf]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # juju_application.telegraf will be updated in-place
  ~ resource "juju_application" "telegraf" {
        id        = "m:telegraf"
        name      = "telegraf"
      + principal = (known after apply)
      + storage   = (known after apply)
        # (4 unchanged attributes hidden)

      ~ charm {
            name     = "telegraf"
          ~ revision = 73 -> 75
            # (3 unchanged attributes hidden)
        }
    }

Plan: 0 to add, 1 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

juju_application.telegraf: Modifying... [id=m:telegraf]
juju_application.telegraf: Modifications complete after 1s [id=m:telegraf]

Apply complete! Resources: 0 added, 1 changed, 0 destroyed.

^ notice that the apply runs successfully, and constraints do not appear as (known after compute)

@jack-w-shaw jack-w-shaw changed the title chore(constraints): ensure constraints are non-nil chore(constraints): ensure constraints are non-null Aug 22, 2024
@jack-w-shaw jack-w-shaw requested a review from hmlanigan August 22, 2024 13:34
@jack-w-shaw jack-w-shaw force-pushed the JUJU-6545_ensure_constraints branch 4 times, most recently from 99148bc to 4a47025 Compare August 22, 2024 17:30
Copy link
Member

@hmlanigan hmlanigan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Base on the conventional commits, this should be a fix, not a chore. Please update the commit message and PR title.

This statement from the PR description is incorrect.
Since the constraints schema marks constraints as a computed field, this means if we insert null into state it will be considered uncomputed & on the next apply terraform will attempt to compute the constraints by setting them.


The plan listed in the QA steps should really have revision = 72 based on the juju deploy step. Then:

$ terraform plan
juju_application.telegraf: Refreshing state... [id=test:telegraf]

No changes. Your infrastructure matches the configuration.

Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed.

internal/provider/provider_test.go Outdated Show resolved Hide resolved
internal/provider/resource_application.go Outdated Show resolved Hide resolved
internal/provider/resource_application.go Outdated Show resolved Hide resolved
@jack-w-shaw jack-w-shaw changed the title chore(constraints): ensure constraints are non-null fix(constraints): ensure constraints are non-null Aug 23, 2024
@jack-w-shaw
Copy link
Member Author

Base on the conventional commits, this should be a fix, not a chore. Please update the commit message and PR title.

Yes thanks, I forgot that fix is a commit type, so was assuming bug fixes counted a chores. Updated

This statement from the PR description is incorrect. Since the constraints schema marks constraints as a computed field, this means if we insert null into state it will be considered uncomputed & on the next apply terraform will attempt to compute the constraints by setting them.

How so?

The plan listed in the QA steps should really have revision = 72 based on the juju deploy step. Then:

$ terraform plan
juju_application.telegraf: Refreshing state... [id=test:telegraf]

No changes. Your infrastructure matches the configuration.

Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed.

I don't think so. This won't verify the bug reported (#466) will be fixed.

The point of these QA steps are to verify that we can run a charm refresh on an imported subordinate. As we see above, if we set the revision to 72 in the plan no upgrade is run

@jack-w-shaw jack-w-shaw requested a review from hmlanigan August 23, 2024 11:33
@jack-w-shaw jack-w-shaw force-pushed the JUJU-6545_ensure_constraints branch from 4a47025 to 253b365 Compare August 23, 2024 12:11
@hmlanigan
Copy link
Member

This statement from the PR description is incorrect.

Since the constraints schema marks constraints as a computed field, this means if we insert null into state it will be considered uncomputed & on the next apply terraform will attempt to compute the constraints by setting them.
This leads to bugs since subordinate applications cannot have constraints applied.

How so?

Perhaps misleading is the word instead. In the reported but scenario, even if terraform didn't believe the computed value must be set, the Update method still would have been called and the equality check could have been false - leading to an incorrect attempt to set nil constraints.

I still believe that

if !plan.Constraints.Equal(state.Constraints) {
should be changed to

if plan.Constraints.ValueString() != state.Constraints.ValueString() {

Thus even if the constraints come up with Unknown vs Null, vs EmptyString, the provider will no attempt to set them and cause havoc. It will also be a good reminder for the future of this type of problem.

@jack-w-shaw jack-w-shaw force-pushed the JUJU-6545_ensure_constraints branch from 253b365 to 81ac502 Compare August 27, 2024 09:24
@jack-w-shaw
Copy link
Member Author

I still believe that

if !plan.Constraints.Equal(state.Constraints) {

should be changed to

if plan.Constraints.ValueString() != state.Constraints.ValueString() {

Thus even if the constraints come up with Unknown vs Null, vs EmptyString, the provider will no attempt to set them and cause havoc. It will also be a good reminder for the future of this type of problem.

Happy to apply this change. But it is actually impossible for constraints to be null or unknown here now, since terraform apply starts by refreshing, which will set null constraints in state to "".

So the only value this change will provide is documentary. Still valuable though!

@jack-w-shaw jack-w-shaw force-pushed the JUJU-6545_ensure_constraints branch 3 times, most recently from b999ee5 to 159ab0d Compare August 27, 2024 15:59
Copy link
Member

@hmlanigan hmlanigan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thank you for the updates.

@jack-w-shaw
Copy link
Member Author

/merge

Fixes: juju#466

Juju does not distinguish between null constraints and empty-string
constraints. However, null is not considered by terraform to be a
computed value, whereas the empty string is.

Since the constraints schema marks constraints as a computed field, this
means if we insert null into state it will be considered uncomputed & on
the next apply terraform will attempt to compute the constraints by
setting them.

This leads to bugs since subordinate applications cannot have
constraints applied.

Since we know that subordinate applications cannot have constraints, on
Create or Read we can easily 'compute' the constraints ourselves as "".
@jack-w-shaw jack-w-shaw force-pushed the JUJU-6545_ensure_constraints branch from 159ab0d to fc09948 Compare August 28, 2024 12:40
@jack-w-shaw
Copy link
Member Author

/merge

@jujubot jujubot merged commit 651e8d1 into juju:main Aug 28, 2024
24 of 28 checks passed
@jack-w-shaw jack-w-shaw deleted the JUJU-6545_ensure_constraints branch August 28, 2024 13:31
@hmlanigan hmlanigan added this to the 0.14.0 milestone Aug 28, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

subordinate charm upgrades fail
3 participants