terraform-provider-rancher2: Rancher internal annotations and labels should not be considered "changes"

After creating a rancher2_namespace resource, when I go to apply again, it detects changes that were made by Rancher and wants to remove those changes:

  ~ rancher2_namespace.cert_manager
      annotations.%:                                         "3" => "1"
      annotations.cattle.io/status:                          "{\"Conditions\":[{\"Type\":\"InitialRolesPopulated\",\"Status\":\"True\",\"Message\":\"\",\"LastUpdateTime\":\"2019-06-04T20:34:00Z\"},{\"Type\":\"ResourceQuotaInit\",\"Status\":\"True\",\"Message\":\"\",\"LastUpdateTime\":\"2019-06-04T20:33:59Z\"}]}" => ""
      annotations.lifecycle.cattle.io/create.namespace-auth: "true" => ""
      labels.%:                                              "3" => "1"
      labels.cattle.io/creator:                              "norman" => ""
      labels.field.cattle.io/projectId:                      "p-6js9c" => ""

The resource defintion:

resource "rancher2_namespace" "cert_manager" {
  name       = "cert-manager"
  project_id = "${local.rancher_system_project}"

  annotations {
    "iam.amazonaws.com/permitted" = "${var.cluster}-cert-manager.*"
  }

  labels {
    "certmanager.k8s.io/disable-validation" = "true"
  }
}

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 2
  • Comments: 16 (9 by maintainers)

Commits related to this issue

Most upvoted comments

Digging a little bit more in the terraform DiffSuppressFunc function, i’d like to retract my previous comment No way to set a wildcard to ignore all rancher managed annotations/labels due to map keys are not taken into account on TypeMap DiffSuppressFunc It can be done.

PR #373 is adding a DiffSuppressFunc function on annotations/labels arguments to suppress diff if annotations/labels name contains cattle.io/ OR rancher.io AND has old value AND new value is empty. This should address the issue.

Bumping this up, with Terraform 0.12 it’s impossible / difficult to handle it, specially if you want to write your own annotations.

Looking at the code, it seems that while the object is defined as Computed and Optional but it might not be saving / merging the annotation keys when doing resourceRancher2ClusterRead(). We define our own custom annotations for other tools to pick up but then on changes, the other Rancher annotations (the cattle.io ones) conflict because they weren’t declared by us.

ignore_changes might work for case scenarios where annotations aren’t exposed / touched by devs but when you do, it’s impossible to get a no-diff plan.

Actually, I was able to ignore specific attribute changes with:

  lifecycle {
    ignore_changes = [
      "annotations.%",
      "annotations.cattle.io/status",
      "annotations.lifecycle.cattle.io/create.namespace-auth",
      "labels.%",
      "labels.cattle.io/creator",
      "labels.field.cattle.io/projectId",
    ]
  }

Not the greatest solution since it feel pretty fragile and will break as soon as Rancher changes anything about the annotations and labels it uses for metadata, but it at least is a workaround.

The issue is related to terraform v0.12.x and the “new” way to manage ignore_changes, https://github.com/hashicorp/terraform/issues/21433

annotations/labels are TypeMap arguments. Solution is not simple due to how terraform schema type map works. No way to set a wildcard to ignore all rancher managed annotations/labels due to map keys are not taken into account on TypeMap DiffSuppressFunc. We are working on a better way to manage this, adding a DiffSuppressFunc on annotations/labels arguments, limited to ignore changes on annotations/labels that had old value but new value is empty. Any comment, idea or proposal will be very welcomed on that.

In the meantime, there is a workaround to continue using ignore_changes on arguments type map,

You can also ignore specific map elements by writing references like tags[“Name”] in the ignore_changes list, though with an important caveat: the ignoring applies only to in-place updates to an existing key. Adding or removing a key is treated by Terraform as a change to the containing map itself rather than to the individual key, and so if you wish to ignore changes to a particular tag made by an external system you must ensure that the Terraform configuration creates a placeholder element for that tag name so that the external system changes will be understood as an in-place edit of that key:

That means that to successfully ignore a map element, it should be declared on tf file. The value could be whatever due to tf is going to ignore changes.

This example should work

resource "rancher2_cluster" "cluster-test" {
  # ...
  annotations = {
    "lifecycle.cattle.io/create.cluster-agent-controller-cleanup" = "placeholder"
    "lifecycle.cattle.io/create.cluster-provisioner-controller" = "placeholder"
    "lifecycle.cattle.io/create.cluster-scoped-gc" = "placeholder"   
    "lifecycle.cattle.io/create.mgmt-cluster-rbac-remove" = "placeholder"
  }
  lifecycle {
    ignore_changes = [
      annotations["lifecycle.cattle.io/create.cluster-agent-controller-cleanup"],
      annotations["lifecycle.cattle.io/create.cluster-provisioner-controller"],
      annotations["lifecycle.cattle.io/create.cluster-scoped-gc"],
      annotations["lifecycle.cattle.io/create.mgmt-cluster-rbac-remove"],
    ]
  }
}