Skip to content

Commit 99534ec

Browse files
committed
Fix fetching charts when it's specified as <repo>/<chart>
1 parent 31fa484 commit 99534ec

File tree

4 files changed

+138
-69
lines changed

4 files changed

+138
-69
lines changed

e2e-tests/e2e.test.ts

Lines changed: 89 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { getAvailablePort } from "https://deno.land/x/port/mod.ts"
1+
import { getAvailablePort } from "https://deno.land/x/port@1.0.0/mod.ts"
22
import * as fs from "https://deno.land/std@0.86.0/fs/mod.ts"
33
import * as path from "https://deno.land/std@0.86.0/path/mod.ts"
44
import * as yaml from "https://deno.land/std@0.86.0/encoding/yaml.ts"
@@ -17,9 +17,9 @@ function toText(bytes: Uint8Array): string {
1717
return new TextDecoder().decode(bytes)
1818
}
1919

20-
async function runHelmDeno(args: string[]) {
20+
async function run(args: string[]) {
2121
const cmd = Deno.run({
22-
cmd: [helmDenoBin, ...args],
22+
cmd: args,
2323
env: {
2424
HELM_PLUGIN_DIR: helmPluginDir,
2525
},
@@ -37,6 +37,14 @@ async function runHelmDeno(args: string[]) {
3737
return { status, stdout: toText(output), stderr: toText(error) }
3838
}
3939

40+
async function runHelm(args: string[]) {
41+
return await run([Deno.env.get("HELM_BIN") as string, ...args])
42+
}
43+
44+
async function runHelmDeno(args: string[]) {
45+
return await run([helmDenoBin, ...args])
46+
}
47+
4048
Deno.test({
4149
name: "should successfuly run `helm deno template` with deno chart",
4250
async fn() {
@@ -314,39 +322,26 @@ function sleep(ms: number): Promise<void> {
314322

315323
async function startHelmRegistry() {
316324
const port = await getAvailablePort()
317-
const cmd = Deno.run({
318-
cmd: [
319-
"docker",
320-
"run",
321-
"--rm",
322-
"--detach",
323-
`--publish=${port}:8080`,
324-
"--env=STORAGE=local",
325-
"--env=STORAGE_LOCAL_ROOTDIR=/home/chartmuseum/charts",
326-
"chartmuseum/chartmuseum:v0.12.0@sha256:38c5ec3b30046d7a02a55b4c8bd8a0cd279538c2e36090973798a858e900b18e",
327-
],
328-
stdout: "piped",
329-
stderr: "piped",
330-
})
331-
332-
const [status, stdout, stderr] = await Promise.all([
333-
cmd.status(),
334-
cmd.output(),
335-
cmd.stderrOutput(),
325+
const { status, stdout, stderr } = await run([
326+
"docker",
327+
"run",
328+
"--rm",
329+
"--detach",
330+
`--publish=${port}:8080`,
331+
"--env=STORAGE=local",
332+
"--env=STORAGE_LOCAL_ROOTDIR=/home/chartmuseum/charts",
333+
"chartmuseum/chartmuseum:v0.12.0@sha256:38c5ec3b30046d7a02a55b4c8bd8a0cd279538c2e36090973798a858e900b18e",
336334
])
337-
cmd.close()
338335

339336
if (!status.success) {
340-
throw new Error(
341-
`Could not start chartmuseum ${new TextDecoder().decode(stderr)}`
342-
)
337+
throw new Error(`Could not start chartmuseum ${stderr}`)
343338
}
344-
const containerID = new TextDecoder().decode(stdout)
339+
const containerID = stdout
345340

346341
let errorsCount = 0
347342
// eslint-disable-next-line no-constant-condition
348343
while (true) {
349-
if (errorsCount > 100) {
344+
if (errorsCount > 10) {
350345
throw new Error("To many errors")
351346
}
352347
try {
@@ -367,21 +362,10 @@ async function startHelmRegistry() {
367362
// There is an error "response from daemon: 404 page not found"
368363
// with full ID not sure why
369364
const shortContainerID = containerID.slice(0, 12)
370-
const cmd = Deno.run({
371-
cmd: ["docker", "stop", shortContainerID],
372-
stdout: "piped",
373-
stderr: "piped",
374-
})
375-
376-
const [status, , stderr] = await Promise.all([
377-
cmd.status(),
378-
cmd.output(),
379-
cmd.stderrOutput(),
380-
])
381-
cmd.close()
365+
const { status, stderr } = await run(["docker", "stop", shortContainerID])
382366

383367
if (!status.success) {
384-
const dockerStopError = new TextDecoder().decode(stderr)
368+
const dockerStopError = stderr
385369
throw new Error(
386370
`Could not stop helm registry docker container ${containerID} ${dockerStopError}`
387371
)
@@ -663,3 +647,67 @@ Deno.test({
663647
assertEquals(status.success, true)
664648
},
665649
})
650+
651+
Deno.test({
652+
name:
653+
"should successfuly run `helm deno template` with remote deno chart (with --repo option)",
654+
ignore: !runAllTests,
655+
async fn() {
656+
const { status } = await runHelmDeno([
657+
"template",
658+
"ingress",
659+
"nginx-ingress",
660+
"--repo",
661+
"https://charts.helm.sh/stable",
662+
"--version",
663+
"1.41.3",
664+
])
665+
666+
assertEquals(status.success, true)
667+
},
668+
})
669+
670+
async function addStableRepo() {
671+
const tmpRepoName = `tmp-repo-${Math.random().toFixed(10).slice(2)}`
672+
const repoAddCmd = await runHelm([
673+
"repo",
674+
"add",
675+
tmpRepoName,
676+
"https://charts.helm.sh/stable",
677+
])
678+
679+
assertEquals(repoAddCmd.status.success, true)
680+
return {
681+
name: tmpRepoName,
682+
async cleanup() {
683+
const repoAddCmd = await runHelm(["repo", "remove", tmpRepoName])
684+
685+
if (!repoAddCmd.status.success) {
686+
throw new Error(`Could not remove repo ${tmpRepoName}`)
687+
}
688+
},
689+
}
690+
}
691+
692+
Deno.test({
693+
name:
694+
"should successfuly run `helm deno template` with remote chart (with helm repo add)",
695+
ignore: !runAllTests,
696+
async fn() {
697+
const tmpRepo = await addStableRepo()
698+
699+
try {
700+
const templateCmd = await runHelmDeno([
701+
"template",
702+
"ingress",
703+
`${tmpRepo.name}/nginx-ingress`,
704+
"--version",
705+
"1.41.3",
706+
])
707+
708+
assertEquals(templateCmd.status.success, true)
709+
} finally {
710+
await tmpRepo.cleanup()
711+
}
712+
},
713+
})

src/helm/fetch.ts

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,26 @@ import { parseHelmFetchArgs } from "../args/parse-helm-fetch-args.ts"
44
import { withErrorMsg } from "../std/mod.ts"
55

66
export async function fetchChart(
7+
chartBlob: string,
8+
tmpDirectory: string,
79
chartPath: string,
8-
destination: string,
910
args: readonly string[]
1011
): Promise<void> {
11-
const destinationExists = await fs.exists(destination)
12+
const destinationExists = await fs.exists(tmpDirectory)
1213
if (!destinationExists) {
13-
return Promise.reject(`Could not find ${destination}`)
14+
return Promise.reject(`Could not find ${tmpDirectory}`)
1415
}
16+
const fetchDestination = path.join(tmpDirectory, "fetch-destination")
1517

1618
const helm = Deno.env.get("HELM_BIN") as string
1719
const cmd = Deno.run({
1820
cmd: [
1921
helm,
2022
"fetch",
21-
chartPath,
23+
chartBlob,
2224
"--untar",
2325
"--untardir",
24-
destination,
26+
fetchDestination,
2527
...parseHelmFetchArgs(args),
2628
],
2729
stdout: "piped",
@@ -40,10 +42,21 @@ export async function fetchChart(
4042
return Promise.reject(new TextDecoder().decode(error))
4143
}
4244

45+
const directories = []
46+
for await (const file of fs.expandGlob("*/", {
47+
root: fetchDestination,
48+
})) {
49+
if (file.isDirectory && !file.path.endsWith(".tgz")) {
50+
directories.push(file.path)
51+
}
52+
}
53+
54+
if (directories.length !== 1) {
55+
throw new Error("Found more then one directory")
56+
}
57+
4358
await withErrorMsg(
44-
fs.copy(path.join(destination, chartPath), destination, {
45-
overwrite: true,
46-
}),
47-
`Could not fetch chart ${chartPath}`
59+
fs.copy(directories[0], chartPath),
60+
`Could not fetch chart ${chartBlob}`
4861
)
4962
}

src/helm/get-chart-context.ts

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,28 +34,32 @@ function normalizeRelease(r: HelmRelease): Release {
3434

3535
export async function getChartContext(
3636
release: string,
37-
chartPath: string,
37+
tmpDir: string,
3838
args: readonly string[]
3939
): Promise<ChartContext> {
40-
const chartContextTemplatePath = path.join(
41-
chartPath,
42-
"templates/values-and-release.yaml"
43-
)
40+
const getValuesChartDir = path.join(tmpDir, "get-values-chart")
4441
try {
45-
await fs.ensureDir(path.join(chartPath, "templates"))
42+
await fs.ensureDir(path.join(getValuesChartDir, "templates"))
4643

4744
await Deno.writeFile(
48-
chartContextTemplatePath,
45+
path.join(getValuesChartDir, "templates/values-and-release.yaml"),
4946
new TextEncoder().encode(valuesAndReleaseData)
5047
)
5148

49+
await Deno.writeFile(
50+
path.join(getValuesChartDir, "Chart.yaml"),
51+
new TextEncoder().encode(
52+
"apiVersion: v2\nname: get-values\nversion: 1.0.0"
53+
)
54+
)
55+
5256
const helm = Deno.env.get("HELM_BIN") as string
5357
const cmd = Deno.run({
5458
cmd: [
5559
helm,
5660
"template",
5761
release,
58-
chartPath,
62+
getValuesChartDir,
5963
...parseHelmTemplateArgs(args),
6064
],
6165
stdout: "piped",
@@ -86,6 +90,8 @@ export async function getChartContext(
8690
values: yaml.parse(x.values),
8791
}
8892
} finally {
89-
await ignoreNotFoundError(Deno.remove(chartContextTemplatePath))
93+
await ignoreNotFoundError(
94+
Deno.remove(getValuesChartDir, { recursive: true })
95+
)
9096
}
9197
}

src/index.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -109,36 +109,38 @@ async function main() {
109109
return
110110
}
111111

112-
const workdir = await withErrorMsg(
112+
const tmpDir = await withErrorMsg(
113113
Deno.makeTempDir({ prefix: "chart-" }),
114114
"Could not create temp directory"
115115
)
116116

117-
debug(`Temporary directory ${workdir} has been created`)
117+
const tmpChartPath = path.join(tmpDir, "chart")
118+
119+
debug(`Temporary directory ${tmpDir} has been created`)
118120
const isLocalChart = await isChartExist(chartLocation)
119121

120122
try {
121123
// Fetch chart into temporaty directory
122124
if (isLocalChart) {
123125
debug(`Copying chart ${chartLocation} to temporary directory`)
124-
await copyChart(chartLocation, workdir)
126+
await copyChart(chartLocation, tmpChartPath)
125127
debug(`Successfuly copied chart ${chartLocation} to temporary directory`)
126128
} else {
127129
debug(`Fetching chart ${chartLocation} to temporary directory`)
128-
await fetchChart(chartLocation, workdir, args)
130+
await fetchChart(chartLocation, tmpDir, tmpChartPath, args)
129131
debug(`Successfuly fetched chart ${chartLocation} to temporary directory`)
130132
}
131133

132-
const chartContext = await getChartContext(releaseName, workdir, args)
134+
const chartContext = await getChartContext(releaseName, tmpDir, args)
133135
debug(`Chart context:\n${JSON.stringify(chartContext, null, 2)}`)
134136

135-
await renderDenoChart(chartContext, workdir, options)
137+
await renderDenoChart(chartContext, tmpChartPath, options)
136138
debug("Deno templates were successfuly rendered")
137139

138140
const helmExecuteArgs = [
139141
...command,
140142
...[releaseName].filter(Boolean),
141-
workdir,
143+
tmpChartPath,
142144
...helmRestArgs,
143145
]
144146
debug(`Executing: ${helmExecuteArgs.join(" ")}`)
@@ -148,8 +150,8 @@ async function main() {
148150
} catch (err) {
149151
const replaceChartPath = (str: string) => {
150152
return isLocalChart
151-
? str.replaceAll(workdir, chartLocation)
152-
: str.replaceAll(`file://${workdir}`, "<chart-root>")
153+
? str.replaceAll(tmpChartPath, chartLocation)
154+
: str.replaceAll(`file://${tmpChartPath}`, "<chart-root>")
153155
}
154156

155157
// Replace paths in error or error stacktrace with readable value
@@ -165,7 +167,7 @@ async function main() {
165167
} finally {
166168
if (!options.keepTmpChart) {
167169
// Remove temporary directory
168-
await ignoreNotFoundError(Deno.remove(workdir, { recursive: true }))
170+
await ignoreNotFoundError(Deno.remove(tmpDir, { recursive: true }))
169171
}
170172
}
171173
}

0 commit comments

Comments
 (0)