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

KeyError: 'type' with valid Ansible and Terrform data #93

Closed
ahussey-redhat opened this issue Oct 12, 2023 · 16 comments · Fixed by #101
Closed

KeyError: 'type' with valid Ansible and Terrform data #93

ahussey-redhat opened this issue Oct 12, 2023 · 16 comments · Fixed by #101
Labels
needs_info Needs additional information from original reporter needs_verify

Comments

@ahussey-redhat
Copy link

SUMMARY

When running a cloud.terraform.terraform task to provision a Red Hat OpenShift Service on AWS (ROSA) instance, I am getting a KeyError: 'type'.
Both the Ansible and Terraform config are valid. If I go to the project_path and run terraform plan it succeeds.

ISSUE TYPE
  • Bug Report
COMPONENT NAME

module_utils/models.py

https://github.com/ansible-collections/cloud.terraform/blob/main/plugins/module_utils/models.py

ANSIBLE VERSION
$ ansible --version
ansible [core 2.15.5]
  config file = /home/local-admin/Documents/code/github/aap-rosa-deploy/ansible.cfg
  configured module search path = ['/home/local-admin/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python3.11/site-packages/ansible
  ansible collection location = /home/local-admin/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/local/bin/ansible
  python version = 3.11.5 (main, Aug 28 2023, 00:00:00) [GCC 13.2.1 20230728 (Red Hat 13.2.1-1)] (/usr/bin/python3)
  jinja version = 3.0.3
  libyaml = True
COLLECTION VERSION
$ ansible-galaxy collection list cloud.terraform

# /home/local-admin/.ansible/collections/ansible_collections
Collection      Version
--------------- -------
cloud.terraform 1.1.1  
CONFIGURATION
CONFIG_FILE() = /home/local-admin/Documents/code/github/aap-rosa-deploy/ansible.cfg
DEFAULT_HOST_LIST(/home/local-admin/Documents/code/github/aap-rosa-deploy/ansible.cfg) = ['/home/local-admin/Documents/code/github/aap-rosa-deploy/inventory.yaml']
EDITOR(env: EDITOR) = /usr/bin/vim
OS / ENVIRONMENT
$ cat /etc/redhat-release 
Fedora release 38 (Thirty Eight)
STEPS TO REPRODUCE

Apply the Terraform from a project_path.
The

# example task
- name: "Create ROSA cluster"
  environment:
    TF_BACKEND_BUCKET: "{{ rosa_cluster_name }}-terraform"
    TF_LOG: debug
    TF_LOG_PATH: "{{ work_dir }}/tf_rosa_deploy.log"
  cloud.terraform.terraform:
    complex_vars: false
    force_init: true
    project_path: "{{ work_dir }}/{{ rosa_cluster_name }}/terraform"
    provider_upgrade: true
    state: present
    variables:
      token: "{{ rosa_token }}"
      AWS_ACCESS_KEY_ID: "{{ aws_access_key_id }}"
      AWS_SECRET_ACCESS_KEY: "{{ aws_secret_access_key }}"
      AWS_DEFAULT_REGION: "{{ rosa_region }}"
  register: "create"
EXPECTED RESULTS

The module executes (whether the Terraform apply works or not).

ACTUAL RESULTS
TASK [rh.rosa.create : Create ROSA cluster: hub] *******************************
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: KeyError: 'type'
fatal: [localhost]: FAILED! => {"changed": false, "module_stderr": "Traceback (most recent call last):\n  File \"/home/local-admin/.ansible/tmp/ansible-tmp-1697092022.8988516-114662-223120601896804/AnsiballZ_terraform.py\", line 107, in <module>\n    _ansiballz_main()\n  File \"/home/local-admin/.ansible/tmp/ansible-tmp-1697092022.8988516-114662-223120601896804/AnsiballZ_terraform.py\", line 99, in _ansiballz_main\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n  File \"/home/local-admin/.ansible/tmp/ansible-tmp-1697092022.8988516-114662-223120601896804/AnsiballZ_terraform.py\", line 47, in invoke_module\n    runpy.run_module(mod_name='ansible_collections.cloud.terraform.plugins.modules.terraform', init_globals=dict(_module_fqn='ansible_collections.cloud.terraform.plugins.modules.terraform', _modlib_path=modlib_path),\n  File \"<frozen runpy>\", line 226, in run_module\n  File \"<frozen runpy>\", line 98, in _run_module_code\n  File \"<frozen runpy>\", line 88, in _run_code\n  File \"/tmp/ansible_cloud.terraform.terraform_payload_ub98fnq0/ansible_cloud.terraform.terraform_payload.zip/ansible_collections/cloud/terraform/plugins/modules/terraform.py\", line 659, in <module>\n  File \"/tmp/ansible_cloud.terraform.terraform_payload_ub98fnq0/ansible_cloud.terraform.terraform_payload.zip/ansible_collections/cloud/terraform/plugins/modules/terraform.py\", line 514, in main\n  File \"/tmp/ansible_cloud.terraform.terraform_payload_ub98fnq0/ansible_cloud.terraform.terraform_payload.zip/ansible_collections/cloud/terraform/plugins/module_utils/terraform_commands.py\", line 173, in providers_schema\n  File \"/tmp/ansible_cloud.terraform.terraform_payload_ub98fnq0/ansible_cloud.terraform.terraform_payload.zip/ansible_collections/cloud/terraform/plugins/module_utils/models.py\", line 202, in from_json\n  File \"/tmp/ansible_cloud.terraform.terraform_payload_ub98fnq0/ansible_cloud.terraform.terraform_payload.zip/ansible_collections/cloud/terraform/plugins/module_utils/models.py\", line 203, in <dictcomp>\n  File \"/tmp/ansible_cloud.terraform.terraform_payload_ub98fnq0/ansible_cloud.terraform.terraform_payload.zip/ansible_collections/cloud/terraform/plugins/module_utils/models.py\", line 186, in from_json\n  File \"/tmp/ansible_cloud.terraform.terraform_payload_ub98fnq0/ansible_cloud.terraform.terraform_payload.zip/ansible_collections/cloud/terraform/plugins/module_utils/models.py\", line 187, in <dictcomp>\n  File \"/tmp/ansible_cloud.terraform.terraform_payload_ub98fnq0/ansible_cloud.terraform.terraform_payload.zip/ansible_collections/cloud/terraform/plugins/module_utils/models.py\", line 157, in from_json\n  File \"/tmp/ansible_cloud.terraform.terraform_payload_ub98fnq0/ansible_cloud.terraform.terraform_payload.zip/ansible_collections/cloud/terraform/plugins/module_utils/models.py\", line 158, in <dictcomp>\n  File \"/tmp/ansible_cloud.terraform.terraform_payload_ub98fnq0/ansible_cloud.terraform.terraform_payload.zip/ansible_collections/cloud/terraform/plugins/module_utils/models.py\", line 128, in from_json\nKeyError: 'type'\n", "module_stdout": "", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1}
@ahussey-redhat
Copy link
Author

The trace make it look like it errors here

@gravesm
Copy link
Member

gravesm commented Oct 16, 2023

@ahussey-redhat Is there a way you can provide a minimal terraform config to reproduce this problem? The error is coming from

The type in this case is a field in the terraform show output (https://developer.hashicorp.com/terraform/internals/json-format#values-representation). I'm having a hard time understanding the circumstances under which that type field would be absent.

@gravesm gravesm added needs_info Needs additional information from original reporter needs_verify labels Oct 16, 2023
@MehuiSeklayr
Copy link

Hello @gravesm

I have the exact same issue, but unfortunately, I can't share the whole code. However, I could extract the relevant parts of the json output, and "anonymise" what seems necessary.

Could you tell me what it is you need that would help you identify the issue ?

@ahussey-redhat
Copy link
Author

ahussey-redhat commented Oct 18, 2023

@ahussey-redhat Is there a way you can provide a minimal terraform config to reproduce this problem? The error is coming from

The type in this case is a field in the terraform show output (https://developer.hashicorp.com/terraform/internals/json-format#values-representation). I'm having a hard time understanding the circumstances under which that type field would be absent.

Sorry for my delay in response.
Unfortunately it is a fairly complicated deployment.
Would a sanitized terraform.tfstate assist?

@ahussey-redhat
Copy link
Author

ahussey-redhat commented Oct 18, 2023

terraform.tfstate:      "type": "aws_iam_policy"

I did a grep -ir "type" * in the directory that has all the terraform config, and that was one of the results.

@ahussey-redhat
Copy link
Author

or maybe this?

terraform.tfstate:      "type": "null_resource",
terraform.tfstate:      "type": "null_resource",

@gravesm
Copy link
Member

gravesm commented Oct 25, 2023

Looking a bit closer at this, what we actually need would be the output of terraform providers schema -json. The problem is happening when parsing the resource schema.

@justinc1
Copy link
Contributor

@gravesm This is something that should be fixed, right? Reproducing bug is currently the problem.

@justinc1
Copy link
Contributor

justinc1 commented Nov 2, 2023

@ahussey-redhat @MehuiSeklayr could you send output of terraform version? As @gravesm said, providers schema output is problematic. Installing same providers locally should reproduce the problem.

Update: in between I tried to reproduce with terraform 1.6.2. I looked only at 35 official providers from https://registry.terraform.io/browse/providers?tier=official. The same bug was triggered by

  • waypoint v0.1.0
  • hcp 0.76.0
  • awscc 0.63.0

json fragment from terraform providers schema -json for hcp:

        "hcp_iam_workload_identity_provider": {
          "version": 0,
          "block": {
            "attributes": {
              "aws": {
                "nested_type": {
                  "attributes": {
                    "account_id": {
                      "type": "string",
                      "description": "The AWS Account ID that is allowed to exchange workload identities.",
                      "description_kind": "plain",
                      "required": true
                    }
                  },
                  "nesting_mode": "single"
                },
                "description_kind": "plain",
                "optional": true
              },
              "conditional_access": {
                "type": "string",
                "description": "conditional_access is a hashicorp/go-bexpr string that is evaluated when exchanging tokens. It restricts which upstream identities are allowed to access the service principal.",
                "description_kind": "plain",
                "required": true
              },

Schema is parsed only to detect sensitive values and hide them in output.
I looked closer into awscc, and was not able to find an attribute with nested_type and sensitive=True.

If it is not worth to properly sanitize sensitive attributes in output, then a single line change in helps:

    @classmethod
    def from_json(cls, json: TJsonObject) -> "TerraformAttributeSpec":
        return cls(
-            type=json["type"],
+            type=json.get("type"),

Proper sanitize needs some extra work, as TerraformAttributeSpec can contain other complex objects inside.
As said, I'm not sure it is worth. Do users relay on collection to hide attributes marked as sensitive?

@ahussey-redhat
Copy link
Author

Sorry for the delay in response.
If it helps, here is the requested terraform version output

$ terraform version
Terraform v1.6.2
on linux_amd64

Your version of Terraform is out of date! The latest version
is 1.6.3. You can update by downloading from https://www.terraform.io/downloads.html

The terraform providers schema -json output is veeeeery long. Like exceeds my terminal buffer size long.

@justinc1
Copy link
Contributor

justinc1 commented Nov 3, 2023

terraform version outpu should include also installed providers, something like this:

Terraform v1.6.2
on linux_amd64
+ provider registry.terraform.io/hashicorp/awscc v0.63.0

Your version of Terraform is out of date! The latest version
is 1.6.3. You can update by downloading from https://www.terraform.io/downloads.html

The + provider ... lines are the most interesting part. Did you run terraform version from same directory as terraform providers schema -json @ahussey-redhat ?

justinc1 added a commit to justinc1/cloud.terraform that referenced this issue Nov 4, 2023
@ahussey-redhat
Copy link
Author

I didn't but please find the requested output below, run from the directory the Terraform config resides in

$ terraform version
Terraform v1.6.3
on linux_amd64
+ provider registry.terraform.io/hashicorp/aws v4.67.0
+ provider registry.terraform.io/hashicorp/null v3.2.1
+ provider registry.terraform.io/hashicorp/random v3.5.1
+ provider registry.terraform.io/hashicorp/time v0.9.1
+ provider registry.terraform.io/terraform-redhat/rhcs v1.4.0

@justinc1
Copy link
Contributor

justinc1 commented Nov 7, 2023

The registry.terraform.io/terraform-redhat/rhcs is the one with nested_type attributes.

Output of a small debug script that is searching for nested_type:

SENS NESTED=1 parents=['registry.terraform.io/terraform-redhat/rhcs', 'rhcs_cluster_rosa_classic', 'admin_credentials', 'password'] sensitive=True
SENS NESTED=1 parents=['registry.terraform.io/terraform-redhat/rhcs', 'rhcs_identity_provider', 'github', 'client_secret'] sensitive=True
SENS NESTED=1 parents=['registry.terraform.io/terraform-redhat/rhcs', 'rhcs_identity_provider', 'gitlab', 'client_secret'] sensitive=True
SENS NESTED=1 parents=['registry.terraform.io/terraform-redhat/rhcs', 'rhcs_identity_provider', 'google', 'client_secret'] sensitive=True
SENS NESTED=1 parents=['registry.terraform.io/terraform-redhat/rhcs', 'rhcs_identity_provider', 'htpasswd', 'users', 'password'] sensitive=True
SENS NESTED=1 parents=['registry.terraform.io/terraform-redhat/rhcs', 'rhcs_identity_provider', 'ldap', 'bind_password'] sensitive=True
SENS NESTED=1 parents=['registry.terraform.io/terraform-redhat/rhcs', 'rhcs_identity_provider', 'openid', 'client_secret'] sensitive=True

The rhcs_cluster_rosa_classic ... admin_credentials is also an attribute that is not marked as sensitive, but it contains one sensitve sub-attribute (password).

terraform providers schema -json | jq '.provider_schemas["registry.terraform.io/terraform-redhat/rhcs"].resource_schemas.rhcs_cluster_rosa_classic.block.attributes.admin_credentials'
{
  "nested_type": {
    "attributes": {
      "password": {
        "type": "string",
        "description": "Admin password that will be created with the cluster.",
        "description_kind": "plain",
        "required": true,
        "sensitive": true
      },
      "username": {
        "type": "string",
        "description": "Admin username that will be created with the cluster.",
        "description_kind": "plain",
        "required": true
      }
    },
    "nesting_mode": "single"
  },
  "description": "Admin user credentials",
  "description_kind": "plain",
  "optional": true
}

@ahussey-redhat
Copy link
Author

@justinc1
Copy link
Contributor

justinc1 commented Nov 8, 2023

The nested_type was simply not implemented, and #101 does the implementation.

@ahussey-redhat
Copy link
Author

ahussey-redhat commented Nov 8, 2023 via email

justinc1 added a commit to justinc1/cloud.terraform that referenced this issue Nov 16, 2023
alinabuzachis pushed a commit that referenced this issue Nov 17, 2023
* Allow terraform resource_schema attributes with nested_type

Fixes #93

Signed-off-by: Justin Cinkelj <[email protected]>

* Test nested_type attributes by using awscc

Signed-off-by: Justin Cinkelj <[email protected]>

* Update unit tests

Signed-off-by: Justin Cinkelj <[email protected]>

* Add changelog fragment

Signed-off-by: Justin Cinkelj <[email protected]>

* fix linters, mypy

Signed-off-by: Justin Cinkelj <[email protected]>

* isort fix

Signed-off-by: Justin Cinkelj <[email protected]>

---------

Signed-off-by: Justin Cinkelj <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs_info Needs additional information from original reporter needs_verify
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants