Hello. In a general sense, how can I tell when an ...
# general
f
Hello. In a general sense, how can I tell when an object has been "created" ... as opposed to "updated", "deleted" in Python?
r
Are you using automation api or the CLI?
f
API. Python SDK
r
You can get this information by parsing the structured logs accessed by the
on_event
callback on the
up
,
preview
, etc. methods
f
Do you happen to have an example? I'm not following the docs very well. If not... no big deal. Thanks for the direction.
r
This area of the codebase isn't particularly well documented. I'd be happy to give you an example, but I think I need more information about what you're trying to do to be able to adequately help. Can you tell me a little more about what you're trying to do?
f
When a RDS instance is created, and only created, I want to print its resource_id to the user.
I can get the resource_id ... I just can't see how to determine when an instance was created.
r
Okay so currently something like the following would work. Note that the
print_id_on_create
function is passed to the
on_event
callback on
stack.up
Copy code
import sys
from pulumi import automation as auto
import pulumi_random as random

# This is the pulumi program in "inline function" form
def pulumi_program():
    random.RandomString("my_rando_string", length=16, special=True)

def print_id_on_create(evt: auto.EngineEvent):
    # Check if it's a ResOutputsEvent. This event is emitted after an operation on a resource has completed.
    if evt.res_outputs_event:
        meta = evt.res_outputs_event.metadata
        # Only print when a resource of type "random:index/randomString:RandomString" is created.
        if meta["type"] == "random:index/randomString:RandomString" \
            and meta["op"] == auto.OpType.CREATE:
            print("\n--------")
            print(f"{meta['type']} created")
            # meta.new contains the new state for the resource. 
            # We can get whatever output we want from `outputs`.
            print(f"resource_id: {meta['new']['outputs']['id']}")
            print(f"urn: {meta['new']['urn']}")
            print("--------\n")


# To destroy our program, we can run python main.py destroy
destroy = False
args = sys.argv[1:]
if len(args) > 0:
    if args[0] == "destroy":
        destroy = True

project_name = "print_on_create"
# We use a simple stack name here, but recommend using auto.fully_qualified_stack_name for maximum specificity.
stack_name = "dev"
# stack_name = auto.fully_qualified_stack_name("myOrgOrUser", project_name, stack_name)

# create or select a stack matching the specified name and project.
# this will set up a workspace with everything necessary to run our inline program (pulumi_program)
stack = auto.create_or_select_stack(stack_name=stack_name,
                                    project_name=project_name,
                                    program=pulumi_program)

print("stack initialized")

# for inline programs, we must manage plugins ourselves
print("installing plugins... ", end="")
stack.workspace.install_plugin("random", "v4.3.1")
print("done.")

print("refreshing stack... ", end="")
stack.refresh()
print("done.")

if destroy:
    print("destroying stack... ", end="")
    stack.destroy()
    print("done.")
    sys.exit()

print("updating stack...")
up_res = stack.up(on_event=print_id_on_create)
print("stack update complete")
There's currently a small error in deserializing events though, so once https://github.com/pulumi/pulumi/pull/8375/files is merged,
print_id_on_create
will instead become:
Copy code
def print_id_on_create(evt: auto.EngineEvent):
    # Check if it's a ResOutputsEvent. This event is emitted after an operation on a resource has completed.
    if evt.res_outputs_event:
        meta = evt.res_outputs_event.metadata
        # Only print when a resource of type "random:index/randomString:RandomString" is created.
        if meta.type == "random:index/randomString:RandomString" \
            and meta.op == auto.OpType.CREATE:
            print("\n--------")
            print(f"{meta.type} created")
            # meta.new contains the new state for the resource. 
            # We can get whatever output we want from `outputs`.
            print(f"resource_id: {meta.new.outputs['id']}")
            print(f"urn: {meta.new.urn}")
            print("--------\n")
Note that the properties on
meta
can now be accessed as
meta.type
instead of
meta['type']
f
Thanks. I've been playing with this for a little bit now, and I think I'm missing properly defining the pulumi program in the
auto.create_or_select_stack
For a bit of context. My config is in .yaml file, and this is my
___main___.py
Copy code
def pulumi_program():
    random.RandomString("my_rando_string", length=16, special=True)


def print_id_on_create(evt: auto.EngineEvent):
    print("THERE")
    # Check if it's a ResOutputsEvent. This event is emitted after an operation on a resource has completed.
    if evt.res_outputs_event:
        meta = evt.res_outputs_event.metadata
        # Only print when a resource of type "random:index/randomString:RandomString" is created.
        if meta["op"] == auto.OpType.CREATE:
            print("\n--------")
            print(f"{meta['type']} created")
            # meta.new contains the new state for the resource.
            # We can get whatever output we want from `outputs`.
            print(f"resource_id: {meta['new']['outputs']['id']}")
            print(f"urn: {meta['new']['urn']}")
            print("--------\n")


def main():
    stack_name = pulumi.get_stack()
    if not stack_name.startswith("rds-") or not stack_name[2:]:
        raise ValueError("Stack name must be the format \"rds-<environment>-<service>\"")
    imported = False
    environment = EnvironmentMap[pulumi.get_stack().split("-")[1]]
    pulumi_lw = pulumi.automation.LocalWorkspace(work_dir='../')
    config = pulumi_lw.get_all_config(stack_name=stack_name)
    json_config = None
    for key in config.keys():
        json_config= json.loads(config[key].value)
    config_handler = ConfigHandler(json_config)
    config_handler.get_regions()
    project_name = pulumi.get_project()
    stack = auto.create_or_select_stack(stack_name=stack_name, project_name=project_name, program=pulumi_program)
    for region in ConfigHandler.regions:
        aws_provider = provider.Provider(resource_name=f"rds-{region}", region=region, profile=environment)
        for instance in ConfigHandler.parent_key['regions'][region].keys():
            instance_config = ConfigHandler.parent_key['regions'][region][instance]
            identifier = instance_config['identifier']
            resource = RDSResource(instance_config, region, environment, aws_provider, identifier, imported=imported).get_resource()
            if resource:
                preview_res = stack.preview(on_event=print_id_on_create)


if __name__ == "__main__":
    main()
pulumi preview
just hangs. The first time I ran it, I was prompted to allow Python to accept incoming connections. I denied that, so I think its just hanging waiting on some inbound connection
r
Yeah, when using inline programs there is a call to the grpc server running in-process so that's probably what's being blocked.
Ah it also looks like you're defining resources outside the pulumi program itself.
Take a look at https://github.com/pulumi/automation-api-examples#python-examples to see examples of how to use Automation API and where the pulumi program resources must be defined (i.e. in the
pulumi_program
function that is passed into
create_or_select_stack
)
In the code I posted above, my pulumi program simply creates a
random.RandomString
resource. Yours will likely create an RDS resource