Does anyone have experience with splitting up your...
# typescript
c
Does anyone have experience with splitting up your infra into multiple stacks and referencing resources from one stack in another? Specifically I have a core stack that creates an EKS cluster, and I'm looking to use that cluster in my other stacks. I'm trying to do this with: pulumi.StackReference I can get the reference to the cluster with: cluster = coreInfrastructureReference.requireOutput("cluster"); But how can I then use that cluster variable. I need to access the provider property from it, but Property 'provider' does not exist on type 'Output<any>'. How do I convert this Output<any> to a cluster object?
s
I haven't verified this works but what do you think about trying: • Just export the provider as its own variable rather than the entire Cluster • Use a Type Assertion on the Provider
Copy code
import { ProviderArgs } from '@pulumi/kubernetes'
import { Unwrap } from '@pulumi/pulumi'

const eksProvider = coreInfrastructureReference.requireOutput("eksProvider") as Unwrap<ProviderArgs>
c
I was just attempting to do that, running into issues attempting to export the provider though.
s
what sort of issues?
c
Got that side working but this threw an error: Type 'UnwrappedObject<ProviderArgs>' is missing the following properties from type 'ProviderResource': id, urn, getProvider
s
can you paste your code?
c
On the importation side?
s
yeah
c
This is a condensed version:
Copy code
import * as pulumi from "@pulumi/pulumi";
import { ProviderArgs } from '@pulumi/kubernetes'
import * as k8s from '@pulumi/kubernetes';

const config = new pulumi.Config();

// Obtain the declared configs for the dev stack in api-gateway
const referrerOrganizationName = config.require("core-org");
const referrerProjectName = config.require("core-project-name");
const referrerStackName = config.require("core-stack");

// Create a stack reference to obtain resources from the referrer stack
const coreInfrastructureReference = new pulumi.StackReference(`${referrerOrganizationName}/${referrerProjectName}/${referrerStackName}`);

// Export any required outputs from the core-infrastructure
export const clusterProvider = coreInfrastructureReference.requireOutput("clusterProvider") as pulumi.Unwrap<ProviderArgs>

// Create secrets
export const secrets = new k8s.core.v1.Secret(
    'hasura-secrets',
    {
      metadata: {
        namespace: namespaceName,
      },
      data: {
        hasuraGraphqlJWTSecret: HASURA_GRAPHQL_JWT_SECRET.apply(toBase64),
        backendJWTPrivateKey: AuthJwtPrivateKey.privateKeyPem.apply(toBase64),
        backendJWTPublicKey: AuthJwtPrivateKey.publicKeyPem.apply(toBase64),
        dbConnectionUrl: connectionUrl.apply(toBase64),
      },
    },
    { provider: clusterProvider }
  );
s
instead of
{ provider: clusterProvider }
can you try
Copy code
{ provider: new Provider('whateveryouwanttonamethis', clusterProvider) }
where Provider is imported from '@pulumi/kubernetes'
basically we want to create a
new Provider
object with the same params as the outputted one. Since the output is a json serialized Provider, it's not an actual Provider object
c
That threw no errors, so this is looking good but it's failing for other reasons (The provider isn't available as an output yet, when I ran a pulumi up after adding it as an export it doesn't seem to have added to the outputs). Hoping I don't have to do a down and up.
s
did you add the export to index.ts? you should absolutely not have to do a down and up
c
You're right, it's just because I have so many different resources and commenting things out to make it quicker to debug something got left out which it had a requireOutput for on the other end. I'm rerunning it now.
Didn't work: <ref *1> TypeError: Cannot read properties of undefined (reading 'driver') and TypeError: Cannot read properties of undefined (reading 'burst')
My cluster is an EKS cluster, is it possible it's a different provider?
s
that is a very odd error message. This is a compile error? Can you post your export and import code?
c
Copy code
import * as eks from '@pulumi/eks';
import { Output } from '@pulumi/pulumi';

import * as config from '@project/infrastructure-config';
import { vpc } from './vpc';

const cluster = new eks.Cluster(config.CLUSTER_NAME, {
    name: config.CLUSTER_NAME,
    vpcId: vpc.id,
    publicSubnetIds: vpc.publicSubnetIds,
    privateSubnetIds: vpc.privateSubnetIds,
    instanceType: config.CLUSTER_NODE_INSTANCE_TYPE,
    createOidcProvider: true,
    desiredCapacity: 2,
    minSize: 1,
    maxSize: 3,
});

const kubeconfig: Output<unknown> = cluster.kubeconfig;

const urn: Output<string> = cluster.urn;

const clusterProvider = cluster.provider

export { urn, kubeconfig, clusterProvider };
That's the export.
Copy code
import { Provider } from '@pulumi/kubernetes';
import * as pulumi from "@pulumi/pulumi";
import { ProviderArgs } from '@pulumi/kubernetes'
import * as k8s from '@pulumi/kubernetes';

