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

Terraform crashes when trying to select an element from a concat containing a potentially empty list #14438

Closed
betabandido opened this issue May 12, 2017 · 3 comments · Fixed by #14526
Assignees

Comments

@betabandido
Copy link

I am trying to build a module to configure a swagger endpoint that allows for either placing the endpoint under the root resource path or under a path named 'swagger'. I am following a similar approach to the one shown by Gruntwork (link to the article)

When I run terraform, it crashes with the following message: panic: runtime error: invalid memory address or nil pointer dereference (more details below).

I am including a minimal example that reproduces the error. In this example, the endpoint is represented just as an API gateway method (no integration, integration response or response resources are present).

Terraform Version

Terraform v0.9.5

Affected Resource(s)

Please list the resources as a list, for example:

  • aws_api_gateway_method

Based on the nature of the problem, it might well be related to the core components, though.

Terraform Configuration Files

The module contains:

variable "api" {}
variable "parent" {}
variable "location" {}
variable "use_swagger_path" {
    default = true
}

resource "aws_api_gateway_resource" "swagger" {
  count       = "${var.use_swagger_path}"
  rest_api_id = "${var.api}"
  parent_id   = "${var.parent}"
  path_part   = "swagger"
}

resource "aws_api_gateway_method" "method" {
  rest_api_id   = "${var.api}"
  resource_id   = "${element(concat(aws_api_gateway_resource.swagger.*.id, list(var.parent)), 0)}"
  http_method   = "GET"
  authorization = "NONE"
}

The main file contains:

provider "aws" {
  profile = "${var.profile}"
  region  = "${var.region}"
}

variable "swagger_location" {
  default = "a_url"
}

resource "aws_api_gateway_rest_api" "api" {
  name = "api"
}

module "swagger_root" {
  source   = "modules/swagger"
  api      = "${aws_api_gateway_rest_api.api.id}"
  parent   = "${aws_api_gateway_rest_api.api.root_resource_id}"
  location = "${var.swagger_location}"
  use_swagger_path = false
}

Debug Output

https://gist.github.com/betabandido/2bcdcfbde244fdee016fc0f8f11d39ad

Panic Output

https://gist.github.com/betabandido/d516d992ef83f95e5d285daebcfdeb41

Expected Behavior

The API gateway resources should be created correctly.

Actual Behavior

Terraform crashed.

Steps to Reproduce

Please list the steps required to reproduce the issue, for example:

  1. terraform get
  2. terraform apply
@apparentlymart
Copy link
Member

Hi @betabandido! Sorry for this crash, and thanks for reporting it.

I'm looking into this now, and will hopefully have an explanation (and ideally, a fix!) soon.

@apparentlymart
Copy link
Member

Okay! This is a rather insidious edge-case.

The following is a minimal repro for this:

# root.tf

module "child" {
  source = "./child"
}
# child/child.tf

resource "null_resource" "a" {
  count = 0
}

resource "null_resource" "b" {
  triggers = {
      a_ids = "${join(" ", null_resource.a.*.id)}"
  }
}

The issue arises because of the shape of the intermediate state we're in when we resolve that null_resource.a.*.id variable in the child.

Normally by the time we resolve that null_resource.a.*.id expression there would be instances of null_resource.a in the module's state, but since we have count = 0 here no instances were created. A module's state isn't created until the first time a resource is written into it, so when we reach this point the module has no state at all, but this is not a situation this code was built to expect and so it crashes.

The extra-edgey part of this edge case is that it only applies to non-root modules, since Terraform creates an empty state for the root module very early on its processing, but non-root modules don't get a state until the first resource is created.

I'm working on a fix and will have a PR for it soon.

apparentlymart added a commit that referenced this issue May 15, 2017
For child modules, a ModuleState isn't allocated until the first time a
module instance is inserted into the state under the module's path.
Normally interpolations of resource attributes are delayed until at least
one resource has been created due to the nature of the dependency graph,
but if the interpolation value is a multi-var (splat) then it is possible
that the referenced resource has count=0 and thus created _no_ resource
states when it was visited.

Previously we would crash when trying to access the resource map for the
nil module in order to count how many instances are present. Since we know
there can't be any instances present in a nil module, we now preempt
this crash by returning zero early.

This edge-case does not apply to the root module because its ModuleState
is allocated as part of initializing the main State instance.

This fixes #14438.
apparentlymart added a commit that referenced this issue May 16, 2017
For child modules, a ModuleState isn't allocated until the first time a
module instance is inserted into the state under the module's path.
Normally interpolations of resource attributes are delayed until at least
one resource has been created due to the nature of the dependency graph,
but if the interpolation value is a multi-var (splat) then it is possible
that the referenced resource has count=0 and thus created _no_ resource
states when it was visited.

Previously we would crash when trying to access the resource map for the
nil module in order to count how many instances are present. Since we know
there can't be any instances present in a nil module, we now preempt
this crash by returning zero early.

This edge-case does not apply to the root module because its ModuleState
is allocated as part of initializing the main State instance.

This fixes #14438.
@ghost
Copy link

ghost commented Apr 12, 2020

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.

@ghost ghost locked and limited conversation to collaborators Apr 12, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants