Hey there, I'm doing some evaluations on Pulumi fo...
# general
p
Hey there, I'm doing some evaluations on Pulumi for GCP. I'm testing importing a (basic) instance and can't seem to get past this:
Copy code
user1@linux1:~/pulumi/project> pulumi import 'gcp:compute/instance:Instance' instance4 "<https://www.googleapis.com/compute/v1/projects/test-gcp-project/zones/us-west1-a/instances/instance4>"
Previewing import (dev)

     Type                     Name            Plan       Info
 +   pulumi:pulumi:Stack      proj-dev  create     1 error
 =   └ gcp:compute:Instance  instance4     import     7 errors

Diagnostics:
  pulumi:pulumi:Stack (proj-dev):
    error: preview failed

  gcp:compute:Instance (instance4):
    error: gcp:compute/instance:Instance resource 'instance4' has a problem: Computed attribute cannot be set. Examine values at 'Instance.NetworkInterfaces[0].Name'.
    error: gcp:compute/instance:Instance resource 'instance4' has a problem: Computed attribute cannot be set. Examine values at 'Instance.NetworkInterfaces[0].Ipv6AccessType'.
    error: gcp:compute/instance:Instance resource 'instance4' has a problem: expected network_interface.0.nic_type to be one of [GVNIC VIRTIO_NET], got . Examine values at 'Instance.NetworkInterfaces[0].NicType'.
    error: gcp:compute/instance:Instance resource 'instance4' has a problem: ConflictsWith: "boot_disk.0.disk_encryption_key_raw": conflicts with boot_disk.0.kms_key_self_link. Examine values at 'Instance.BootDisk.DiskEncryptionKeyRaw'.
    error: gcp:compute/instance:Instance resource 'instance4' has a problem: Computed attribute cannot be set. Examine values at 'Instance.BootDisk.DiskEncryptionKeySha256'.
    error: gcp:compute/instance:Instance resource 'instance4' has a problem: ConflictsWith: "boot_disk.0.kms_key_self_link": conflicts with boot_disk.0.disk_encryption_key_raw. Examine values at 'Instance.BootDisk.KmsKeySelfLink'.
    error: Preview failed: one or more inputs failed to validate
s
can you share your code?
p
Hey Mike, thanks for the reply. Here's my
___main___.py
Copy code
import pulumi
import json
from abc.Helpers import *

related_resources = load()
for resources in related_resources:
    # Create disks
    disks = []
    for resource in filter(lambda obj: obj.__kind__ == 'gcp.compute.Disk',resources):
        resource.validate() # always validate
        disks.append(resource.run())

    # Create addresses
    addresses = []
    for resource in filter(lambda obj: obj.__kind__ == 'gcp.compute.Address',resources):
        resource.validate() # always validate
        addresses.append(resource.run())

    # Create any other resources (not including instance)
    for resource in \
            filter(lambda obj: obj.__kind__ not in ['gcp.compute.Instance','gcp.compute.Disk','gcp.compute.Address'],resources):
        resource.run()

    # Create instance and attach ips/disks
    for resource in filter(lambda obj: obj.__kind__ == 'gcp.compute.Instance',resources):
        resource.validate() # always validate

        # Attach reserved ip addresses
        if not 'network_interfaces' in resource.__dict__.keys():
            resource.network_interfaces = []
        for output in addresses:
            resource.network_interfaces.append(
                    { 'network_ip': output.address,
                      'subnetwork': output.subnetwork,
                    }
            )

        # Attach disks
        if not 'attached_disks' in resource.__dict__.keys():
            resource.attached_disks = []
        for output in disks:
            resource.attached_disks.append(
                    { 'source': output.self_link, }
            )

        # Create instance
        instance = resource.run()
Here's the simple Helpers.py lib that allows us to load from template:
Copy code
import yaml
import re
import os
import sys
import string
from glob import glob
import pulumi_gcp as gcp
import pulumi

# Objects/validators
class ABCGcpResource:
    """
    lazy object for loading and validating required arguments
    Other GCP objects may inherit
    """
    required_kwargs = dict()
    required_kwargs.update({'gcp.compute.Instance': ['resource_name','project','boot_disk','machine_type','zone',]})
    required_kwargs.update({'gcp.compute.Address':  ['resource_name','project','region','subnetwork',]})
    required_kwargs.update({'gcp.compute.Disk':     ['resource_name','project','zone',]})

    def __init__(self, kind, resource_name, **kwargs):
        self.__kind__ = kind
        self.resource_name = resource_name.split('.')[0] # ensure resource name is short name
        for k,v in kwargs.items():
            if v != None: self.__dict__[k] = v

        # We should probably subclass but then we'd need more objects
        # trying to avoid complexity here as this should work for 99% of our builds
        if self.__kind__ == 'gcp.compute.Instance':
            try:
                self.hostname = hostname
            except NameError:
                self.hostname = resource_name
        if self.__kind__ == 'gcp.compute.Address':
            try:
                self.address_type = address_type
            except NameError:
                self.address_type = 'INTERNAL'
        try:
            self.name = name
        except NameError:
            self.name = self.resource_name

    def validate(self,metadata=['dns_servers','dns_search','iocode','requester','owner','description','environment']):
        # Once again, we should probably sublcass but this works for 99% of our builds
        # If we need to subclass in the future, we can
        try:
            for required in self.required_kwargs[self.__kind__]:
                if required not in self.__dict__.keys():
                    raise TypeError("{}() missing 1 required positional argument: '{}'".format(self.__kind__,required))
        except KeyError:
            sys.stderr.write("warn: no validations run resource of type {}\n".format(self.__kind__))
            return()

        if self.__kind__ == 'gcp.compute.Instance':
            for required in metadata:
                if required not in self.metadata.keys():
                    raise KeyError("'{}' is required metadata".format(required))
            for k,v in self.metadata.items():
                if not v: raise ValueError("value for '{}' must be set in metadata".format(k))

    def run(self):
        return(eval(self.__kind__)(**{ k:v for (k,v) in self.__dict__.items() if not k.startswith('_') }))

def load():
    config = pulumi.Config()
    templates = os.environ.get('TEMPLATES')
    if templates:
        templates = re.split('\s+|,',templates)
    else:
        templates = config.require_object('templates')

    # load the templates
    loaded = []
    for template in templates:
        data = yaml.safe_load(open(template).read())
        resources = []
        for obj in data:
            # validate kind is a valid object name to load
            allowed = set(string.ascii_lowercase + string.ascii_uppercase + string.digits + '.')
            obj['kind'] = obj['kind'].strip()
            if set(obj['kind']) <= allowed:
                try:
                    # Allows for handling of custom objects
                    resources.append(eval(obj['kind'])(**obj))
                except TypeError:
                    resources.append(ABCGcpResource(**obj))
            else:
                raise ValueError("illegal characters detected for kind".format(kind))
        loaded.append(resources)

    if loaded: return(loaded)
    raise Exception("couldn't load resources from template!")
And here's a "template" so you can see what args I passed when creating instances/disks:
Copy code
-   kind: gcp.compute.Instance
    resource_name: <http://instance2.abc.com|instance2.abc.com>
    boot_disk:
        auto_delete: true
        initialize_params:
            image: projects/suse-byos-cloud/global/images/sles-15-sp2-byos-v20210604
            size: 100
    project: gcp-project
    guest_accelerator: null
    machine_type: n1-standard-1
    zone: us-west1-a
    tags: null
    shielded_instance_config: null
    shielded_instance_config: null
    metadata:
        dns_servers: 10.10.10.10 10.10.10.20
        dns_search: <http://abc.com|abc.com>
        iocode: 00000
        requester: <mailto:abc-admins@abc.com|abc-admins@abc.com>
        owner: <mailto:abc-admins@abc.com|abc-admins@abc.com>
        description: The Cool Application
        environment: prd
-   kind: gcp.compute.Address
    resource_name: instance2-0
    region: us-west1
    project: gcp-project
    address_type: INTERNAL
    subnetwork: projects/vpc-project/regions/us-west1/subnetworks/np-west1
-   kind: gcp.compute.Disk
    project: gcp-project
    resource_name: instance2-disk-1
    size: 200
    zone: us-west1-a
    type: pd-standard
Basically, I'm loading resources to create from simple yaml templates, then validating, then running
bump