Hey everyone! We use Pulumi extensively here, but ...
# automation-api
f
Hey everyone! We use Pulumi extensively here, but all in the CLI. We are just getting started with the automation api. We configured a starter project based on the pulumi over http example, and used the same pulumi.yaml as our cli projects. When we run it, it says it succesfully creates resources, but nothing is created in our project. Has anyone run into this before? I am using GCP and TS.
s
Are you able to share any of your code? Also, how do you have your code organized? Anecdotally I've seen issues if the Automation API code is a parent of the Pulumi program; moving folders around so they are siblings (peers in the same parent folder) usually fixes that.
f
client platform is a working cli program, server is where we are trying out the automation api.
Copy code
├── client-platform
│   ├── Pulumi.summa-development.yaml
│   ├── Pulumi.yaml
│   ├── database.ts
│   ├── index.ts
│   ├── package.json
│   ├── tsconfig.json
│   └── yarn.lock
└── server
    ├── Pulumi.yaml
    ├── index.ts
    ├── package.json
    └── yarn.lock

8 directories, 42 files
Copy code
import {
  LocalWorkspace,
  ConcurrentUpdateError,
  StackAlreadyExistsError,
  StackNotFoundError, InlineProgramArgs, ProjectSettings
} from '@pulumi/pulumi/automation'
import express from "express";
import { DB } from '../client-platform/database'
const projectName = 'summa-development'

const workDir = './'; // Path to your server directory


const createPulumiProgram = (content: string) => async () => {
  const DatabaseParameters = {
    name: 'summa-test-primary',
    region: 'us-east1',
    environment: 'production',
    diskSize: 25,
    tier: 'db-f1-micro',
    replica: true,
    secondaryRegion: 'us-central1',
    secondaryName: 'summa-test-secondary',
  }
  const db = new DB(DatabaseParameters)
  return db;
}

const createHandler: express.RequestHandler = async (req, res) => {
  const stackName = 'automation-api'
  const content = req.body.content as string
  const db = createPulumiProgram(content)
  const args: InlineProgramArgs = {
    stackName: stackName,
    projectName: projectName,
    program: db,
  };

  const projectSettings: ProjectSettings = {
    name: projectName,
    runtime: "nodejs",
    backend: {
      url: '<gs://client-platform-pulumi-state>',
    },
  };
  try {

    const stack = await LocalWorkspace.createOrSelectStack(args, {
      projectSettings,
      secretsProvider,
      stackSettings: {
        [stackName]: {
          secretsProvider,
        },
      },
    })

    const upRes = await stack.up({ onOutput: console.info })
    res.json({ id: stackName })
  } catch (e) {
    if (e instanceof StackAlreadyExistsError) {
      res.status(409).send(`stack "${stackName}" already exists`)
    } else {
      res.status(500).send(e)
    }
  }
}

// deletes a site
const deleteHandler: express.RequestHandler = async (req, res) => {
  const stackName = req.params.id
  try {
    // select the existing stack
    const stack = await LocalWorkspace.selectStack({
      stackName,
      projectName,
      // don't need a program for destroy
      program: async () => { },
    })
    // deploy the stack, tailing the logs to console
    await stack.destroy({ onOutput: console.info })
    await stack.workspace.removeStack(stackName)
    res.status(200).end()
  } catch (e) {
    if (e instanceof StackNotFoundError) {
      res.status(404).send(`stack "${stackName}" does not exist`)
    } else if (e instanceof ConcurrentUpdateError) {
      res
        .status(409)
        .send(`stack "${stackName}" already has update in progress`)
    } else {
      res.status(500).send(e)
    }
  }
}
const ensurePlugins = async () => {
  const ws = await LocalWorkspace.create({
    workDir: workDir,
  })
  await ws.installPlugin('gcp', '6.0.0')
}

// install necessary plugins once upon boot
// ensurePlugins()

const app = express()
app.use(express.json())

app.post('/', createHandler)
app.delete('/:id', deleteHandler)

app.listen(1337, () => console.info('server running on :1337'))
Sorry for the long code file. That is our server. I omitted the secret configuration.
s
Your server program (with the Automation API code) generally doesn't need a
Pulumi.yaml
. Try removing that and see if it helps.
f
ok running
s
A quick perusal of the server/Automation API code seems reasonable, but most of the Automation API code I've written has been in Go.
f
I would prefer Go as well, unfortunately when we started using pulumi the Go SDK was not as good as the TS one, and now all our IaC is in TS.
s
You can write your Automation API code in Go even if the IaC program is in TS. That's a supported configuration/use case.
f
Oh interesting, ill have to take a look at that
the thing that is weird to us, is the output of this is:
Copy code
Updating (automation-api):
    pulumi:pulumi:Stack summa-development-automation-api running

    pulumi:pulumi:Stack summa-development-automation-api
Resources:
    + 1 created

Duration: 3s
which we cannot find in our cloud project anywhere
s
Looks like you're using GCS for your backend, you don't see anything created there?
f
No, the stack isnt even in our bucket. Could it be something to do with the
workdir
? if I log the stack, I get this output
Copy code
Stack {
  name: 'automation-api',
  workspace: LocalWorkspace {
    pulumiHome: undefined,
    program: [AsyncFunction (anonymous)],
    secretsProvider: undefined,
    remote: undefined,
    remoteGitProgramArgs: undefined,
    remotePreRunCommands: undefined,
    remoteEnvVars: {},
    remoteSkipInstallDependencies: undefined,
    workDir: '/var/folders/89/x36p7wgx681f21zfzv45h_gh0000gn/T/automation-2zfOxa',
    envVars: {},
    ready: Promise { [Array] },
    _pulumiVersion: SemVer {
      options: {},
      loose: false,
      includePrerelease: false,
      raw: 'v3.94.2',
      major: 3,
      minor: 94,
      patch: 2,
      prerelease: [],
      build: [],
      version: '3.94.2'
    }
  },
  ready: Promise { undefined }
}
s
Hmm...I would think you should see something. Let me make some inquiries internally and see what I'm missing here. Given that it's the end of the week, it may be a few days before I can get back to you.
f
Thanks scott, I appreciate it. No worries!
r
Hey! Is
DB
a pulumi component where the resources are defined? It seems like we are creating a stack,
f
Yeah, and in some of the examples the stack has a program, and thats what we are passing in
l
@few-electrician-82432 not sure if you solved it already, but my guess is you are mixing up an inline program and a local program. If
./client-platform/database.ts
only contains a function or a component class setting up the database, but the instantiation happens in
./client-platform/index.ts
, then nothing will be created in your current setup. Rather than using an inline program (
InlineProgramArgs
), try to use a local program (
LocalProgramArgs
) and pass a relative path from the automation api workdir to the the Pulumi CLI program:
Copy code
const args: LocalProgramArgs = {
    stackName: "automation-api",
    workDir: upath.joinSafe(__dirname, "..", "client-platform"),
};
So the gist is to point to the full working Pulumi program and as Scott mentioned, at that moment the language you use for the infrastructure code and your automation program may differ. Example: https://github.com/pulumi/automation-api-examples/blob/main/nodejs/localProgram-tsnode/automation/index.ts#L17-L20 If you want to use an inline program, you only have a single program and programming language, containing both. Here is an example of an inline program in TS: https://github.com/pulumi/automation-api-examples/tree/main/nodejs/inlineProgram-ts