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

<computed> * 0 should equal 0, not <computed> #15498

Closed
ask0n opened this issue Jul 7, 2017 · 4 comments · Fixed by #33234
Closed

<computed> * 0 should equal 0, not <computed> #15498

ask0n opened this issue Jul 7, 2017 · 4 comments · Fixed by #33234
Labels
config enhancement unknown-values Issues related to Terraform's treatment of unknown values

Comments

@ask0n
Copy link

ask0n commented Jul 7, 2017

Hi,

I faced with situation when I need run tasks as module depending on other variables.
Since module resource doesn't support count, I have to use trigger variable like var.run_task which I use in the reusable module as multiplier for count. So when I don't need module to be executed I just pass run_task = 0 to it.
The strange behavior is: even if one of the multipliers set to 0 explicitly, terraform still tries to calculate value for other multipliers. Since some of module tasks are missed result couldn't be calculated and I'm getting "value of 'count' cannot be computed".
I think this is bug, because we can be pretty sure that result will be equal to 0 if one of the multipliers is 0. At least terraform should check first variable value in the count before looking up others.

UPD: Using in module output coalesce(aws_autoscaling_group.ec2_instance_as.desired_capacity, 0) also doesn't produce 0 value for future use.

@apparentlymart
Copy link
Contributor

Hi @ask0n! Thanks for reporting this.

Would you mind sharing some example config here so we can see what you're getting at?

I think what you're saying here is that the unknown value propagation could make some assumptions about things like any_computed_value * 0 = 0 (whereas today that would be <computed>). Today the language is pretty conservative about unknowns to avoid making promises we can't keep, and it could be a bit more liberal with some special cases like these.

However, your mention of coalesce there makes me wonder if you're asking about something else. Note that there's a difference between a computed value and an empty value, and coalesce is concerned only with the latter. Any computed value in a function call means that the function is not evaluated at all and instead it just immediately returns a computed value; this behavior is necessary because otherwise the result would be evaluated too early and miss values that are learned during the apply phase.

@ask0n
Copy link
Author

ask0n commented Jul 7, 2017

@apparentlymart you're absolutely right, I meant that any_computed_value * 0 = 0.

I mentioned about coalesce, because I thought that coalesce will prefer defined value in favor to not computable value.

My use case for this is "green/blue" deployment. I have reusable ec2 module which creates ec2 instances for both "green" and "blue" configuration. I can't use separate environment for this (with VPC peering, own subnets, etc.) because I have configured VPN and it can't be shared in AWS via VPC peering. Depending on the result of deployment I'm shutting down "green" or "blue" version. After this I need to generate ansible inventory file, using output values for created stack.
I have something like this for my output configuration:

output "master_nodes_count" {
  value = {
    green = "${module.create-master-nodes-green.node_count}"
    blue = "${module.create-master-nodes-blue.node_count}"
  }
}

Now I need to create text file for ansible with this lines:

master1 ansible_ssh_host=x.x.x.x
master2 ansible_ssh_host=y.y.y.y
master3 ansible_shh_host=z.z.z.z

Separate ansible module is used to create this inventory and run ansible. It looks like:

module "ansible_inventory_green" {
  source = "../../../modules/aws/ansible-inventory"
  run_task   = "${var.green_blue}"
  green_blue = "${var.green_blue}"

  region      = "${var.region}"
  project     = "${var.project}"
  environment = "${var.environment}"
  ansible_playbook = "${var.ansible_playbook}"
  ansible_root = "${var.ansible_root}"
  ansible_string = "${var.ansible_string}"

  master_ips = "${module.kubernetes.master_public_ips["green"]}"
  master_count = "${module.kubernetes.master_count["green"]}"
  minion_ips = "${module.kubernetes.minion_public_ips["green"]}"
  minion_count = "${module.kubernetes.minion_count["green"]}"
  kube_cl_name = "${var.kube_cl_name}"
  minion_asg_name = "${module.kubernetes.minion_asg_name["green"]}"
}

Since I didn't find a way to get item index using formatlist, I have separate template_file to construct such list of desired length:

data "template_file" "master_index" {
  count = "${var.run_task * var.master_count}"
  template = "master$${index}"
  vars {
    index = "${count.index+1}"
  }
}

And in resulting template I have such var:
connection_strings_master = "${join("\n", formatlist("%s ansible_ssh_host=%s", data.template_file.master_index.*.rendered, var.master_ips))}"

The issue is master_index template. It count becomes not computed, when for example my "green" stack is not needed. My ec2 module skips all resources for green module, but since terraform can't compute module.create-master-nodes-green.node_count whole variable master_nodes_count is not computed now, including master_nodes_count.blue.

Maybe I have selected too complicated path for such task, but now I don't see other way to disable parts of configuration without commenting out parts of code.

@apparentlymart
Copy link
Contributor

Thanks for this detailed use-case description, @ask0n.

Adding a special case that multiplying by zero always produces zero does seem reasonable, and I think it works within our rules for the behavior of unknown values. One thing we have on the list for improving the configuration language in future is to remove the distinction between integer and floating point numbers in the configuration language, at which point it'll be easier to make assumptions that the operands to arithmetic operators can always be assumed to be numbers (or convertible to numbers.)

Eventually something like #4149 will also help here, because it'll allow Terraform to proceed with a computed count, deferring certain operations (which in your case will actually be a no-op). I think these two features together will make your use-case work most smoothly, but we can get part-way there by implementing either of them in isolation.

@apparentlymart apparentlymart changed the title value of 'count' cannot be computed <computed> * 0 should equal 0, not <computed> Jul 7, 2017
@apparentlymart apparentlymart added the unknown-values Issues related to Terraform's treatment of unknown values label Feb 7, 2023
@github-actions
Copy link

I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues.
If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jun 24, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
config enhancement unknown-values Issues related to Terraform's treatment of unknown values
Projects
None yet
2 participants