Skip to content

Commit

Permalink
feat(commands): add test/task artifacts to command result
Browse files Browse the repository at this point in the history
...and serve .garden/artifacts dir with Garden server
  • Loading branch information
eysi09 authored and edvald committed Nov 12, 2019
1 parent 93ee43c commit 63f245b
Show file tree
Hide file tree
Showing 9 changed files with 276 additions and 17 deletions.
5 changes: 3 additions & 2 deletions garden-service/src/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ import { PluginContext } from "./plugin-context"
import { DeleteServiceTask, deletedServiceStatuses } from "./tasks/delete-service"
import { realpath, writeFile } from "fs-extra"
import { relative, join } from "path"
import { getArtifactKey } from "./util/artifacts"

const maxArtifactLogLines = 5 // max number of artifacts to list in console after task+test runs

Expand Down Expand Up @@ -310,7 +311,7 @@ export class ActionRouter implements TypeGuard {
await this.copyArtifacts(
params.log,
artifactsPath,
`test.${params.testConfig.name}.${params.module.version.versionString}`
getArtifactKey("test", params.testConfig.name, params.module.version.versionString)
)
} finally {
await tmpDir.cleanup()
Expand Down Expand Up @@ -452,7 +453,7 @@ export class ActionRouter implements TypeGuard {
await this.copyArtifacts(
params.log,
artifactsPath,
`task.${params.task.name}.${params.task.module.version.versionString}`
getArtifactKey("task", params.task.name, params.task.module.version.versionString)
)
} finally {
await tmpDir.cleanup()
Expand Down
23 changes: 21 additions & 2 deletions garden-service/src/commands/get/get-task-result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { printHeader } from "../../logger/util"
import { getTaskVersion } from "../../tasks/task"
import { RunTaskResult } from "../../types/plugin/task/runTask"
import chalk from "chalk"
import { getArtifactFileList, getArtifactKey } from "../../util/artifacts"

const getTaskResultArgs = {
name: new StringParameter({
Expand All @@ -22,7 +23,11 @@ const getTaskResultArgs = {

type Args = typeof getTaskResultArgs

export type GetTaskResultCommandResult = RunTaskResult | null
interface Result extends RunTaskResult {
artifacts: string[]
}

export type GetTaskResultCommandResult = Result | null

export class GetTaskResultCommand extends Command<Args> {
name = "task-result"
Expand All @@ -43,12 +48,26 @@ export class GetTaskResultCommand extends Command<Args> {

const actions = await garden.getActionRouter()

const result = await actions.getTaskResult({
const taskResult = await actions.getTaskResult({
log,
task,
taskVersion: await getTaskVersion(garden, graph, task),
})

let result: GetTaskResultCommandResult = null

if (taskResult) {
const artifacts = await getArtifactFileList({
key: getArtifactKey("task", task.name, task.module.version.versionString),
artifactsPath: garden.artifactsPath,
log: garden.log,
})
result = {
...taskResult,
artifacts,
}
}

printHeader(headerLog, `Task result for task ${chalk.cyan(taskName)}`, "rocket")

if (result === null) {
Expand Down
23 changes: 21 additions & 2 deletions garden-service/src/commands/get/get-test-result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { getTestVersion } from "../../tasks/test"
import { findByName, getNames } from "../../util/util"
import { printHeader } from "../../logger/util"
import chalk from "chalk"
import { getArtifactFileList, getArtifactKey } from "../../util/artifacts"

const getTestResultArgs = {
module: new StringParameter({
Expand All @@ -25,7 +26,11 @@ const getTestResultArgs = {
}),
}

export type GetTestResultCommandResult = TestResult | null
interface Result extends TestResult {
artifacts: string[]
}

export type GetTestResultCommandResult = Result | null

type Args = typeof getTestResultArgs

Expand Down Expand Up @@ -67,13 +72,27 @@ export class GetTestResultCommand extends Command<Args> {

const testVersion = await getTestVersion(garden, graph, module, testConfig)

const result = await actions.getTestResult({
const testResult = await actions.getTestResult({
log,
testName,
module,
testVersion,
})

let result: GetTestResultCommandResult = null

if (testResult) {
const artifacts = await getArtifactFileList({
key: getArtifactKey("test", testName, module.version.versionString),
artifactsPath: garden.artifactsPath,
log: garden.log,
})
result = {
...testResult,
artifacts,
}
}

if (result === null) {
log.info(`Could not find results for test '${testName}'`)
} else {
Expand Down
5 changes: 4 additions & 1 deletion garden-service/src/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,11 @@ export class GardenServer {
return this.server.close()
}

setGarden(garden?: Garden) {
setGarden(garden: Garden) {
this.garden = garden

// Serve artifacts as static assets
this.app.use(mount("/artifacts", serve(garden.artifactsPath)))
}

private async createApp() {
Expand Down
50 changes: 50 additions & 0 deletions garden-service/src/util/artifacts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (C) 2018 Garden Technologies, Inc. <info@garden.io>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

import { join } from "path"
import { readFile } from "fs-extra"
import { LogEntry } from "../logger/log-entry"

/**
* @param type task | test
* @param name name of the task or test
* @param version the version of the module that the task/test belongs to
*/
export function getArtifactKey(type: "task" | "test", name: string, version: string) {
return `${type}.${name}.${version}`
}

/**
* Returns the file list from the artifact metadata file (under `.garden/artifacts/.metadata.<key>.json)
* for the given artifact key.
*
* Returns an empty array if the metadata file is not found or if we can't parse it.
*/
export async function getArtifactFileList({
artifactsPath,
key,
log,
}: {
artifactsPath: string
key: string
log: LogEntry
}) {
const metadataPath = join(artifactsPath, `.metadata.${key}.json`)
let files: string[] = []
try {
const metadata = await readFile(metadataPath)
try {
files = JSON.parse(metadata.toString()).files || []
} catch (err) {
log.debug(`Failed parsing artifact metadata file: ${err.message}`)
}
} catch (err) {
log.debug(`Failed reading metadata file: ${err.message}`)
}
return files
}
49 changes: 47 additions & 2 deletions garden-service/test/unit/src/commands/get/get-task-result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ import {
withDefaultGlobalOpts,
configureTestModule,
testModuleSpecSchema,
cleanProject,
} from "../../../../helpers"
import { GetTaskResultCommand } from "../../../../../src/commands/get/get-task-result"
import { expect } from "chai"
import { createGardenPlugin } from "../../../../../src/types/plugin/plugin"
import { LogEntry } from "../../../../../src/logger/log-entry"
import { Garden } from "../../../../../src/garden"
import { GetTaskResultParams } from "../../../../../src/types/plugin/task/getTaskResult"
import { getArtifactKey } from "../../../../../src/util/artifacts"
import { writeFile } from "fs-extra"

const now = new Date()

Expand Down Expand Up @@ -50,14 +53,18 @@ const testPlugin = createGardenPlugin({
describe("GetTaskResultCommand", () => {
let garden: Garden
let log: LogEntry
const projectRootB = join(dataDir, "test-project-b")
const command = new GetTaskResultCommand()

before(async () => {
const projectRootB = join(dataDir, "test-project-b")
beforeEach(async () => {
garden = await Garden.factory(projectRootB, { plugins: [testPlugin] })
log = garden.log
})

afterEach(async () => {
await cleanProject(garden.gardenDirPath)
})

it("should throw error if task not found", async () => {
const name = "banana"

Expand Down Expand Up @@ -88,6 +95,44 @@ describe("GetTaskResultCommand", () => {
})

expect(res.result).to.be.eql({
artifacts: [],
moduleName: "module-a",
taskName: "task-a",
command: ["foo"],
completedAt: now,
log: "bla bla",
outputs: { log: "bla bla" },
success: true,
startedAt: now,
version: "1234",
})
})

it("should include paths to artifacts if artifacts exist", async () => {
const name = "task-a"

const graph = await garden.getConfigGraph()
const module = await graph.getModule("module-a")
const artifactKey = getArtifactKey("task", name, module.version.versionString)
const metadataPath = join(garden.artifactsPath, `.metadata.${artifactKey}.json`)
const metadata = {
key: artifactKey,
files: ["/foo/bar.txt", "/bas/bar.txt"],
}

await writeFile(metadataPath, JSON.stringify(metadata))

const res = await command.action({
garden,
log,
footerLog: log,
headerLog: log,
args: { name },
opts: withDefaultGlobalOpts({}),
})

expect(res.result).to.be.eql({
artifacts: ["/foo/bar.txt", "/bas/bar.txt"],
moduleName: "module-a",
taskName: "task-a",
command: ["foo"],
Expand Down
65 changes: 59 additions & 6 deletions garden-service/test/unit/src/commands/get/get-test-result.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
import { expectError, withDefaultGlobalOpts, configureTestModule, makeTestGardenA } from "../../../../helpers"
import {
expectError,
withDefaultGlobalOpts,
configureTestModule,
makeTestGardenA,
cleanProject,
} from "../../../../helpers"
import { GetTestResultCommand } from "../../../../../src/commands/get/get-test-result"
import { expect } from "chai"
import { GetTestResultParams } from "../../../../../src/types/plugin/module/getTestResult"
import { Garden } from "../../../../../src/garden"
import { LogEntry } from "../../../../../src/logger/log-entry"
import { createGardenPlugin } from "../../../../../src/types/plugin/plugin"
import { joi } from "../../../../../src/config/common"
import { getArtifactKey } from "../../../../../src/util/artifacts"
import { join } from "path"
import { writeFile } from "fs-extra"

const now = new Date()

Expand Down Expand Up @@ -45,13 +54,17 @@ describe("GetTestResultCommand", () => {
let garden: Garden
let log: LogEntry
const command = new GetTestResultCommand()
const module = "module-a"
const moduleName = "module-a"

before(async () => {
beforeEach(async () => {
garden = await makeTestGardenA([testPlugin])
log = garden.log
})

afterEach(async () => {
await cleanProject(garden.gardenDirPath)
})

it("should throw error if test not found", async () => {
const name = "banana"

Expand All @@ -62,7 +75,7 @@ describe("GetTestResultCommand", () => {
log,
headerLog: log,
footerLog: log,
args: { name, module },
args: { name, module: moduleName },
opts: withDefaultGlobalOpts({}),
}),
"not-found"
Expand All @@ -77,11 +90,51 @@ describe("GetTestResultCommand", () => {
log,
headerLog: log,
footerLog: log,
args: { name, module },
args: { name, module: moduleName },
opts: withDefaultGlobalOpts({}),
})

expect(res.result).to.eql({
artifacts: [],
moduleName: "module-a",
command: [],
completedAt: now,
log: "bla bla",
outputs: {
log: "bla bla",
},
success: true,
startedAt: now,
testName: "unit",
version: "1234",
})
})

it("should include paths to artifacts if artifacts exist", async () => {
const name = "unit"

const graph = await garden.getConfigGraph()
const module = await graph.getModule("module-a")
const artifactKey = getArtifactKey("test", name, module.version.versionString)
const metadataPath = join(garden.artifactsPath, `.metadata.${artifactKey}.json`)
const metadata = {
key: artifactKey,
files: ["/foo/bar.txt", "/bas/bar.txt"],
}

await writeFile(metadataPath, JSON.stringify(metadata))

const res = await command.action({
garden,
log,
headerLog: log,
footerLog: log,
args: { name, module: moduleName },
opts: withDefaultGlobalOpts({}),
})

expect(res.result).to.eql({
artifacts: ["/foo/bar.txt", "/bas/bar.txt"],
moduleName: "module-a",
command: [],
completedAt: now,
Expand All @@ -104,7 +157,7 @@ describe("GetTestResultCommand", () => {
log,
footerLog: log,
headerLog: log,
args: { name, module },
args: { name, module: moduleName },
opts: withDefaultGlobalOpts({}),
})

Expand Down
Loading

0 comments on commit 63f245b

Please sign in to comment.