```import * as express from 'express' import { Loc...
# automation-api
m
Copy code
import * as express from 'express'
import { LocalWorkspace } from '@pulumi/pulumi/automation'

import clusterApi from './cluster'

const router = express.Router()

const ensurePlugins = async () => {
  const ws = await LocalWorkspace.create({})
  await ws.installPlugin('aws', 'v4.15.0')
  await ws.installPlugin('awsx', 'v0.31.0')
  await ws.installPlugin('eks', 'v0.32.0')
  await ws.installPlugin('kubernetes', 'v3.12.1')
  await ws.installPlugin('kubernetes-cert-manager', 'v0.0.1')
}

// install necessary plugins once upon boot
ensurePlugins()

// API routes
router.use('/cluster', clusterApi)

export default router
t
What's the error you're getting? I see two potential issues: 1. You're not awaiting the
ensurePlugins
function. a. There will be a period of time where the plugins are installing and you won't be notified when it's done. 2. You're not returning and using the workspace reference. a. At the end of
ensurePlugins
add
return ws
It looks like you've correctly created the workspace, but you will probably need a reference that after creation. Something like:
Copy code
const workspace = await ensurePlugins() // or rename it to something like `createWorkspace` or `loadWorkspace`
Another option: Use the static method
createOrSelectStack
from
LocalWorkspace
, then call
installPlugin
on the
stack.workspace
reference. See example here: https://github.com/pulumi/automation-api-examples/blob/80afd678f6adc51eb9f0279ca62408b7f1e4623c/nodejs/inlineProgram-ts/index.ts#L73
m
OK I’ll try that - thank you!
One more quick question while I have you @thousands-hairdresser-72380
I’m a little unclear as to how this
ws
reference works - in the Automation via REST API example code, it shows:
Copy code
const ws = await LocalWorkspace.create({ projectSettings: { name: projectName, runtime: "nodejs" } });
const stacks = await ws.listStacks();
To list stacks in
GET /cluster
for example
And
Copy code
const stack = await LocalWorkspace.createStack({
  stackName,
  projectName,
  // generate our pulumi program on the fly from the POST body
  program: createPulumiProgram(content),
})
To create a stack in
POST /cluster
The former looks like it’s creating a new LocalWorkspace, but in fact, both of these endpoints seem to be sharing the same LocalWorkspace - is this correct?
So if I reference a LocalWorkspace somewhere in the server, it is sharing that for all endpoints?
And if that’s the case, shouldn’t I be running
ensurePlugins
only once somewhere in my server and I don’t need to return and use
ws
in my endpoints?
t
@most-lighter-95902 It really depends on the use-case of the app you're building. 1. Is the goal to provide a web service that can spin up pulumi resources? 2. Who are the users and what is the target usage? a. Are the users spinning up services directly or is this intended to be internal automation? 3. What is the target lifecycle of the resources? a. How long do they last? b. Who is responsible for them? A
LocalWorkspace
is a execution context containing a single Pulumi project, a program, and multiple stacks So it really depends on what you're spinning up. Is it a custom project each time (for example created from an
inline program
) or is it a static project spun up for many users? If it's custom, you'll want a new workspace for each project. If it's just spinning up the same project for many users, you can share a workspace and split up the ownership by stack. Also, I'm no pro, I'm still learning as well. Anyone else here, please feel free to jump in and correct me.
m
My use case is to create a project per client via REST API. Each client will have multiple stacks that spin up an EKS cluster along with a bunch of other services, but essentially my app would manage their infrastructure for them using the Pulumi Automation API.
But I’m unclear as to how the LocalWorkspace works exactly - I understand that a LocalWorkspace is basically equivalent to a Pulumi project, but does the REST API server share a single LocalWorkspace reference or is it per endpoint? Not sure how to reference existing LocalWorkspace vs creating a separate one.
Is LocalWorkspace reference simply differentiated by the
name
field i the
projectSettings
argument? Either way, the pulumiOverHttps-ts tutorial seems to error out for me when I include the
ensurePlugins
function
t
Hmm it's hard to know where to put it without seeing the code. If you could put up a fork of the repo with your changes I may be able to glean more insight. If I were to set up a REST API where clients have managed infrastructure stacks, provided that the app code stays the same for each, I'd probably just have a shared reference. The other problem I see is: creating a workspace is asynchronous, so you'll have to be careful about where you create it and when you know it's ready. My initial thought would be
Copy code
// types.ts
export interface System {
  workspace: LocalWorkspace
}

// workspace.ts
export interface Plugin {
  name: string
  version: string
}

export async function createWorkspace(plugins: Plugin[]) {
  const workspace = await LocalWorkspace.create({})
  // or if it's always the same plugins, just reference them directly here like in your first example
  for(const { name, version } of plugins) {
    await workspace.installPlugin(name, version)
  }
  return workspace
}

// router.ts
import express from 'express'
import asyncHandler from 'express-async-handler'

export default function createRouter({ workspace }: System) {
  const app = express.Router()
  app.get('/stacks', asyncHandler((req, res) => {
    const stacks = await workspace.listStacks()
    res.json(stacks)
  }))

  return app
}

// app.ts
import express from 'express'
import createRouter from './router'

async function main() {
  const defaultPlugins = [...]
  export default workspace = await createWorkspace(defaultPlugins)

  const app = express()

  app.use(createRouter({ workspace }))

  // app.listen, etc.
}
oops, sent too early, still typing....
@most-lighter-95902 done now. btw I wrote this all inline (idk why) so there's probably mistakes but you get the idea.
m
Thanks for the detailed example @thousands-hairdresser-72380 I’ll try that
t
@most-lighter-95902 lmk if you run into issues. it's definitely incomplete and will require additional configuration
m
Will do thanks!
t
@most-lighter-95902 Any luck?
m
Yes turns out I didn’t have Pulumi CLI installed in my server that was causing this error - once I added the script to my Dockerfile to install it, it works fine