const config = new pulumi.Config();

// Obtain the declared configs for the dev stack in api-gateway
const referrerOrganizationName = config.require("core-org");
const referrerProjectName = config.require("core-project-name");
const referrerStackName = config.require("core-stack");

// Create a stack reference to obtain resources from the referrer stack
const coreInfrastructureReference = new pulumi.StackReference(`${referrerOrganizationName}/${referrerProjectName}/${referrerStackName}`);

// Export any required outputs from the core-infrastructure
export const clusterProvider = coreInfrastructureReference.requireOutput("clusterProvider") as pulumi.Unwrap<ProviderArgs>

// Create secrets
export const secrets = new k8s.core.v1.Secret(
  'hasura-secrets',
  {
    metadata: {
      namespace: "default",
    },
    data: {
      secret: "test",
    },
  },
  { provider: new Provider('EksCluster', clusterProvider) }
);
s
ah i noticed that the type assertion should have been
Output<Unwrap<ProviderArgs>>
since requireOutput returns an Output. my mistake. but then it means youd have to create your secret within an apply 😕
c
I think the issue is using 2 different provider types, I'm saving an eks.provider, but then trying to create a kubernetes provider, and they have completely different args.
But what's strange is, I can't seem to find the eks.Provider type, despite it being in the docs: https://www.pulumi.com/registry/packages/eks/api-docs/provider/#eks-provider
s
No, the args should be the same. The EKS cluster provider is of type
k8s.Provider
c
Strange, the provider args in the docs for eks.provider show only an ID param?
s
as Unwrap<ProviderArgs>
is wrong though. it needs to be an
Output<Unwrap<ProviderArgs>>
or a
Promise<Unwrap<ProviderArgs>>
if you use
requireOutputValue
if you look at the exported value you'll see that it's a JSON object with the same params that k8s Provider takes in its args
but i wonder if
requireOutput
is reading it in as a stringified json instead? Pulumi making it a Output<any> sort of masks the actual type. if you
coreInfrastructureReference.requireOutput("clusterProvider").apply(console.log)
you should have a better idea what you are importing
c
Copy code
{
      id: '8079f57d-31b6-456a-8ad5-625f489a0db6',
      kubeconfig: '....',
      pkg: 'kubernetes',
      urn: 'urn:pulumi:prod::core::eks:index:Cluster$pulumi:providers:kubernetes::core-k8-cluster-provider'
    }
s
not directly related to the current issue, but i don't personally configure my providers from StackReferences. EKS clusters are not really ephemeral resources, so I just hardcode the cluster ARN in a new provider I create
c
The plan is to have multiple clusters, per env, so that's the reason for not hardcoding them.
s
hmm that's interesting, that value is completely different from what I see in my Stack Output when I export my EKS cluster provider. I'm using the same package as you to create the cluster version 1.0.1
c
That's why I'm extremely confused.
s
ill see if i can reproduce the import issue a bit later
c
Thanks, I appreciate all the help. I'll keep plugging away and seeing why I'm getting this provider as my export.
s
actually, turns out i get the same as you. the initial output of
pulumi up
when you are first creating the provider output is misleading.
It looks like the exported Provider isn't what you want. Maybe you just need to export the properties individually and construct a new Provider with the multiple exported properties. It's not convenient, but it would work... you'd still need to create all your resources within a
.apply
or
.then
though
Or export a custom object with all the properties you'd want in the provider. e.g.
Copy code
export const eksProviderArgs = {
  kubeconfig: cluster.kubeconfig,
  context: ...,
  ...
}
c
I'll give that a try then and see if I can get that working. Thanks for all your help.
Got it working.
Copy code
// Create secrets
export const secrets = new k8s.core.v1.Secret(
  'hasura-secrets',
  {
    metadata: {
      namespace: "default",
    },
    data: {
      dbConnectionUrl: "test",
    },
  },
  {
    provider: new Provider('EksCluster', 
      {
        ...clusterProvider,
        helmReleaseSettings: {
          driver: "secret"
        },
        kubeClientSettings: {
          burst: 10
        }
      }
    )
  }
);
s
oh nice!! i wouldn't have guessed those props you added would have been required. And I'm also confused why you didn't need to do this inside an
apply
given that the
clusterProvider
is an
Output<T>
But glad you got it working!!
c
Ya, I won't question the pulumi gods there lol strange that both those values should have defaults but they're required in there. And as you said why don't I need to do an apply!? But it works!
w
Very glad to hear you got this working! Just wanted to drop this blog post. We recently shipped a feature allowing you to access StackReferences without Output. https://www.pulumi.com/blog/stack-reference-output-details/
c
That is awesome, thank you! I'll look to implement this immediately!