cdk8s-team / cdk8s

Define Kubernetes native apps and abstractions using object-oriented programming
https://cdk8s.io
Apache License 2.0
4.29k stars 290 forks source link

[Helm Synthesis] ERROR: synthesis failed, app expected to create "dist/templates" #1560

Open vumdao opened 11 months ago

vumdao commented 11 months ago

Description of the bug:

Synthesizing application
ERROR: synthesis failed, app expected to create "dist/templates"
👾 Task "helm » sync-helm" failed when executing "cdk8s synth --format helm --chart-version 1.0.0" (cwd: /Users/vudao/workspace/codecommit/cdk8s)
 ERROR  Command failed with exit code 1: projen helm

Reproduction Steps:

  1. Init project using projen

    mkdir repro-cdk8s-helm
    cd repro-cdk8s-helm
    projen new cdk8s-app-ts --no-git --projenrc-ts --package-manager PNPM --github false --cdk8s-version 2.66.9 --cdk8s-cli-version 2.133.0
  2. Add projen task for Synthesizing applications into Helm charts

    
    ➜  repro-cdk8s-helm cat .projenrc.ts 
    import { cdk8s, javascript } from 'projen';
    const project = new cdk8s.Cdk8sTypeScriptApp({
    cdk8sCliVersion: '2.133.0',
    cdk8sVersion: '2.66.9',
    cdk8sPlus: true,
    k8sMinorVersion: 27,
    defaultReleaseBranch: 'main',
    github: false,
    name: 'repro-cdk8s-helm',
    packageManager: javascript.NodePackageManager.PNPM,
    projenrcTs: true,
    });

project.addTask('sync-helm', { description: 'Synthesizing applications into Helm charts', exec: 'cdk8s synth --format helm --chart-version 1.0.0' });

project.addTask('helm', { description: 'Full release build for helm chart', steps: [ { spawn: 'default' }, { spawn: 'pre-compile' }, { spawn: 'compile', }, { spawn: "sync-helm" } ] })

project.synth();


- Update packages

npx projen


3. Create deployment chart

➜ repro-cdk8s-helm cat src/main.ts import { App, Chart, ChartProps } from 'cdk8s'; import * as kplus from 'cdk8s-plus-27'; import { Construct } from 'constructs';

export class MyChart extends Chart { constructor(scope: Construct, id: string, props: ChartProps = { }) { super(scope, id, props);

new kplus.Deployment(this, 'FrontEnds', {
  containers: [{ image: 'node' }],
});

} }

const app = new App({ outdir: 'dist/my-helm', });

new MyChart(app, 'deployment'); app.synth();


4. Build

➜ repro-cdk8s-helm pj helm ../../Library/pnpm/store/v3/tmp/dlx-8904 | +1 + ../../Library/pnpm/store/v3/tmp/dlx-8904 | Progress: resolved 1, reused 1, downloaded 0, added 1, done 👾 helm » default | ts-node --project tsconfig.dev.json .projenrc.ts 👾 helm » compile | tsc --build 👾 helm » sync-helm | cdk8s synth --format helm --chart-version 1.0.0 Synthesizing application ERROR: synthesis failed, app expected to create "dist/templates" 👾 Task "helm » sync-helm" failed when executing "cdk8s synth --format helm --chart-version 1.0.0" (cwd: /Users/vudao/workspace/repro-cdk8s-helm)  ERROR  Command failed with exit code 1: projen helm

pnpm: Command failed with exit code 1: projen helm at makeError (/opt/homebrew/Cellar/pnpm/8.6.7/libexec/dist/pnpm.cjs:24796:17) at handlePromise (/opt/homebrew/Cellar/pnpm/8.6.7/libexec/dist/pnpm.cjs:25367:33) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async Object.handler [as dlx] (/opt/homebrew/Cellar/pnpm/8.6.7/libexec/dist/pnpm.cjs:209900:7) at async /opt/homebrew/Cellar/pnpm/8.6.7/libexec/dist/pnpm.cjs:219307:21 at async main (/opt/homebrew/Cellar/pnpm/8.6.7/libexec/dist/pnpm.cjs:219274:34) at async runPnpm (/opt/homebrew/Cellar/pnpm/8.6.7/libexec/dist/pnpm.cjs:219529:5) at async /opt/homebrew/Cellar/pnpm/8.6.7/libexec/dist/pnpm.cjs:219521:7



### Environment:
  - **OS**: macOS Sonoma 14.0
shinebayar-g commented 11 months ago

I think

const app = new App({
  outdir: 'dist/my-helm',
});

doesn't work.

Try cdk8s synth --output dist/my-helm CLI flag instead.

Related https://github.com/cdk8s-team/cdk8s-cli/issues/210

vumdao commented 11 months ago

@shinebayar-g It does work without helm synthesis

➜  repro-cdk8s-helm pj build
../../Library/pnpm/store/v3/tmp/dlx-2280 |   +1 +
../../Library/pnpm/store/v3/tmp/dlx-2280 | Progress: resolved 1, reused 1, downloaded 0, added 1, done
👾 build » default | ts-node --project tsconfig.dev.json .projenrc.ts
👾 build » compile | tsc --build
👾 build » post-compile » synth | cdk8s synth
Synthesizing application
  - dist/my-helm/deployment.k8s.yaml
👾 build » test | jest --passWithNoTests --updateSnapshot
No tests found, exiting with code 0
----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files |       0 |        0 |       0 |       0 |
----------|---------|----------|---------|---------|-------------------
👾 build » test » eslint | eslint --ext .ts,.tsx --fix --no-error-on-unmatched-pattern src test build-tools projenrc .projenrc.ts
iliapolo commented 11 months ago

@vumdao The issue @shinebayar-g linked to explains the problem.

Here is what happens:

  1. The CLI is unaware of the outdir value configured in your app. So it thinks the output is just dist.
  2. The CLI then tells the App to produce manifests inside dist/templates (via env variable), to conform to helm structure.
  3. The App "ignores" this env variable because the outdir is explicitly set.
  4. The App produces manifests inside dist/my-helm
  5. The CLI still thinks the manifests should be located in dist/templates, and fails to find them.

The reason it works with regular synthesis is just because the outdir you configured happens to be a sub-directory of dist, and the CLI searches for manifests recursively inside what it thinks is the outdir.

There is no real way around it, because the CLI must place the manifests inside {outdir}/templates, but since it doesn't know what outdir is, it is bound to fail.

Are you able to workaround the issue by using --output dist/my-helm as @shinebayar-g suggested? Or is this blocking you?

vumdao commented 11 months ago

@iliapolo Using option --output works well but is it possible to separate build targets like following image

iliapolo commented 11 months ago

What kind of separation are you looking for? Based on what? Are these separate charts or resources inside the same chart?

zhelyan commented 4 months ago

@iliapolo my use case is synth-ing a helm chart per environment ( qa, staging etc) from a single cdk8s app. This doesn't work as all chart instances are part of the same app and get rendered as a single helm chart.

It seems that unlike the cdk ( using the Stage concept ), a single cdk8s App can only emit a single "assembly".

IMO porting / reusing the stage concept to the cdk8s would address this and similar concerns.