Hey guys, One quick question. How can we ensure th...
# general
b
Hey guys, One quick question. How can we ensure the right order of creation of Pulumi components and their resources? Is it again dependsOn property from pulumi.ResourceOpts that needs to be used? I tried that but it is not working for some reason. Does anyone have any idea? A sample code could be found below,
Copy code
Namespace.ts
-----------------
export interface NamespaceArgs {
  name: string;
  metadata: {
    labels: {
      [key: string]: string;
    };
  };
}

export class Namespace extends pulumi.ComponentResource {
  constructor(
    name: string,
    args: NamespaceArgs,
    opts?: pulumi.CustomResourceOptions,
  ) {
    super(Constants.COMPONENT_TYPE, name, {}, opts);

    new k8s.core.v1.Namespace(
      args.name,
      {
        metadata: args.metadata,
      },
      {
        ...opts,
        parent: this,
      },
    );
  }
}


Secret.ts
-----------------
export interface SecretArgs {
  name: string;
  type: string;
  data: string;
  metadata: MetadataArgs;
}

export class Secret extends pulumi.ComponentResource {
  constructor(
    name: string,
    secretArgs: SecretArgs,
    opts?: pulumi.CustomResourceOptions,
  ) {
    super(Constants.COMPONENT_TYPE, name, {}, opts);

    const secretOpts = new SecretOpts();
    new k8s.core.v1.Secret(
      `${secretArgs.metadata.name}-${secretArgs.metadata.namespace}-${Constants.DEPLOYMENT}`,
      {
        data: secretOpts.setData(secretArgs.type, secretArgs.data),
        metadata: secretOpts.setMetadata(secretArgs.metadata),
        type: secretOpts.setType(secretArgs.type),
      },
      {
        ...opts,
        parent: this,
      },
    );
  }
}

IngressNginx.ts
-----------------
export interface IngressNginxArgs {
  helmVersion: HelmVersion.V2 | HelmVersion.V3;
  chartArgs: ChartArgs;
  namespaceArgs: NamespaceArgs;
  secretArgs: SecretArgs;
  provider: k8s.Provider;
}

export class IngressNginx extends pulumi.ComponentResource {
  constructor(
    name: string,
    ingressNginxArgs: IngressNginxArgs,
    opts?: pulumi.CustomResourceOptions,
  ) {
    super(Constants.COMPONENT_TYPE, name, {}, opts);

    const defaultResourceOpts: pulumi.ComponentResourceOptions = {
      parent: this,
      provider: ingressNginxArgs.provider,
    };

    const namespace = new Namespace(
      ingressNginxArgs.namespaceArgs.name,
      ingressNginxArgs.namespaceArgs,
      {
        ...defaultResourceOpts,
      },
    );

    const secret = new Secret(
      ingressNginxArgs.secretArgs.name,
      ingressNginxArgs.secretArgs,
      {
        ...defaultResourceOpts,
        dependsOn: [namespace],
      },
    );

    const ingressNginxResourceOpts: pulumi.ComponentResourceOptions = {
      ...defaultResourceOpts,
      dependsOn: [namespace, secret],
    };
    if (ingressNginxArgs.helmVersion == HelmVersion.V2) {
      new k8s.helm.v2.Chart(
        name,
        new IngressNginxChartOpts(ingressNginxArgs.chartArgs).setChartOpts(),
        ingressNginxResourceOpts,
      );
    } else {
      new k8s.helm.v3.Chart(
        name,
        new IngressNginxChartOpts(ingressNginxArgs.chartArgs).setChartOpts(),
        ingressNginxResourceOpts,
      );
    }
  }
}
p
pulumi should automatically detect dependencies among resources
dependsOn
was introduced for dependencies that cannot be implicitly detected
so if something doesn’t work with
dependsOn
explicitly set, you might have found a bug
Can you describe what exactly didn’t go as expected?
b
I will probably share some screenshots but they are based on
pulumi destroy
The actual behaviour must be as below,
p
but it’s not possible to get the behavior based on this screenshot AFAIK
the parent-child relationship are independent from dependency graphs
b
However, I see the following behaviour as well.
p
I don’t think the order of resources printed by pulumi preview is related to the real order of actions.
If that was the case, you won’t be able to cleanly print the tree structure of resources (showing parent-child relationships).
I might be wrong though… Did you confirm that pulumi indeed tries to perform actions in a wrong order?
b
Ok, is it a problem with pulumi preview then? But, it will go anyway fine with pulumi updates. Is that what you mean?
p
yeah, that’s what I’m suggesting
pulumi preview shows a diff so by default it only includes resources that are going to be either deleted/created/updated/replaced
and it tries to render a tree view to show you the parent/child relationships
b
Ok, why do it behave differently from time to time?
p
you mean the order of resources printed there?
sorry, I’d have to closely compare these 2 screenshots
b
Yes, the order is different and please have a closer look.
p
if there are “the same” assuming you sort resources alphabetically, I’d say it might be expected
I mean, the order is not supposed to matter here, only the set of affected resources and their relationships (again, parent-child only)
and (pure guess) the reason for it might be… golang implementation of maps
map is considered association container and the order of elements is not required to be preserved and in case of golang, it’s gonna be random everytime (https://stackoverflow.com/questions/55925822/why-are-iterations-over-maps-random/55925880#55925880)
b
Ok, let me try to destroy the resources to see if they are getting deleted in right order.
p
To sum up, if: • pulumi performs actions properly (that is, it creates resources with dependsOn after the dependencies are created) • the content (but not the order of elements itself) of pulumi preview looks as expected • the tree structure is rendered as expected (based on
parent
field) there’s nothing wrong here in my opinion :)
b
Got it! However, I will verify things once how things are getting deleted.
p
Sure 🙂 If you still have some doubts after that, let me know and we’ll try to figure this out.
b
Ok, that's great and thanks!
p
Two additional things: 1) You don’t have to use
dependsOn
explicitly if you use another resource outputs while creating a new one (pulumi will detect that automatically); not sure if that’s applicable to your case, I’d have to check the code once again
Copy code
WARNING - pseudo code
----

  resource_a = gcp.BucketOrWhatever(...)

  resource_b = gcp.LoadBalancerToBucket(
=>   target=resource_a.name
  )
due to the fact you referenced
resource_a.name
while creating
resource_b
, pulumi knows it must create a
resource_a
first.
DependsOn
is useful if such dependencies are not visible in code. 2) The fact you marked a resource with
dependsOn
does not affect parent-child relationship. If you want to affect the tree structure for visibility, you have to explicitly set
parent
field.
b
Like @prehistoric-activity-61023 was saying, and to clarify,
pulumi preview
shows you the parent/child relationship - which is purely organizational and has no bearing on the ordering of resource provisioning. The parent/child relationship is one:many, and is used to create logical groups of your resources - such as when you create a
ComponentResource
and assign all resources beneath it as
parent: this,
you are creating a logical group. This is different from the dependency tree, which is many:one and is determined either implicitly by
Outputs
being passed around, or explicitly with the
dependsOn
property - if you would like to see what this tree looks like you can use the
pulumi stack graph
command.
b
Yes, it is true that tree structure you see from
pulumi preview
doesn't affect the parent-child relationship during
pulumi update
and I verified it as well. Thanks a lot @prehistoric-activity-61023 & @bored-oyster-3147 for providing more information and clarity on this.