pulumi / pulumi-gcp

A Google Cloud Platform (GCP) Pulumi resource package, providing multi-language access to GCP
Apache License 2.0
176 stars 50 forks source link

`gcp.serviceaccount.get_account()` raises `AttributeError: 'NoneType' object has no attribute 'account_id'` when fetching a service account that doesn't exist #886

Open lazcamus opened 1 year ago

lazcamus commented 1 year ago

What happened?

gcp.serviceaccount.get_account() raises AttributeError: 'NoneType' object has no attribute 'account_id' when fetching a service account that doesn't exist.

Steps to reproduce

laz@builder-git_bando ~/git/bando/infra/pulumi/svcacct$ cat Pulumi.yaml 
name: foo
runtime:
  name: python
description: bar
backend:
  url: file:///tmp
laz@builder-git_bando ~/git/bando/infra/pulumi/svcacct$ cat __main__.py 
import pulumi
import pulumi_gcp as gcp

gcp.serviceaccount.get_account(account_id="does.not.exist", project="camus-infra")
laz@builder-git_bando ~/git/bando/infra/pulumi/svcacct$ pulumi pre -s demo
Previewing update (demo):
     Type                 Name      Plan       Info
 +   pulumi:pulumi:Stack  foo-demo  create     3 errors

Diagnostics:
  pulumi:pulumi:Stack (foo-demo):
    error: Program failed with an unhandled exception:
    error: Traceback (most recent call last):
      File "/Users/laz/.cache/bazel/_bazel_laz/399bb0addbf925d8f1855f7fe3c4553e/external/pulumi_linux_arm64/pulumi/pulumi-language-python-exec", line 107, in <module>
        loop.run_until_complete(coro)
      File "/Users/laz/.cache/bazel/_bazel_laz/399bb0addbf925d8f1855f7fe3c4553e/external/python3_10_aarch64-unknown-linux-gnu/lib/python3.10/asyncio/base_events.py", line 641, in run_until_complete
        return future.result()
      File "/Users/laz/git/bando/bazel-bin/infra/pulumi/pulumi.runfiles/common_deps_pulumi/pulumi/runtime/stack.py", line 126, in run_in_stack
        await run_pulumi_func(lambda: Stack(func))
      File "/Users/laz/git/bando/bazel-bin/infra/pulumi/pulumi.runfiles/common_deps_pulumi/pulumi/runtime/stack.py", line 49, in run_pulumi_func
        func()
      File "/Users/laz/git/bando/bazel-bin/infra/pulumi/pulumi.runfiles/common_deps_pulumi/pulumi/runtime/stack.py", line 126, in <lambda>
        await run_pulumi_func(lambda: Stack(func))
      File "/Users/laz/git/bando/bazel-bin/infra/pulumi/pulumi.runfiles/common_deps_pulumi/pulumi/runtime/stack.py", line 149, in __init__
        func()
      File "/Users/laz/.cache/bazel/_bazel_laz/399bb0addbf925d8f1855f7fe3c4553e/external/pulumi_linux_arm64/pulumi/pulumi-language-python-exec", line 106, in <lambda>
        coro = pulumi.runtime.run_in_stack(lambda: runpy.run_path(args.PROGRAM, run_name='__main__'))
      File "/Users/laz/.cache/bazel/_bazel_laz/399bb0addbf925d8f1855f7fe3c4553e/external/python3_10_aarch64-unknown-linux-gnu/lib/python3.10/runpy.py", line 286, in run_path
        return _run_code(code, mod_globals, init_globals,
      File "/Users/laz/.cache/bazel/_bazel_laz/399bb0addbf925d8f1855f7fe3c4553e/external/python3_10_aarch64-unknown-linux-gnu/lib/python3.10/runpy.py", line 86, in _run_code
        exec(code, run_globals)
      File "/Users/laz/git/bando/infra/pulumi/svcacct/./__main__.py", line 4, in <module>
        gcp.serviceaccount.get_account(account_id="does.not.exist", project="camus-infra")
      File "/Users/laz/git/bando/bazel-bin/infra/pulumi/pulumi.runfiles/common_deps_pulumi_gcp/pulumi_gcp/serviceaccount/get_account.py", line 145, in get_account
        account_id=__ret__.account_id,
    AttributeError: 'NoneType' object has no attribute 'account_id'
    error: an unhandled error occurred: Program exited with non-zero exit code: 1

Expected Behavior

I think I expected a return of None, but raising a more specific exception that can be caught in a less fragile way than catching AttributeError would also work.

Actual Behavior

AttributeError: 'NoneType' object has no attribute 'account_id'

Output of pulumi about

CLI          
Version      3.37.1
Go Version   go1.17.12
Go Compiler  gc

Plugins
NAME           VERSION
gcp            6.25.0
google-native  0.19.1
python         unknown

Host     
OS       ubuntu
Version  22.04
Arch     aarch64

This project is written in python: executable='/Users/laz/git/bando/admin/../bazel-bin/infra/pulumi/pulumi.runfiles/python3_10_aarch64-unknown-linux-gnu/bin/python3' version='3.10.2'

Backend        
Name           a73c63b45dcc
URL            file:///tmp
User           laz
Organizations  

Dependencies:
NAME                         VERSION
absl-py                      1.0.0
google-cloud-secret-manager  2.11.0
grpcio-status                1.46.3
pip                          22.0.3
pulumi-gcp                   6.25.0
pulumi-google-native         0.19.1
pulumi-policy                1.4.0
setuptools                   60.9.3

Pulumi locates its logs in /tmp by default
warning: Failed to get information about the current stack: No current snapshot

Additional context

No response

Contributing

Vote on this issue by adding a 👍 reaction. To contribute a fix for this issue, leave a comment (and link to your pull request, if you've opened one already).

lazcamus commented 1 year ago

it looks like pulumi_google_native raises Exception with some google generated text that I could probably safely match on:

laz@builder-git_bando ~/git/bando/infra/pulumi/svcacct$ cat __main__.py 
from pulumi_google_native.iam import v1 as iam

svc_acct = iam.get_service_account(service_account_id="does.not.exist", project="camus-infra")
laz@builder-git_bando ~/git/bando/infra/pulumi/svcacct$ pulumi pre -s demo
Previewing update (demo):
     Type                 Name      Plan       Info
 +   pulumi:pulumi:Stack  foo-demo  create     3 errors

Diagnostics:
  pulumi:pulumi:Stack (foo-demo):
    error: Program failed with an unhandled exception:
    error: Traceback (most recent call last):
      File "/Users/laz/.cache/bazel/_bazel_laz/399bb0addbf925d8f1855f7fe3c4553e/external/pulumi_linux_arm64/pulumi/pulumi-language-python-exec", line 107, in <module>
        loop.run_until_complete(coro)
      File "/Users/laz/.cache/bazel/_bazel_laz/399bb0addbf925d8f1855f7fe3c4553e/external/python3_10_aarch64-unknown-linux-gnu/lib/python3.10/asyncio/base_events.py", line 641, in run_until_complete
        return future.result()
      File "/Users/laz/git/bando/bazel-bin/infra/pulumi/pulumi.runfiles/common_deps_pulumi/pulumi/runtime/stack.py", line 126, in run_in_stack
        await run_pulumi_func(lambda: Stack(func))
      File "/Users/laz/git/bando/bazel-bin/infra/pulumi/pulumi.runfiles/common_deps_pulumi/pulumi/runtime/stack.py", line 49, in run_pulumi_func
        func()
      File "/Users/laz/git/bando/bazel-bin/infra/pulumi/pulumi.runfiles/common_deps_pulumi/pulumi/runtime/stack.py", line 126, in <lambda>
        await run_pulumi_func(lambda: Stack(func))
      File "/Users/laz/git/bando/bazel-bin/infra/pulumi/pulumi.runfiles/common_deps_pulumi/pulumi/runtime/stack.py", line 149, in __init__
        func()
      File "/Users/laz/.cache/bazel/_bazel_laz/399bb0addbf925d8f1855f7fe3c4553e/external/pulumi_linux_arm64/pulumi/pulumi-language-python-exec", line 106, in <lambda>
        coro = pulumi.runtime.run_in_stack(lambda: runpy.run_path(args.PROGRAM, run_name='__main__'))
      File "/Users/laz/.cache/bazel/_bazel_laz/399bb0addbf925d8f1855f7fe3c4553e/external/python3_10_aarch64-unknown-linux-gnu/lib/python3.10/runpy.py", line 286, in run_path
        return _run_code(code, mod_globals, init_globals,
      File "/Users/laz/.cache/bazel/_bazel_laz/399bb0addbf925d8f1855f7fe3c4553e/external/python3_10_aarch64-unknown-linux-gnu/lib/python3.10/runpy.py", line 86, in _run_code
        exec(code, run_globals)
      File "/Users/laz/git/bando/infra/pulumi/svcacct/./__main__.py", line 3, in <module>
        svc_acct = iam.get_service_account(service_account_id="does.not.exist", project="camus-infra")
      File "/Users/laz/git/bando/bazel-bin/infra/pulumi/pulumi.runfiles/common_deps_pulumi_google_native/pulumi_google_native/iam/v1/get_service_account.py", line 156, in get_service_account
        __ret__ = pulumi.runtime.invoke('google-native:iam/v1:getServiceAccount', __args__, opts=opts, typ=GetServiceAccountResult).value
      File "/Users/laz/git/bando/bazel-bin/infra/pulumi/pulumi.runfiles/common_deps_pulumi/pulumi/runtime/invoke.py", line 166, in invoke
        raise invoke_error
    Exception: invoke of google-native:iam/v1:getServiceAccount failed: invocation of google-native:iam/v1:getServiceAccount returned an error: error sending request: googleapi: Error 404: Unknown service account
    error: an unhandled error occurred: Program exited with non-zero exit code: 1

I think I'm going to end up with something like:

from pulumi_google_native.iam import v1 as iam

def get_service_account_safe(**kwargs):
    try:
        return iam.get_service_account(**kwargs)
    except Exception as e:
        if 'Error 404: Unknown service account' in e.args[0]:
            return None
        else:
            raise(e)

I could craft the same wrapper and catch AttributeError I suppose ... but relying on code implementation seems hackier than digging for a specific google error string in the exception text.

stack72 commented 1 year ago

Hi @lazcamus

I apologise for the issue here - the upstream provider doesn't actually return an error if no service_account is found. This is then falling through to what you are seeing :/

I can have a look into this but I think it will require talking to the Google team that manages that provider

Paul