How can I do this `apply` correctly, it keeps biti...
# python
g
How can I do this
apply
correctly, it keeps biting me?
Copy code
cfg = pulumi.Config()
    ssm.Parameter(
        f"{prefix}-app-dn-credentials",
        name=f"/{prefix}",
        value=json.dumps(
            {
                "user": "user",
                "password": cfg.require_secret('secret').apply(lambda x: x)
            }
        )
    )
r
The
json.dumps
should be within the apply
Copy code
cfg = pulumi.Config()
    ssm.Parameter(
        f"{prefix}-app-dn-credentials",
        name=f"/{prefix}",
        value=cfg.require_secret('secret').apply(lambda x: json.dumps({"user": "user", "password": x}))
    )
g
ah, I will not get used to this weirdness 😞
if I'd need more than 1 secret the resulting syntax would be a complete mess
I cannot even imagine how would it look if I required another secret called
another_secret
to get object like:
Copy code
{"user": "user", "password": x, "another": another_secret}
b
Use Output.all
stolen from the docs
g
thanks I looked there
but it requires so much typing that it doesn't make it any easier
r
🤷🏽
Copy code
my_dict = {"user": "user", "password": x, "another": another_secret}
Output.all(**my_dict).apply(lambda args: json.dumps(args))
👍 1
g
Thanks that works! I should go to bed, I've been trying too hard past week
e
To be fair, it isn't very pythonic @great-sunset-355. Pulumi's Output model still trips me up regularly. I'd love to see the api move to asyncio, but I believe there are some fundamental architectural differences which make this unlikely.
g
I'm simply unable to grasp it I just need to create complex dicts that can be serialized to JSON with this apply magic it's simply just too much
it's enough for me to move the codebase to terraform
What is the way to play with outputs and inputs outside the pulumi scope co I do not have wait so long for execution? if it's possible.
a
pulumi stack output
?
From personal experience, Pulumi is way more flexible than Terraform for obvious reasons. I also had some problems passing stuff around in Terraform, but I was just stuck with those. I agree that dealing with Outputs vs strings is the worst part of Pulumi, but once you "get it" you know how to work with it easily. I do major complex operations with dicts with no problems.
The trick for me is: the stuff inside
apply()
can be used as strings. That's it.
g
that won't help me, I have a complex dict of items which I want to json.dump
but when it fails I get error from
.pulumi/bin/pulumi-language-python-exec", line 92, in <module>
I'm unable to debug the code at all
b
@great-sunset-355 I understand your frustration, this trips a lot of users up and often comes up when users adopt Pulumi. We’re continuously working on improving it where we can Having said that, your frustration is starting to become difficult to interact with. If you pose concrete questions we can try to answer them, but belligerence and complaints make it difficult for us and other community members to help. Please try and consider the way you’re interacting with the community
g
Thank you, I'm really sorry for my behaviour, I'm being pushed in the corner and have no results and sleep deprivation. I really appreciate you are trying to help me. I had to clean up the code because it's pretty messy right now. Are there any examples of recommended patterns? I studied the example you shared before
ol-infrastructure
but I could not find enough with outputs. I'm trying to create a
container_definitions
https://www.pulumi.com/docs/reference/pkg/aws/ecs/taskdefinition/#example-using-container_definitions-and-inference_accelerator using
json.dumps
Copy code
app_env_vars = {
        "EMAIL_HOST_USER": ses_user_access_key.id,
        "EMAIL_HOST_PASSWORD": ses_user_access_key.ses_smtp_password_v4,
        "a": "b",
    }

    # container_name = container_name

    container_definition = {
        "name": container_name,  # is output Config.require
        "image": image_name,  # just str
        "portMappings": [{"containerPort": 80, "hostPort": 80, "protocol": "tcp"}],
        "environment": [{"name": k, "value": v} for k, v in app_env_vars.items()],
    }

    expected_result = container_definition.apply(json.dumps)
r
@great-sunset-355 every time you want to go from a dictionary of outputs to an output dictionary, you’ll need to use
apply
. So for your code, I think something like the following should work:
Copy code
app_env_vars_dict = {
        "EMAIL_HOST_USER": ses_user_access_key.id,
        "EMAIL_HOST_PASSWORD": ses_user_access_key.ses_smtp_password_v4,
        "a": "b",
    }

app_env_vars = Output.all(**app_env_vars_dict).apply(args: [{"name": k, "value": v} for k, v in args.items()])

container_definition = {
        "name": container_name,
        "image": image_name,
        "portMappings": [{"containerPort": 80, "hostPort": 80, "protocol": "tcp"}],
        "environment": app_env_vars,
    }

expected_result = Output.all(**container_definition).apply(json.dumps)
Note that you have multiple outputs that you are trying to convert to json. Output.all accepts keyword args, so when you do
**container_definition
it spreads the keys/values as keyword args.
I recognize that the Output/Input workflow is not straightforward, but it is a necessary tool for eventually resolving values and a lot of what is possible with Pulumi wouldn’t be without it. It took me a while to get the hang of too, but it becomes second-nature after the initial hurdles. I’d recommend reading https://www.pulumi.com/docs/intro/concepts/inputs-outputs/, which I’m sure you’ve seen already, but I had to see it a few times over to really “get” it.
b
👍 1
g
yeah the blogpost was great entry point, helped for a little until I got stuck. I'm not great with absorbing theory, so more complex examples would be awesome
I will try to make a better playground for this and get better
b
We are happy to help if you provide concrete examples of what you need
1
g
@billowy-army-68599 I've been thinking about this, do you think that custom json Encoder passed to
json.dump
could handle the outputs?
b
yes, if it can handle the async nature of the output. it needs to await the result, but otherwise this would work
g
Are there any examples of creating a custom output so I could play with this without calling real resources? I'd like to create isolated environment where I could control and simulate Outputs. If I understand it correctly I'd use component resource and register the outputs. Correct?
b
yes that sounds right, you can also implement a dynamic provider
r
Are there any examples of creating a custom output so I could play with this without calling real resources?
You can use
Output.from_input()
as described in https://www.pulumi.com/docs/intro/concepts/inputs-outputs/#convert-input-to-output-through-interpolation
👍 2
g
@red-match-15116 @billowy-army-68599 I finally got the expected structure! After trying
Output.from_input()
I found out that it can also take the Output or anything else. Also reviewing the ECS Task docs one more time. It turned that
ContainerDefinitions
is not a JSON object but JSON array of objects! from there I made this code: That yields into the desired structure!
Copy code
container_definition = {
            "name": config.container_name,  # is output Config.require
            "image": config.image_name,  # just str
            "portMappings": [{"containerPort": 80, "hostPort": 80, "protocol": "tcp"}],
            "environment": [{"name": k, "value": v} for k, v in config.env_vars.items()],  # config.env_vars is Dict[str,Any]
        }
        container_definitions = Output.from_input([container_definition]).apply(json.dumps)  # List[Dict[str,str]]
Thank you so much for baring with me, in case you'd like to have this in docs or any examples, let me know and I can put something more informative together. Because I believe others will find this helpful.
👍 2