From 4cdd051f8423dfaf6dd33e2b85694d139181fa39 Mon Sep 17 00:00:00 2001 From: David Dooling Date: Tue, 25 Feb 2020 11:49:50 -0600 Subject: [PATCH 1/6] Remove unused loglog function --- lib/core/goal/container/util.ts | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/lib/core/goal/container/util.ts b/lib/core/goal/container/util.ts index cad40a40b..74ef91d03 100644 --- a/lib/core/goal/container/util.ts +++ b/lib/core/goal/container/util.ts @@ -15,7 +15,6 @@ */ import { MutationNoCacheOptions } from "@atomist/automation-client/lib/spi/graph/GraphClient"; -import { LeveledLogMethod } from "@atomist/automation-client/lib/util/logger"; import * as fs from "fs-extra"; import * as _ from "lodash"; import * as path from "path"; @@ -23,7 +22,6 @@ import { SdmContext } from "../../../api/context/SdmContext"; import { ExecuteGoalResult } from "../../../api/goal/ExecuteGoalResult"; import { GoalInvocation } from "../../../api/goal/GoalInvocation"; import { SdmGoalEvent } from "../../../api/goal/SdmGoalEvent"; -import { ProgressLog } from "../../../spi/log/ProgressLog"; import { OnBuildCompleteForDryRun, PushFields, @@ -155,20 +153,7 @@ export async function prepareInputAndOutput(input: string, output: string, gi: G } } -/** - * Write to client and progress logs. Add newline to progress log. - * - * @param msg Message to write, should not have newline at end - * @param l Logger method, e.g., `logger.warn` - * @param p Progress log - */ -export function loglog(msg: string, l: LeveledLogMethod, p: ProgressLog): void { - l(msg); - p.write(msg + "\n"); -} - -export async function processResult(result: any, - gi: GoalInvocation): Promise { +export async function processResult(result: any, gi: GoalInvocation): Promise { const { goalEvent, context } = gi; if (!!result) { if (result.SdmGoal) { From 083d736811fa53d342b533d4134d1b367f6836fb Mon Sep 17 00:00:00 2001 From: David Dooling Date: Tue, 25 Feb 2020 12:51:50 -0600 Subject: [PATCH 2/6] Import from where possible --- lib/api-helper/goal/storeGoals.ts | 4 +--- .../project/configuration/projectConfiguration.ts | 4 ++-- lib/core/log/RolarProgressLog.ts | 2 +- .../goal/DefaultGoalImplementationMapper.test.ts | 4 ++-- test/api-helper/listener/executeCancel.test.ts | 7 +++---- test/api-helper/misc/job/createJob.test.ts | 6 +++--- test/api-helper/pushtest/materialChangeTest.test.ts | 4 ++-- test/api/goal/GoalNameGenerator.test.ts | 2 +- 8 files changed, 15 insertions(+), 18 deletions(-) diff --git a/lib/api-helper/goal/storeGoals.ts b/lib/api-helper/goal/storeGoals.ts index 00be42584..f547d8389 100644 --- a/lib/api-helper/goal/storeGoals.ts +++ b/lib/api-helper/goal/storeGoals.ts @@ -21,6 +21,7 @@ import { import { RemoteRepoRef } from "@atomist/automation-client/lib/operations/common/RepoId"; import { MutationNoCacheOptions } from "@atomist/automation-client/lib/spi/graph/GraphClient"; import * as _ from "lodash"; +import * as omitEmpty from "omit-empty"; import { sprintf } from "sprintf-js"; import { Goal, @@ -48,9 +49,6 @@ import { UpdateSdmGoalSetMutationVariables, } from "../../typings/types"; -// tslint:disable-next-line:no-var-requires -const omitEmpty = require("omit-empty"); - export function environmentFromGoal(goal: Goal): string { return goal.definition.environment.replace(/\/$/, ""); // remove trailing slash at least } diff --git a/lib/api-helper/project/configuration/projectConfiguration.ts b/lib/api-helper/project/configuration/projectConfiguration.ts index 1cbdceb44..866bcf349 100644 --- a/lib/api-helper/project/configuration/projectConfiguration.ts +++ b/lib/api-helper/project/configuration/projectConfiguration.ts @@ -1,5 +1,5 @@ /* - * Copyright © 2019 Atomist, Inc. + * Copyright © 2020 Atomist, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,7 @@ import { configurationValue } from "@atomist/automation-client/lib/configuration"; import { Project } from "@atomist/automation-client/lib/project/Project"; import { logger } from "@atomist/automation-client/lib/util/logger"; -import yaml = require("js-yaml"); +import * as yaml from "js-yaml"; import * as _ from "lodash"; /** diff --git a/lib/core/log/RolarProgressLog.ts b/lib/core/log/RolarProgressLog.ts index 591ae4d7f..bc10cdfd2 100644 --- a/lib/core/log/RolarProgressLog.ts +++ b/lib/core/log/RolarProgressLog.ts @@ -23,7 +23,7 @@ import { import { logger } from "@atomist/automation-client/lib/util/logger"; import { redact } from "@atomist/automation-client/lib/util/redact"; import * as _ from "lodash"; -import os = require("os"); +import * as os from "os"; import { format } from "../../api-helper/log/format"; import { ProgressLog } from "../../spi/log/ProgressLog"; diff --git a/test/api-helper/goal/DefaultGoalImplementationMapper.test.ts b/test/api-helper/goal/DefaultGoalImplementationMapper.test.ts index 770271146..4a3464a49 100644 --- a/test/api-helper/goal/DefaultGoalImplementationMapper.test.ts +++ b/test/api-helper/goal/DefaultGoalImplementationMapper.test.ts @@ -1,5 +1,5 @@ /* - * Copyright © 2019 Atomist, Inc. + * Copyright © 2020 Atomist, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,7 @@ import { GitHubRepoRef } from "@atomist/automation-client/lib/operations/common/GitHubRepoRef"; import { InMemoryProject } from "@atomist/automation-client/lib/project/mem/InMemoryProject"; -import assert = require("power-assert"); +import * as assert from "power-assert"; import { DefaultGoalImplementationMapper } from "../../../lib/api-helper/goal/DefaultGoalImplementationMapper"; import { fakePush } from "../../../lib/api-helper/testsupport/fakePush"; import { Goal } from "../../../lib/api/goal/Goal"; diff --git a/test/api-helper/listener/executeCancel.test.ts b/test/api-helper/listener/executeCancel.test.ts index 89b49bbc8..c16f708b4 100644 --- a/test/api-helper/listener/executeCancel.test.ts +++ b/test/api-helper/listener/executeCancel.test.ts @@ -1,5 +1,5 @@ /* - * Copyright © 2019 Atomist, Inc. + * Copyright © 2020 Atomist, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,8 +16,7 @@ import { guid } from "@atomist/automation-client/lib/internal/util/string"; import { MutationOptions } from "@atomist/automation-client/lib/spi/graph/GraphClient"; -import { fail } from "power-assert"; -import assert = require("power-assert"); +import * as assert from "power-assert"; import { executeCancelGoalSets } from "../../../lib/api-helper/listener/executeCancel"; import { AutoCodeInspection } from "../../../lib/api/goal/common/AutoCodeInspection"; import { Autofix } from "../../../lib/api/goal/common/Autofix"; @@ -141,7 +140,7 @@ describe("executeCancelGoalSets", () => { }, messageClient: { send: async () => { - fail(); + assert.fail(); }, }, context: { diff --git a/test/api-helper/misc/job/createJob.test.ts b/test/api-helper/misc/job/createJob.test.ts index 44722036a..60312055a 100644 --- a/test/api-helper/misc/job/createJob.test.ts +++ b/test/api-helper/misc/job/createJob.test.ts @@ -1,5 +1,5 @@ /* - * Copyright © 2019 Atomist, Inc. + * Copyright © 2020 Atomist, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -import assert = require("power-assert"); +import * as assert from "power-assert"; import { createJob, JobTaskType, @@ -220,7 +220,7 @@ describe("createJob", () => { }); it("should allow split parameter into multiple addJobTask calls", async () => { - const parameters = Array.from({length: 500}, (v, k) => ({ counter: k })); + const parameters = Array.from({ length: 500 }, (v, k) => ({ counter: k })); const result = await createJob({ command: "TestCommand", registration: "@atomist/sdm-test", diff --git a/test/api-helper/pushtest/materialChangeTest.test.ts b/test/api-helper/pushtest/materialChangeTest.test.ts index ddf061183..ee926a1ff 100644 --- a/test/api-helper/pushtest/materialChangeTest.test.ts +++ b/test/api-helper/pushtest/materialChangeTest.test.ts @@ -1,5 +1,5 @@ /* - * Copyright © 2019 Atomist, Inc. + * Copyright © 2020 Atomist, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -import assert = require("power-assert"); +import * as assert from "power-assert"; import { anyFileChanged, MaterialChangeOptions, diff --git a/test/api/goal/GoalNameGenerator.test.ts b/test/api/goal/GoalNameGenerator.test.ts index 1f0f0cce3..78c611871 100644 --- a/test/api/goal/GoalNameGenerator.test.ts +++ b/test/api/goal/GoalNameGenerator.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import assert = require("power-assert"); +import * as assert from "power-assert"; import { Autofix } from "../../../lib/api/goal/common/Autofix"; describe("GoalNameGenerator", () => { From 114ed7e3ebe2b4cbbc3f7918086fd20f92e08c86 Mon Sep 17 00:00:00 2001 From: David Dooling Date: Tue, 25 Feb 2020 13:08:21 -0600 Subject: [PATCH 3/6] Resolve no-invalid-this issues See this https://www.typescriptlang.org/docs/handbook/functions.html#this (pun intended). --- lib/api-helper/misc/project/filteredView.ts | 13 +++++-------- lib/api-helper/project/GitHubLazyProjectLoader.ts | 13 ++++--------- test/api-helper/misc/child_process.test.ts | 9 ++++----- .../project/GitHubLazyProjectLoader.test.ts | 5 ++--- test/core/goal/container/docker.test.ts | 7 +------ test/core/goal/container/k8s.test.ts | 4 +--- test/core/mapping/pushtest/toPublicRepo.test.ts | 3 +-- test/core/pack/gcp/cache.test.ts | 3 +-- test/core/pack/k8s/kubernetes/api.test.ts | 12 ++++-------- test/core/pack/k8s/kubernetes/application.test.ts | 6 ++---- test/core/pack/k8s/kubernetes/delete.test.ts | 9 +++------ test/core/pack/k8s/kubernetes/fetch.test.ts | 6 ++---- test/core/pack/k8s/sync/diff.test.ts | 6 ++---- 13 files changed, 32 insertions(+), 64 deletions(-) diff --git a/lib/api-helper/misc/project/filteredView.ts b/lib/api-helper/misc/project/filteredView.ts index 111141a8a..decd2a055 100644 --- a/lib/api-helper/misc/project/filteredView.ts +++ b/lib/api-helper/misc/project/filteredView.ts @@ -1,5 +1,5 @@ /* - * Copyright © 2019 Atomist, Inc. + * Copyright © 2020 Atomist, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,8 +31,7 @@ import * as stream from "stream"; * @param filter function to filter file paths. Return true to eliminate a file * @return {Promise} */ -export function filteredView

(p: Project, - filter: (path: string) => boolean): P { +export function filteredView

(p: Project, filter: (path: string) => boolean): P { // Use an ES6 proxy to bring back memories of Spring AOP const handler = { get: (target, prop) => { @@ -45,10 +44,9 @@ export function filteredView

(p: Project, return origMethod; } const decoratedMethod = decorator[prop]; - return function(...args: any[]): any { + return function(this: any, ...args: any[]): any { return !!decoratedMethod ? decoratedMethod.apply(decorator, args) : - // tslint:disable-next-line:no-invalid-this origMethod.apply(this, args); }; }, @@ -62,8 +60,7 @@ export function filteredView

(p: Project, */ class FilteredProject implements Partial { - constructor(private readonly project: Project, - private readonly filter: (path: string) => boolean) { + constructor(private readonly project: Project, private readonly filter: (path: string) => boolean) { } public getFile(path: string): Promise { @@ -92,7 +89,7 @@ class FilteredProject implements Partial { */ public streamFilesRaw(globPatterns: string[], opts: {}): FileStream { const filter = this.filter; - const onlyIncludedFilters = new stream.Transform({objectMode: true}); + const onlyIncludedFilters = new stream.Transform({ objectMode: true }); onlyIncludedFilters._transform = function(f: any, encoding: string, done: stream.TransformCallback): void { if (filter(f.path)) { this.push(f); diff --git a/lib/api-helper/project/GitHubLazyProjectLoader.ts b/lib/api-helper/project/GitHubLazyProjectLoader.ts index c28b11e00..b7acf50c1 100644 --- a/lib/api-helper/project/GitHubLazyProjectLoader.ts +++ b/lib/api-helper/project/GitHubLazyProjectLoader.ts @@ -1,5 +1,5 @@ /* - * Copyright © 2019 Atomist, Inc. + * Copyright © 2020 Atomist, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -79,9 +79,7 @@ class GitHubLazyProject extends AbstractProject implements GitProject, LazyProje private projectPromise: QueryablePromise; - constructor(id: RemoteRepoRef, - private readonly delegate: ProjectLoader, - private readonly params: ProjectLoadingParameters) { + constructor(id: RemoteRepoRef, private readonly delegate: ProjectLoader, private readonly params: ProjectLoadingParameters) { super(id); } @@ -198,8 +196,7 @@ class GitHubLazyProject extends AbstractProject implements GitProject, LazyProje public streamFilesRaw(globPatterns: string[], opts: {}): FileStream { const resultStream = new stream.Transform({ objectMode: true }); - resultStream._transform = function(chunk: any, encoding: string, done: stream.TransformCallback): void { - // tslint:disable-next-line:no-invalid-this + resultStream._transform = function(this: stream.Transform, chunk: any, encoding: string, done: stream.TransformCallback): void { this.push(chunk); done(); }; @@ -227,9 +224,7 @@ class GitHubLazyProject extends AbstractProject implements GitProject, LazyProje return this.projectPromise.then(mp => mp.configureFromRemote()) as any; } - public createAndSetRemote(gid: RemoteRepoRef, - description: string, - visibility: "private" | "public"): Promise { + public createAndSetRemote(gid: RemoteRepoRef, description: string, visibility: "private" | "public"): Promise { this.materializeIfNecessary("createAndSetRemote"); return this.projectPromise.then(mp => mp.createAndSetRemote(gid, description, visibility)) as any; } diff --git a/test/api-helper/misc/child_process.test.ts b/test/api-helper/misc/child_process.test.ts index 5f3e456ba..28ff33ccf 100644 --- a/test/api-helper/misc/child_process.test.ts +++ b/test/api-helper/misc/child_process.test.ts @@ -1,5 +1,5 @@ /* - * Copyright © 2019 Atomist, Inc. + * Copyright © 2020 Atomist, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -149,7 +149,7 @@ describe("child_process", () => { let closed = false; let exited = false; const cp = spawn("node", ["-e", script]); - cp.on("exit", (code, signal) => { + cp.on("exit", () => { exited = true; }); cp.on("close", (code, signal) => { @@ -165,9 +165,8 @@ describe("child_process", () => { assert(closed, `child process ${cp.pid} should have closed`); }); - it("should fall through to SIGKILL", async function(): Promise { + it("should fall through to SIGKILL", async function(this: Mocha.Context): Promise { if (os.platform() === "win32") { - /* tslint:disable-next-line:no-invalid-this */ this.skip(); } // delay to allow the spawned node process to start and set up signal handler @@ -179,7 +178,7 @@ describe("child_process", () => { let closed = false; let exited = false; const cp = spawn("node", ["-e", script]); - cp.on("exit", (code, signal) => { + cp.on("exit", () => { exited = true; }); cp.on("close", (code, signal) => { diff --git a/test/api-helper/project/GitHubLazyProjectLoader.test.ts b/test/api-helper/project/GitHubLazyProjectLoader.test.ts index 98b215b52..7b51acd06 100644 --- a/test/api-helper/project/GitHubLazyProjectLoader.test.ts +++ b/test/api-helper/project/GitHubLazyProjectLoader.test.ts @@ -1,5 +1,5 @@ /* - * Copyright © 2019 Atomist, Inc. + * Copyright © 2020 Atomist, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,9 +33,8 @@ const credentials = { describe("GitHubLazyProjectLoader", () => { - before(function(): void { + before(function(this: Mocha.Context): void { if (!process.env.GITHUB_TOKEN) { - // tslint:disable-next-line:no-invalid-this this.skip(); } }); diff --git a/test/core/goal/container/docker.test.ts b/test/core/goal/container/docker.test.ts index 4e30d380f..23ab50e2f 100644 --- a/test/core/goal/container/docker.test.ts +++ b/test/core/goal/container/docker.test.ts @@ -243,20 +243,15 @@ describe("goal/container/docker", () => { }, } as any; - before(async function dockerCheckProjectSetup(): Promise { - // tslint:disable-next-line:no-invalid-this + before(async function dockerCheckProjectSetup(this: Mocha.Context): Promise { this.timeout(20000); if (runningInK8s() || (!process.env.DOCKER_HOST && !fs.existsSync("/var/run/docker.sock"))) { - // tslint:disable-next-line:no-invalid-this this.skip(); - return; } try { await execPromise("docker", ["pull", containerTestImage], { timeout: 18000 }); } catch (e) { - // tslint:disable-next-line:no-invalid-this this.skip(); - return; } await fs.ensureDir(projectDir); tmpDirs.push(projectDir); diff --git a/test/core/goal/container/k8s.test.ts b/test/core/goal/container/k8s.test.ts index d7806fd4b..298b82c6c 100644 --- a/test/core/goal/container/k8s.test.ts +++ b/test/core/goal/container/k8s.test.ts @@ -1782,8 +1782,7 @@ dGe21S9sMOqyEp9D8geeXkg3VAItxuXbLIBfKL45kwSvB6fEFtQnJEOrT4YXSRDY let originalOsHostname: any; let k8sCore: k8s.CoreV1Api; - before(async function minikubeCheckProjectSetup(): Promise { - // tslint:disable-next-line:no-invalid-this + before(async function minikubeCheckProjectSetup(this: Mocha.Context): Promise { this.timeout(20000); try { // see if minikube is available and responding @@ -1792,7 +1791,6 @@ dGe21S9sMOqyEp9D8geeXkg3VAItxuXbLIBfKL45kwSvB6fEFtQnJEOrT4YXSRDY const kc = loadKubeConfig(); k8sCore = kc.makeApiClient(k8s.CoreV1Api); } catch (e) { - // tslint:disable-next-line:no-invalid-this this.skip(); } originalOsHostname = Object.getOwnPropertyDescriptor(os, "hostname"); diff --git a/test/core/mapping/pushtest/toPublicRepo.test.ts b/test/core/mapping/pushtest/toPublicRepo.test.ts index ade900f0e..87b6fce26 100644 --- a/test/core/mapping/pushtest/toPublicRepo.test.ts +++ b/test/core/mapping/pushtest/toPublicRepo.test.ts @@ -22,11 +22,10 @@ import { ToPublicRepo } from "../../../../lib/core/mapping/pushtest/toPublicRepo describe("pushToPublicRepo", () => { let credentials: any; - before(function(): void { + before(function(this: Mocha.Context): void { if (process.env.GITHUB_TOKEN) { credentials = { token: process.env.GITHUB_TOKEN }; } else { - // tslint:disable-next-line:no-invalid-this this.skip(); } }); diff --git a/test/core/pack/gcp/cache.test.ts b/test/core/pack/gcp/cache.test.ts index 76d0c5a2c..0b354a4c0 100644 --- a/test/core/pack/gcp/cache.test.ts +++ b/test/core/pack/gcp/cache.test.ts @@ -117,9 +117,8 @@ describe("support/cache", () => { describe("GoogleCloudStorageGoalCacheArchiveStore", () => { - before(function(): void { + before(function(this: Mocha.Context): void { if (!process.env.GOOGLE_APPLICATION_CREDENTIALS || !process.env.GCS_TEST_BUCKET) { - // tslint:disable-next-line:no-invalid-this this.skip(); } }); diff --git a/test/core/pack/k8s/kubernetes/api.test.ts b/test/core/pack/k8s/kubernetes/api.test.ts index 4e79da267..0573c402d 100644 --- a/test/core/pack/k8s/kubernetes/api.test.ts +++ b/test/core/pack/k8s/kubernetes/api.test.ts @@ -75,14 +75,12 @@ describe("pack/k8s/kubernetes/api", () => { }); - describe("integration", function(): void { + describe("integration", function(this: Mocha.Suite): void { - // tslint:disable-next-line:no-invalid-this this.timeout(5000); - before(async function(): Promise { + before(async function(this: Mocha.Context): Promise { if (!await k8sAvailable()) { - // tslint:disable-next-line:no-invalid-this this.skip(); } beforeRetry(); @@ -94,12 +92,11 @@ describe("pack/k8s/kubernetes/api", () => { describe("K8sObjectApi.specUriPath", () => { let client: K8sObjectApi; - before(function(): void { + before(function(this: Mocha.Context): void { try { const kc = loadKubeConfig(); client = kc.makeApiClient(K8sObjectApi); } catch (e) { - // tslint:disable-next-line:no-invalid-this this.skip(); } }); @@ -331,12 +328,11 @@ describe("pack/k8s/kubernetes/api", () => { describe("baseRequestOptions", () => { let client: K8sObjectApi; - before(function(): void { + before(function(this: Mocha.Context): void { try { const kc = loadKubeConfig(); client = kc.makeApiClient(K8sObjectApi); } catch (e) { - // tslint:disable-next-line:no-invalid-this this.skip(); } }); diff --git a/test/core/pack/k8s/kubernetes/application.test.ts b/test/core/pack/k8s/kubernetes/application.test.ts index 99a4d31a2..1bb50cb39 100644 --- a/test/core/pack/k8s/kubernetes/application.test.ts +++ b/test/core/pack/k8s/kubernetes/application.test.ts @@ -53,14 +53,12 @@ describe("pack/k8s/kubernetes/application", () => { }); - describe("upsertApplication & deleteApplication", function(): void { + describe("upsertApplication & deleteApplication", function(this: Mocha.Suite): void { - // tslint:disable-next-line:no-invalid-this this.timeout(10000); - before(async function(): Promise { + before(async function(this: Mocha.Context): Promise { if (!await k8sAvailable()) { - // tslint:disable-next-line:no-invalid-this this.skip(); } beforeRetry(); diff --git a/test/core/pack/k8s/kubernetes/delete.test.ts b/test/core/pack/k8s/kubernetes/delete.test.ts index 93a7ea2f5..b4b2f0440 100644 --- a/test/core/pack/k8s/kubernetes/delete.test.ts +++ b/test/core/pack/k8s/kubernetes/delete.test.ts @@ -37,14 +37,12 @@ import { rng, } from "../k8s"; -describe("pack/k8s/kubernetes/delete", function(): void { +describe("pack/k8s/kubernetes/delete", function(this: Mocha.Suite): void { - // tslint:disable-next-line:no-invalid-this this.timeout(5000); - before(async function(): Promise { + before(async function(this: Mocha.Context): Promise { if (!await k8sAvailable()) { - // tslint:disable-next-line:no-invalid-this this.skip(); } beforeRetry(); @@ -56,12 +54,11 @@ describe("pack/k8s/kubernetes/delete", function(): void { describe("deleteAppResources", () => { let clients: KubernetesClients; - before(function(): void { + before(function(this: Mocha.Context): void { let config: k8s.KubeConfig; try { config = loadKubeConfig(); } catch (e) { - // tslint:disable-next-line:no-invalid-this this.skip(); } clients = makeApiClients(config); diff --git a/test/core/pack/k8s/kubernetes/fetch.test.ts b/test/core/pack/k8s/kubernetes/fetch.test.ts index 4f83eef63..36f884b5b 100644 --- a/test/core/pack/k8s/kubernetes/fetch.test.ts +++ b/test/core/pack/k8s/kubernetes/fetch.test.ts @@ -1392,14 +1392,12 @@ describe("pack/k8s/kubernetes/fetch", () => { }); - describe("kubernetesFetch", function(): void { + describe("kubernetesFetch", function(this: Mocha.Suite): void { - // tslint:disable-next-line:no-invalid-this this.timeout(15000); - before(async function(): Promise { + before(async function(this: Mocha.Context): Promise { if (!await k8sAvailable()) { - // tslint:disable-next-line:no-invalid-this this.skip(); } beforeRetry(); diff --git a/test/core/pack/k8s/sync/diff.test.ts b/test/core/pack/k8s/sync/diff.test.ts index 2c67fa0fc..adc349b52 100644 --- a/test/core/pack/k8s/sync/diff.test.ts +++ b/test/core/pack/k8s/sync/diff.test.ts @@ -77,16 +77,14 @@ describe("pack/k8s/sync/diff", () => { }); - describe("diffPush", function(): void { + describe("diffPush", function(this: Mocha.Suite): void { - // tslint:disable-next-line:no-invalid-this this.timeout(10000); - before(async function(): Promise { + before(async function(this: Mocha.Context): Promise { try { await execPromise("git", ["fetch", "origin", "test-branch-do-not-delete"]); } catch (e) { - // tslint:disable-next-line:no-invalid-this this.skip(); } }); From f7de1689f9b4214d9e347b9be954fef6c902ca23 Mon Sep 17 00:00:00 2001 From: David Dooling Date: Tue, 25 Feb 2020 15:51:26 -0600 Subject: [PATCH 4/6] Consolidate goal event data parsing --- lib/api-helper/goal/sdmGoal.ts | 25 ++++++++++++---- lib/api/goal/GoalWithFulfillment.ts | 3 +- lib/api/mapping/goalTest.ts | 3 +- lib/core/goal/cache/goalCaching.ts | 12 ++++---- lib/core/goal/container/k8s.ts | 29 ++++--------------- lib/core/goal/skillOutput.ts | 3 +- lib/core/pack/k8s/deploy/data.ts | 9 ++---- .../KubernetesFulfillmentGoalScheduler.ts | 3 +- .../k8s/scheduler/KubernetesGoalScheduler.ts | 6 ++-- 9 files changed, 46 insertions(+), 47 deletions(-) diff --git a/lib/api-helper/goal/sdmGoal.ts b/lib/api-helper/goal/sdmGoal.ts index 9b44905e4..3646d23d8 100644 --- a/lib/api-helper/goal/sdmGoal.ts +++ b/lib/api-helper/goal/sdmGoal.ts @@ -1,5 +1,5 @@ /* - * Copyright © 2018 Atomist, Inc. + * Copyright © 2020 Atomist, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,22 +35,35 @@ export function goalKeyString(gk: SdmGoalKey): string { } /** - * Retrieve the goal data - * Note: this purposely only works if the data field is stringified JSON. + * Read and parse goal event data. If the goal event has no data, + * return an empty object. Note: this purposely only works if the + * data field is stringified JSON. + * * @param sdmGoal + * @return JSON parsed goal event data property */ export function goalData(sdmGoal: SdmGoalEvent): any { + if (!sdmGoal?.data) { + return {}; + } + let data: any; try { - return JSON.parse(sdmGoal.data || ""); + data = JSON.parse(sdmGoal.data); } catch (e) { - throw new Error("Goal data is not stringified JSON"); + e.message = `Failed to parse goal event data for ${sdmGoal.uniqueName} as JSON '${sdmGoal.data}': ${e.message}`; + throw e; } + return data; } /** - * Merge the provided data into the goal data + * Return a shallow merge the provided `data` and the goal event data + * property, parsed as JSON. Properties in `data` take precedence + * over those in the parsed goal event data object. + * * @param data * @param sdmGoal + * @return shallow merge of data and SDM goal event data property */ export function mergeGoalData(data: any, sdmGoal: SdmGoalEvent): any { return { diff --git a/lib/api/goal/GoalWithFulfillment.ts b/lib/api/goal/GoalWithFulfillment.ts index 283fbb1a2..a5aa76521 100644 --- a/lib/api/goal/GoalWithFulfillment.ts +++ b/lib/api/goal/GoalWithFulfillment.ts @@ -17,6 +17,7 @@ import { configurationValue } from "@atomist/automation-client/lib/configuration"; import { RetryOptions } from "@atomist/automation-client/lib/util/retry"; import * as _ from "lodash"; +import { goalData } from "../../api-helper/goal/sdmGoal"; import { LogSuppressor } from "../../api-helper/log/logInterpreters"; import { AbstractSoftwareDeliveryMachine } from "../../api-helper/machine/AbstractSoftwareDeliveryMachine"; import { InterpretLog } from "../../spi/log/InterpretedLog"; @@ -223,7 +224,7 @@ export abstract class FulfillableGoal extends GoalWithPrecondition implements Re callback: async (goalEvent, repoContext) => { const service = await registration.service(goalEvent, repoContext); if (!!service) { - const data = JSON.parse(goalEvent.data || "{}"); + const data = goalData(goalEvent); const servicesData = {}; _.set(servicesData, `${ServiceRegistrationGoalDataKey}.${registration.name}`, service); goalEvent.data = JSON.stringify(_.merge(data, servicesData)); diff --git a/lib/api/mapping/goalTest.ts b/lib/api/mapping/goalTest.ts index b74f07260..78a1b8845 100644 --- a/lib/api/mapping/goalTest.ts +++ b/lib/api/mapping/goalTest.ts @@ -17,6 +17,7 @@ import { AutomationContextAware } from "@atomist/automation-client/lib/HandlerContext"; import { isEventIncoming } from "@atomist/automation-client/lib/internal/transport/RequestProcessor"; import * as _ from "lodash"; +import { goalData } from "../../api-helper/goal/sdmGoal"; import { SdmGoalState } from "../../typings/types"; import { StatefulPushListenerInvocation } from "../dsl/goalContribution"; import { SdmGoalEvent } from "../goal/SdmGoalEvent"; @@ -51,7 +52,7 @@ export function isGoal(options: { return false; } if (!!options.output) { - const data = JSON.parse(g.data || "{}"); + const data = goalData(g); const outputs: Array<{ classifier: string }> = data["@atomist/sdm/output"]; if (!outputs) { return false; diff --git a/lib/core/goal/cache/goalCaching.ts b/lib/core/goal/cache/goalCaching.ts index 3209b6636..5b4ec9f12 100644 --- a/lib/core/goal/cache/goalCaching.ts +++ b/lib/core/goal/cache/goalCaching.ts @@ -20,6 +20,7 @@ import { GitProject } from "@atomist/automation-client/lib/project/git/GitProjec import { Project } from "@atomist/automation-client/lib/project/Project"; import { gatherFromFiles } from "@atomist/automation-client/lib/project/util/projectUtils"; import * as _ from "lodash"; +import { goalData } from "../../../api-helper/goal/sdmGoal"; import { ExecuteGoalResult } from "../../../api/goal/ExecuteGoalResult"; import { GoalInvocation, @@ -147,8 +148,7 @@ export function cachePut(options: GoalCacheOptions, return { name: listenerName, - listener: async (p: GitProject, - gi: GoalInvocation): Promise => { + listener: async (p: GitProject, gi: GoalInvocation): Promise => { const { goalEvent } = gi; if (!!isCacheEnabled(gi) && !process.env.ATOMIST_ISOLATED_GOAL_INIT) { const cloneEntries = _.cloneDeep(entries); @@ -171,7 +171,7 @@ export function cachePut(options: GoalCacheOptions, } // Set outputs on the goal data - const data = JSON.parse(goalEvent.data || "{}"); + const data = goalData(goalEvent); const newData = { [CacheOutputGoalDataKey]: [ ...(data[CacheOutputGoalDataKey] || []), @@ -179,7 +179,7 @@ export function cachePut(options: GoalCacheOptions, ], }; goalEvent.data = JSON.stringify({ - ...(JSON.parse(goalEvent.data || "{}")), + ...(goalData(goalEvent)), ...newData, }); } @@ -281,7 +281,7 @@ export function cacheRestore(options: GoalCacheRestoreOptions, // Set inputs on the goal data const { goalEvent } = gi; - const data = JSON.parse(goalEvent.data || "{}"); + const data = goalData(goalEvent); const newData = { [CacheInputGoalDataKey]: [ ...(data[CacheInputGoalDataKey] || []), @@ -291,7 +291,7 @@ export function cacheRestore(options: GoalCacheRestoreOptions, ], }; goalEvent.data = JSON.stringify({ - ...(JSON.parse(goalEvent.data || "{}")), + ...(goalData(goalEvent)), ...newData, }); }, diff --git a/lib/core/goal/container/k8s.ts b/lib/core/goal/container/k8s.ts index 07956e8ea..9ef72edd7 100644 --- a/lib/core/goal/container/k8s.ts +++ b/lib/core/goal/container/k8s.ts @@ -31,6 +31,7 @@ import { Merge, } from "ts-essentials"; import { minimalClone } from "../../../api-helper/goal/minimalClone"; +import { goalData } from "../../../api-helper/goal/sdmGoal"; import { RepoContext } from "../../../api/context/SdmContext"; import { ExecuteGoalResult } from "../../../api/goal/ExecuteGoalResult"; import { @@ -189,7 +190,7 @@ export function k8sFulfillmentCallback( } // Preserve the container registration in the goal data before it gets munged with internals - let data = parseGoalEventData(goalEvent); + let data = goalData(goalEvent); let newData: any = {}; delete spec.callback; _.set(newData, ContainerRegistrationGoalDataKey, spec); @@ -338,7 +339,7 @@ export function k8sFulfillmentCallback( }; // Store k8s service registration in goal data - data = JSON.parse(goalEvent.data || "{}"); + data = goalData(goalEvent); newData = {}; _.set(newData, `${ServiceRegistrationGoalDataKey}.${registration.name}`, serviceSpec); goalEvent.data = JSON.stringify(_.merge(data, newData)); @@ -355,7 +356,7 @@ export function k8sFulfillmentCallback( export const scheduleK8sJob: ExecuteGoal = async gi => { const { goalEvent } = gi; const { uniqueName } = goalEvent; - const data = parseGoalEventData(goalEvent); + const data = goalData(goalEvent); const containerReg: K8sContainerRegistration = data["@atomist/sdm/container"]; if (!containerReg) { throw new Error(`Goal ${uniqueName} event data has no container spec: ${goalEvent.data}`); @@ -416,7 +417,7 @@ export function executeK8sJob(): ExecuteGoal { const inputDir = process.env.ATOMIST_INPUT_DIR || ContainerInput; const outputDir = process.env.ATOMIST_OUTPUT_DIR || ContainerOutput; - const data = parseGoalEventData(goalEvent); + const data = goalData(goalEvent); if (!data[ContainerRegistrationGoalDataKey]) { throw new Error("Failed to read k8s ContainerRegistration from goal data"); } @@ -559,24 +560,6 @@ export function executeK8sJob(): ExecuteGoal { }; } -/** - * Read and parse container goal registration from goal event data. - */ -export function parseGoalEventData(goalEvent: SdmGoalEvent): any { - const goalName = goalEvent.uniqueName; - if (!goalEvent || !goalEvent.data) { - return {}; - } - let data: any; - try { - data = JSON.parse(goalEvent.data); - } catch (e) { - e.message = `Failed to parse goal event data for ${goalName} as JSON '${goalEvent.data}': ${e.message}`; - throw e; - } - return data; -} - /** * If running as isolated goal, use [[executeK8sJob]] to execute the * goal. Otherwise, schedule the goal execution as a Kubernetes job @@ -591,7 +574,7 @@ const containerFulfillerCacheRestore: GoalProjectListenerRegistration = { name: "cache restore", events: [GoalProjectListenerEvent.before], listener: async (project, gi) => { - const data = parseGoalEventData(gi.goalEvent); + const data = goalData(gi.goalEvent); if (!data[ContainerRegistrationGoalDataKey]) { throw new Error(`Goal ${gi.goal.uniqueName} has no Kubernetes container registration: ${gi.goalEvent.data}`); } diff --git a/lib/core/goal/skillOutput.ts b/lib/core/goal/skillOutput.ts index e5723abc4..caf109dff 100644 --- a/lib/core/goal/skillOutput.ts +++ b/lib/core/goal/skillOutput.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { goalData } from "../../api-helper/goal/sdmGoal"; import { GoalExecutionListener } from "../../api/listener/GoalStatusListener"; import { CustomSkillOutputInput, @@ -42,7 +43,7 @@ export const SkillOutputGoalExecutionListener: GoalExecutionListener = async gi return; } - const data = JSON.parse(goalEvent.data || "{}"); + const data = goalData(goalEvent); const entries: Array = data[CacheOutputGoalDataKey] || []; for (const entry of entries.filter(e => !!e.type && !!e.classifier && !!e.uri)) { diff --git a/lib/core/pack/k8s/deploy/data.ts b/lib/core/pack/k8s/deploy/data.ts index d47695816..7f332d772 100644 --- a/lib/core/pack/k8s/deploy/data.ts +++ b/lib/core/pack/k8s/deploy/data.ts @@ -22,6 +22,7 @@ import * as dockerfileParser from "docker-file-parser"; import * as stringify from "json-stringify-safe"; import * as _ from "lodash"; import { DeepPartial } from "ts-essentials"; +import { goalData } from "../../../../api-helper/goal/sdmGoal"; import { GoalInvocation } from "../../../../api/goal/GoalInvocation"; import { SdmGoalEvent } from "../../../../api/goal/SdmGoalEvent"; import { readSdmVersion } from "../../../internal/delivery/build/local/projectVersioner"; @@ -93,7 +94,7 @@ export function generateKubernetesGoalEventData( const slug = goalEventSlug(goalEvent); let eventData: any; try { - eventData = JSON.parse(goalEvent.data || "{}"); + eventData = goalData(goalEvent); } catch (e) { logger.warn(`Failed to parse goal event data for ${slug} as JSON: ${e.message}`); logger.warn(`Ignoring current value of goal event data: ${goalEvent.data}`); @@ -127,14 +128,10 @@ export function generateKubernetesGoalEventData( * @return Parsed [[KubernetesApplication]] object */ export function getKubernetesGoalEventData(goalEvent: SdmGoalEvent): KubernetesApplication | undefined { - if (!goalEvent || !goalEvent.data) { - return undefined; - } let data: any; try { - data = JSON.parse(goalEvent.data); + data = goalData(goalEvent); } catch (e) { - e.message = `Failed to parse goal event data (${goalEvent.data}): ${e.message}`; logger.error(e.message); throw e; } diff --git a/lib/core/pack/k8s/scheduler/KubernetesFulfillmentGoalScheduler.ts b/lib/core/pack/k8s/scheduler/KubernetesFulfillmentGoalScheduler.ts index 683b62b96..c00a0b71e 100644 --- a/lib/core/pack/k8s/scheduler/KubernetesFulfillmentGoalScheduler.ts +++ b/lib/core/pack/k8s/scheduler/KubernetesFulfillmentGoalScheduler.ts @@ -16,6 +16,7 @@ import * as _ from "lodash"; import { minimalClone } from "../../../../api-helper/goal/minimalClone"; +import { goalData } from "../../../../api-helper/goal/sdmGoal"; import { descriptionFromState } from "../../../../api-helper/goal/storeGoals"; import { ExecuteGoalResult } from "../../../../api/goal/ExecuteGoalResult"; import { GoalInvocation } from "../../../../api/goal/GoalInvocation"; @@ -82,7 +83,7 @@ export class KubernetesFulfillmentGoalScheduler implements GoalScheduler { }); } - const data: any = JSON.parse(goalEvent.data || "{}"); + const data: any = goalData(goalEvent); const newData: any = {}; delete registration.callback; _.set(newData, ContainerRegistrationGoalDataKey, registration); diff --git a/lib/core/pack/k8s/scheduler/KubernetesGoalScheduler.ts b/lib/core/pack/k8s/scheduler/KubernetesGoalScheduler.ts index 6faf9bf56..4347d5ee3 100644 --- a/lib/core/pack/k8s/scheduler/KubernetesGoalScheduler.ts +++ b/lib/core/pack/k8s/scheduler/KubernetesGoalScheduler.ts @@ -33,6 +33,7 @@ import * as fs from "fs-extra"; import * as stringify from "json-stringify-safe"; import * as _ from "lodash"; import * as os from "os"; +import { goalData } from "../../../../api-helper/goal/sdmGoal"; import { ExecuteGoalResult } from "../../../../api/goal/ExecuteGoalResult"; import { GoalInvocation } from "../../../../api/goal/GoalInvocation"; import { SdmGoalEvent } from "../../../../api/goal/SdmGoalEvent"; @@ -330,11 +331,12 @@ export function createJobSpec(podSpec: k8s.V1Pod, podNs: string, gi: GoalInvocat // Add additional specs from registered services to the job spec if (_.get(gi.configuration, "sdm.k8s.service.enabled", true)) { if (!!goalEvent.data) { - let data: any = {}; + let data: any; try { - data = JSON.parse(goalEvent.data); + data = goalData(goalEvent); } catch (e) { logger.warn(`Failed to parse goal data on '${goalEvent.uniqueName}'`); + data = {}; } if (!!data[ServiceRegistrationGoalDataKey]) { _.forEach(data[ServiceRegistrationGoalDataKey], (v, k) => { From 4818156635e9a80dbe26d8d354de0f2c9ff01a5f Mon Sep 17 00:00:00 2001 From: David Dooling Date: Wed, 26 Feb 2020 14:00:17 -0600 Subject: [PATCH 5/6] Consolidate k8s error message handling --- lib/core/goal/container/k8s.ts | 18 +---------- lib/core/pack/k8s/kubernetes/application.ts | 6 ++-- lib/core/pack/k8s/kubernetes/apply.ts | 6 ++-- lib/core/pack/k8s/kubernetes/delete.ts | 10 +++--- lib/core/pack/k8s/kubernetes/deployment.ts | 4 +-- lib/core/pack/k8s/kubernetes/fetch.ts | 4 +-- lib/core/pack/k8s/kubernetes/ingress.ts | 4 +-- lib/core/pack/k8s/kubernetes/namespace.ts | 6 ++-- lib/core/pack/k8s/kubernetes/role.ts | 6 ++-- lib/core/pack/k8s/kubernetes/roleBinding.ts | 6 ++-- lib/core/pack/k8s/kubernetes/secret.ts | 4 +-- lib/core/pack/k8s/kubernetes/service.ts | 4 +-- .../pack/k8s/kubernetes/serviceAccount.ts | 4 +-- .../k8s/scheduler/KubernetesGoalScheduler.ts | 2 +- lib/core/pack/k8s/support/error.ts | 4 +-- lib/core/pack/k8s/support/retry.ts | 4 +-- lib/core/pack/k8s/sync/goals.ts | 4 +-- lib/core/pack/k8s/sync/sync.ts | 6 ++-- test/core/pack/k8s/support/error.test.ts | 31 ++++++++++--------- 19 files changed, 59 insertions(+), 74 deletions(-) diff --git a/lib/core/goal/container/k8s.ts b/lib/core/goal/container/k8s.ts index 9ef72edd7..b2f08a2db 100644 --- a/lib/core/goal/container/k8s.ts +++ b/lib/core/goal/container/k8s.ts @@ -58,6 +58,7 @@ import { K8sServiceRegistrationType, K8sServiceSpec, } from "../../pack/k8s/scheduler/service"; +import { k8sErrMsg } from "../../pack/k8s/support/error"; import { toArray } from "../../util/misc/array"; import { CacheEntry, @@ -771,20 +772,3 @@ function containerCleanup(c: ContainerDetritus): void { c.watcher.abort(); } } - -/** Try to find a Kubernetes API error message. */ -export function k8sErrMsg(e: any): string { - if (e.message && typeof e.message === "string") { - return e.message; - } else if (e.body && typeof e.body === "string") { - return e.body; - } else if (e.body?.message && typeof e.body.message === "string") { - return e.body.message; - } else if (e.response?.body && typeof e.response.body === "string") { - return e.response.body; - } else if (e.response?.body?.message && typeof e.response.body.message === "string") { - return e.response.body.message; - } else { - return "Kubernetes API request error"; - } -} diff --git a/lib/core/pack/k8s/kubernetes/application.ts b/lib/core/pack/k8s/kubernetes/application.ts index 5ee94c451..7f215339b 100644 --- a/lib/core/pack/k8s/kubernetes/application.ts +++ b/lib/core/pack/k8s/kubernetes/application.ts @@ -15,7 +15,7 @@ */ import * as k8s from "@kubernetes/client-node"; -import { errMsg } from "../support/error"; +import { k8sErrMsg } from "../support/error"; import { KubernetesClients, makeApiClients, @@ -73,7 +73,7 @@ export async function upsertApplication(app: KubernetesApplication, sdmFulfiller k8sResources.push(await upsertIngress(req)); return k8sResources.filter(r => !!r); } catch (e) { - e.message = `Failed to upsert '${reqString(req)}': ${errMsg(e)}`; + e.message = `Failed to upsert '${reqString(req)}': ${k8sErrMsg(e)}`; throw e; } } @@ -170,7 +170,7 @@ export async function deleteApplication(del: KubernetesDelete): Promise client.create(spec), `create resource ${slug}`); } diff --git a/lib/core/pack/k8s/kubernetes/delete.ts b/lib/core/pack/k8s/kubernetes/delete.ts index 9f57084c8..555c58ca3 100644 --- a/lib/core/pack/k8s/kubernetes/delete.ts +++ b/lib/core/pack/k8s/kubernetes/delete.ts @@ -16,7 +16,7 @@ import { logger } from "@atomist/automation-client/lib/util/logger"; import * as k8s from "@kubernetes/client-node"; -import { errMsg } from "../support/error"; +import { k8sErrMsg } from "../support/error"; import { logRetry } from "../support/retry"; import { K8sDeleteResponse, @@ -46,13 +46,13 @@ export async function deleteSpec(spec: k8s.KubernetesObject): Promise req.clients.apps.createNamespacedDeployment(spec.metadata.namespace, spec), `create deployment ${slug}`); diff --git a/lib/core/pack/k8s/kubernetes/fetch.ts b/lib/core/pack/k8s/kubernetes/fetch.ts index c1888c5e0..dd45d391b 100644 --- a/lib/core/pack/k8s/kubernetes/fetch.ts +++ b/lib/core/pack/k8s/kubernetes/fetch.ts @@ -16,7 +16,7 @@ import * as k8s from "@kubernetes/client-node"; import * as _ from "lodash"; -import { errMsg } from "../support/error"; +import { k8sErrMsg } from "../support/error"; import { K8sObjectApi } from "./api"; import { KubernetesClients, @@ -172,7 +172,7 @@ export async function kubernetesFetch(options: KubernetesFetchOptions = defaultK client = kc.makeApiClient(K8sObjectApi); clients = makeApiClients(kc); } catch (e) { - e.message = `Failed to create Kubernetes client: ${errMsg(e)}`; + e.message = `Failed to create Kubernetes client: ${k8sErrMsg(e)}`; throw e; } diff --git a/lib/core/pack/k8s/kubernetes/ingress.ts b/lib/core/pack/k8s/kubernetes/ingress.ts index 849a27ba6..265493774 100644 --- a/lib/core/pack/k8s/kubernetes/ingress.ts +++ b/lib/core/pack/k8s/kubernetes/ingress.ts @@ -17,7 +17,7 @@ import { logger } from "@atomist/automation-client/lib/util/logger"; import * as k8s from "@kubernetes/client-node"; import * as _ from "lodash"; -import { errMsg } from "../support/error"; +import { k8sErrMsg } from "../support/error"; import { logRetry } from "../support/retry"; import { applicationLabels } from "./labels"; import { metadataTemplate } from "./metadata"; @@ -52,7 +52,7 @@ export async function upsertIngress(req: KubernetesResourceRequest): Promise req.clients.ext.createNamespacedIngress(spec.metadata.namespace, spec), `create ingress ${slug}`); return spec; diff --git a/lib/core/pack/k8s/kubernetes/namespace.ts b/lib/core/pack/k8s/kubernetes/namespace.ts index fa1f9d947..b0f5f9c2b 100644 --- a/lib/core/pack/k8s/kubernetes/namespace.ts +++ b/lib/core/pack/k8s/kubernetes/namespace.ts @@ -16,7 +16,7 @@ import { logger } from "@atomist/automation-client/lib/util/logger"; import * as k8s from "@kubernetes/client-node"; -import { errMsg } from "../support/error"; +import { k8sErrMsg } from "../support/error"; import { logRetry } from "../support/retry"; import { applicationLabels } from "./labels"; import { metadataTemplate } from "./metadata"; @@ -41,7 +41,7 @@ export async function upsertNamespace(req: KubernetesResourceRequest): Promise req.clients.core.createNamespace(spec), `create namespace ${slug}`); return spec; @@ -51,7 +51,7 @@ export async function upsertNamespace(req: KubernetesResourceRequest): Promise req.clients.core.patchNamespace(spec.metadata.name, spec, undefined, undefined, undefined, undefined, patchHeaders()), `patch namespace ${slug}`); } catch (e) { - logger.warn(`Failed to patch existing namespace ${slug}, ignoring: ${errMsg(e)}`); + logger.warn(`Failed to patch existing namespace ${slug}, ignoring: ${k8sErrMsg(e)}`); } return spec; } diff --git a/lib/core/pack/k8s/kubernetes/role.ts b/lib/core/pack/k8s/kubernetes/role.ts index 1a9fa5c31..4f3ef72bf 100644 --- a/lib/core/pack/k8s/kubernetes/role.ts +++ b/lib/core/pack/k8s/kubernetes/role.ts @@ -17,7 +17,7 @@ import { logger } from "@atomist/automation-client/lib/util/logger"; import * as k8s from "@kubernetes/client-node"; import * as _ from "lodash"; -import { errMsg } from "../support/error"; +import { k8sErrMsg } from "../support/error"; import { logRetry } from "../support/retry"; import { applicationLabels } from "./labels"; import { metadataTemplate } from "./metadata"; @@ -43,7 +43,7 @@ export async function upsertRole(req: KubernetesResourceRequest): Promise req.clients.rbac.createClusterRole(spec), `create cluster role ${slug}`); return spec; @@ -57,7 +57,7 @@ export async function upsertRole(req: KubernetesResourceRequest): Promise req.clients.rbac.createNamespacedRole(spec.metadata.namespace, spec), `create role ${slug}`); return spec; diff --git a/lib/core/pack/k8s/kubernetes/roleBinding.ts b/lib/core/pack/k8s/kubernetes/roleBinding.ts index c870e8b17..ad0aff1d8 100644 --- a/lib/core/pack/k8s/kubernetes/roleBinding.ts +++ b/lib/core/pack/k8s/kubernetes/roleBinding.ts @@ -17,7 +17,7 @@ import { logger } from "@atomist/automation-client/lib/util/logger"; import * as k8s from "@kubernetes/client-node"; import * as _ from "lodash"; -import { errMsg } from "../support/error"; +import { k8sErrMsg } from "../support/error"; import { logRetry } from "../support/retry"; import { applicationLabels } from "./labels"; import { metadataTemplate } from "./metadata"; @@ -43,7 +43,7 @@ export async function upsertRoleBinding(req: KubernetesResourceRequest): Promise try { await req.clients.rbac.readClusterRoleBinding(spec.metadata.name); } catch (e) { - logger.debug(`Failed to read cluster role binding ${slug}, creating: ${errMsg(e)}`); + logger.debug(`Failed to read cluster role binding ${slug}, creating: ${k8sErrMsg(e)}`); logger.info(`Creating cluster role binding ${slug} using '${logObject(spec)}'`); await logRetry(() => req.clients.rbac.createClusterRoleBinding(spec), `create cluster role binding ${slug}`); @@ -58,7 +58,7 @@ export async function upsertRoleBinding(req: KubernetesResourceRequest): Promise try { await req.clients.rbac.readNamespacedRoleBinding(spec.metadata.name, spec.metadata.namespace); } catch (e) { - logger.debug(`Failed to read role binding ${slug}, creating: ${errMsg(e)}`); + logger.debug(`Failed to read role binding ${slug}, creating: ${k8sErrMsg(e)}`); logger.info(`Creating role binding ${slug} using '${logObject(spec)}'`); await logRetry(() => req.clients.rbac.createNamespacedRoleBinding(spec.metadata.namespace, spec), `create role binding ${slug}`); diff --git a/lib/core/pack/k8s/kubernetes/secret.ts b/lib/core/pack/k8s/kubernetes/secret.ts index 36e117148..e3189deb2 100644 --- a/lib/core/pack/k8s/kubernetes/secret.ts +++ b/lib/core/pack/k8s/kubernetes/secret.ts @@ -21,7 +21,7 @@ import { decrypt, encrypt, } from "../support/crypto"; -import { errMsg } from "../support/error"; +import { k8sErrMsg } from "../support/error"; import { logRetry } from "../support/retry"; import { applicationLabels } from "./labels"; import { metadataTemplate } from "./metadata"; @@ -55,7 +55,7 @@ export async function upsertSecrets(req: KubernetesResourceRequest): Promise req.clients.core.createNamespacedSecret(spec.metadata.namespace, spec), `create secret ${secretName} for ${slug}`); diff --git a/lib/core/pack/k8s/kubernetes/service.ts b/lib/core/pack/k8s/kubernetes/service.ts index 1be3bb067..490218b1a 100644 --- a/lib/core/pack/k8s/kubernetes/service.ts +++ b/lib/core/pack/k8s/kubernetes/service.ts @@ -17,7 +17,7 @@ import { logger } from "@atomist/automation-client/lib/util/logger"; import * as k8s from "@kubernetes/client-node"; import * as _ from "lodash"; -import { errMsg } from "../support/error"; +import { k8sErrMsg } from "../support/error"; import { logRetry } from "../support/retry"; import { applicationLabels, @@ -51,7 +51,7 @@ export async function upsertService(req: KubernetesResourceRequest): Promise req.clients.core.createNamespacedService(spec.metadata.namespace, spec), `create service ${slug}`); return spec; diff --git a/lib/core/pack/k8s/kubernetes/serviceAccount.ts b/lib/core/pack/k8s/kubernetes/serviceAccount.ts index 05bbd7861..5e8a51da5 100644 --- a/lib/core/pack/k8s/kubernetes/serviceAccount.ts +++ b/lib/core/pack/k8s/kubernetes/serviceAccount.ts @@ -17,7 +17,7 @@ import { logger } from "@atomist/automation-client/lib/util/logger"; import * as k8s from "@kubernetes/client-node"; import * as _ from "lodash"; -import { errMsg } from "../support/error"; +import { k8sErrMsg } from "../support/error"; import { logRetry } from "../support/retry"; import { applicationLabels } from "./labels"; import { metadataTemplate } from "./metadata"; @@ -42,7 +42,7 @@ export async function upsertServiceAccount(req: KubernetesResourceRequest): Prom try { await req.clients.core.readNamespacedServiceAccount(spec.metadata.name, spec.metadata.namespace); } catch (e) { - logger.debug(`Failed to read service account ${slug}, creating: ${errMsg(e)}`); + logger.debug(`Failed to read service account ${slug}, creating: ${k8sErrMsg(e)}`); logger.info(`Creating service account ${slug} using '${logObject(spec)}'`); await logRetry(() => req.clients.core.createNamespacedServiceAccount(spec.metadata.namespace, spec), `create service account ${slug}`); diff --git a/lib/core/pack/k8s/scheduler/KubernetesGoalScheduler.ts b/lib/core/pack/k8s/scheduler/KubernetesGoalScheduler.ts index 4347d5ee3..0fc8c114c 100644 --- a/lib/core/pack/k8s/scheduler/KubernetesGoalScheduler.ts +++ b/lib/core/pack/k8s/scheduler/KubernetesGoalScheduler.ts @@ -39,12 +39,12 @@ import { GoalInvocation } from "../../../../api/goal/GoalInvocation"; import { SdmGoalEvent } from "../../../../api/goal/SdmGoalEvent"; import { GoalScheduler } from "../../../../api/goal/support/GoalScheduler"; import { ServiceRegistrationGoalDataKey } from "../../../../api/registration/ServiceRegistration"; -import { k8sErrMsg } from "../../../goal/container/k8s"; import { toArray } from "../../../util/misc/array"; import { loadKubeClusterConfig, loadKubeConfig, } from "../kubernetes/config"; +import { k8sErrMsg } from "../support/error"; import { K8sNamespaceFile } from "../support/namespace"; import { K8sServiceRegistrationType, diff --git a/lib/core/pack/k8s/support/error.ts b/lib/core/pack/k8s/support/error.ts index 5b6113348..6b4711c85 100644 --- a/lib/core/pack/k8s/support/error.ts +++ b/lib/core/pack/k8s/support/error.ts @@ -24,7 +24,7 @@ import * as request from "request"; * @param e Some sort of Error or similar * @return Error message */ -export function errMsg(e: any): string { +export function k8sErrMsg(e: any): string { if (!e) { return stringify(e); } else if (typeof e === "string") { @@ -36,7 +36,7 @@ export function errMsg(e: any): string { } else if (requestErrMsg(e)) { return requestErrMsg(e); } else { - return stringify(e, keyFilter); + return `Kubernetes API request error: ${stringify(e, keyFilter)}`; } } diff --git a/lib/core/pack/k8s/support/retry.ts b/lib/core/pack/k8s/support/retry.ts index e27d7e073..937031d22 100644 --- a/lib/core/pack/k8s/support/retry.ts +++ b/lib/core/pack/k8s/support/retry.ts @@ -18,7 +18,7 @@ import { doWithRetry, RetryOptions, } from "@atomist/automation-client/lib/util/retry"; -import { errMsg } from "./error"; +import { k8sErrMsg } from "./error"; /** * Extract Kubernetes errors for doWithRetry. @@ -30,7 +30,7 @@ export async function logRetry(f: () => Promise, desc: string, options?: R r = await f(); } catch (e) { if (!(e instanceof Error)) { - const err = new Error(errMsg(e)); + const err = new Error(k8sErrMsg(e)); Object.keys(e).forEach(k => (err as any)[k] = e[k]); throw err; } diff --git a/lib/core/pack/k8s/sync/goals.ts b/lib/core/pack/k8s/sync/goals.ts index 701a29364..056dd3323 100644 --- a/lib/core/pack/k8s/sync/goals.ts +++ b/lib/core/pack/k8s/sync/goals.ts @@ -37,7 +37,7 @@ import { KubernetesSyncOptions, SyncRepoRef, } from "../config"; -import { errMsg } from "../support/error"; +import { k8sErrMsg } from "../support/error"; import { changeResource } from "./change"; import { diffPush } from "./diff"; import { isRemoteRepo } from "./repo"; @@ -124,7 +124,7 @@ export const K8sSync: ExecuteGoal = async gi => { try { await changeResource(p, change); } catch (e) { - e.message = `Failed to ${change.change} '${change.path}' resource for commit ${change.sha}: ${errMsg(e)}`; + e.message = `Failed to ${change.change} '${change.path}' resource for commit ${change.sha}: ${k8sErrMsg(e)}`; log.write(e.message); errs.push(e); } diff --git a/lib/core/pack/k8s/sync/sync.ts b/lib/core/pack/k8s/sync/sync.ts index 0572d3d68..ba2332350 100644 --- a/lib/core/pack/k8s/sync/sync.ts +++ b/lib/core/pack/k8s/sync/sync.ts @@ -31,7 +31,7 @@ import { parseKubernetesSpecs, specSlug, } from "../kubernetes/spec"; -import { errMsg } from "../support/error"; +import { k8sErrMsg } from "../support/error"; import { cleanName } from "../support/name"; import { defaultCloneOptions } from "./clone"; import { k8sSpecGlob } from "./diff"; @@ -140,13 +140,13 @@ function syncApply(opts: KubernetesSyncOptions): (p: GitProject) => Promise { +describe("core/pack/k8s/support/error", () => { - describe("errMsg", () => { + describe("k8sErrMsg", () => { it("should handle undefined", () => { - const m = errMsg(undefined); + const m = k8sErrMsg(undefined); assert(m === undefined); }); it("should handle null", () => { // tslint:disable-next-line:no-null-keyword - const m = errMsg(null); + const m = k8sErrMsg(null); assert(m === "null"); }); it("should handle an Error", () => { const r = new Error("Blitzen Trapper"); - const m = errMsg(r); + const m = k8sErrMsg(r); const e = "Blitzen Trapper"; assert(m === e); }); @@ -123,7 +123,7 @@ describe("pack/k8s/support/error", () => { retriesLeft: 5, }; /* tslint:enable:max-line-length no-null-keyword */ - const m = errMsg(r); + const m = k8sErrMsg(r); assert(m === r.body.message); }); @@ -169,7 +169,7 @@ describe("pack/k8s/support/error", () => { retriesLeft: 5, }; /* tslint:disable:no-null-keyword */ - const m = errMsg(r); + const m = k8sErrMsg(r); assert(m === "404 page not found\n"); }); @@ -177,21 +177,21 @@ describe("pack/k8s/support/error", () => { const r = { blitzenTrapper: "Furr", }; - const m = errMsg(r); - const e = JSON.stringify(r); + const m = k8sErrMsg(r); + const e = `Kubernetes API request error: {"blitzenTrapper":"Furr"}`; assert(m === e); }); it("should handle an array", () => { const r = ["Blitzen", "Trapper", "Furr"]; - const m = errMsg(r); + const m = k8sErrMsg(r); const e = JSON.stringify(r); assert(m === e); }); it("should handle a string", () => { const r = "Blitzen Trapper"; - const m = errMsg(r); + const m = k8sErrMsg(r); assert(m === r); }); @@ -208,8 +208,9 @@ describe("pack/k8s/support/error", () => { JWT: "Echo/Always On/Easy Con", }, }; - const m = errMsg(r); - const e = `{"blitzenTrapper":"Furr","token":"**************","Key":"******","song":{"JWT":"E*********************n"}}`; + const m = k8sErrMsg(r); + const e = "Kubernetes API request error: " + + `{"blitzenTrapper":"Furr","token":"**************","Key":"******","song":{"JWT":"E*********************n"}}`; assert(m === e); }); @@ -221,7 +222,7 @@ describe("pack/k8s/support/error", () => { }, }, }; - const m = errMsg(r); + const m = k8sErrMsg(r); assert(m === "Blitzen Trapper"); }); From 00386740182ecea081f72bd5442a666349a73064 Mon Sep 17 00:00:00 2001 From: David Dooling Date: Tue, 25 Feb 2020 11:49:35 -0600 Subject: [PATCH 6/6] Move k8s container goal implementation to pack Move Kubernetes container goal implementation to k8s pack. Remove reference to KubernetesJobDeletingGoalCompletionListenerFactory. Add tests for container goal methods to check failing runtime require statements. --- lib/core/goal/container/container.ts | 6 +- .../k8s.ts => pack/k8s/container.ts} | 38 +-- .../KubernetesFulfillmentGoalScheduler.ts | 2 +- test/core/goal/container/container.test.ts | 243 ++++++++++++++++++ .../k8s/container.test.ts} | 4 +- ...KubernetesFulfillmentGoalScheduler.test.ts | 2 +- 6 files changed, 271 insertions(+), 24 deletions(-) rename lib/core/{goal/container/k8s.ts => pack/k8s/container.ts} (98%) create mode 100644 test/core/goal/container/container.test.ts rename test/core/{goal/container/k8s.test.ts => pack/k8s/container.test.ts} (99%) diff --git a/lib/core/goal/container/container.ts b/lib/core/goal/container/container.ts index 026f12c3a..772235061 100644 --- a/lib/core/goal/container/container.ts +++ b/lib/core/goal/container/container.ts @@ -274,8 +274,6 @@ export class Container extends FulfillableGoalWithRegistrations gs instanceof kgs.KubernetesGoalScheduler)) { if (!process.env.ATOMIST_ISOLATED_GOAL && kgs.isConfiguredInEnv("kubernetes", "kubernetes-all")) { sdm.configuration.sdm.goalScheduler = [...goalSchedulers, new kgs.KubernetesGoalScheduler()]; - const kjdgcl = require("../../pack/k8s/scheduler/KubernetesJobDeletingGoalCompletionListener"); - sdm.addGoalCompletionListener(new kjdgcl.KubernetesJobDeletingGoalCompletionListenerFactory(sdm).create()); } } } else if (runningAsGoogleCloudFunction()) { @@ -295,10 +293,10 @@ export class Container extends FulfillableGoalWithRegistrations { goal.addFulfillment({ goalExecutor: executeK8sJob(), @@ -155,6 +158,9 @@ export const k8sContainerScheduler: ContainerScheduler = (goal, registration: K8 }); }; +/** + * Container scheduler to use when running in Google Cloud Functions. + */ export const k8sSkillContainerScheduler: ContainerScheduler = (goal, registration: K8sContainerRegistration) => { goal.addFulfillment({ goalExecutor: executeK8sJob(), diff --git a/lib/core/pack/k8s/scheduler/KubernetesFulfillmentGoalScheduler.ts b/lib/core/pack/k8s/scheduler/KubernetesFulfillmentGoalScheduler.ts index c00a0b71e..c7cc34034 100644 --- a/lib/core/pack/k8s/scheduler/KubernetesFulfillmentGoalScheduler.ts +++ b/lib/core/pack/k8s/scheduler/KubernetesFulfillmentGoalScheduler.ts @@ -37,7 +37,7 @@ export interface KubernetesFulfillmentOptions { export function defaultKubernetesFulfillmentOptions(): KubernetesFulfillmentOptions { return { registration: "@atomist/k8s-sdm-skill", - name: require("../../../goal/container/k8s").K8sContainerFulfillerName, + name: require("../container").K8sContainerFulfillerName, }; } diff --git a/test/core/goal/container/container.test.ts b/test/core/goal/container/container.test.ts new file mode 100644 index 000000000..4ea1f44ef --- /dev/null +++ b/test/core/goal/container/container.test.ts @@ -0,0 +1,243 @@ +/* + * Copyright © 2020 Atomist, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as assert from "power-assert"; +import { + Container, +} from "../../../../lib/core/goal/container/container"; +import * as util from "../../../../lib/core/goal/container/util"; +import { KubernetesFulfillmentGoalScheduler } from "../../../../lib/core/pack/k8s/scheduler/KubernetesFulfillmentGoalScheduler"; +import { KubernetesGoalScheduler } from "../../../../lib/core/pack/k8s/scheduler/KubernetesGoalScheduler"; + +describe("core/goal/container/container", () => { + + describe("Container.register", () => { + + let origRunningInK8s: any; + let origRunningAsGoogleCloudFunction: any; + let origAtomistIsolatedGoal: string | undefined; + let origAtomistGoalScheduler: string | undefined; + before(() => { + origRunningInK8s = Object.getOwnPropertyDescriptor(util, "runningInK8s"); + origRunningAsGoogleCloudFunction = Object.getOwnPropertyDescriptor(util, "runningAsGoogleCloudFunction"); + if (process.env.ATOMIST_ISOLATED_GOAL) { + origAtomistIsolatedGoal = process.env.ATOMIST_ISOLATED_GOAL; + delete process.env.ATOMIST_ISOLATED_GOAL; + } + if (process.env.ATOMIST_GOAL_SCHEDULER) { + origAtomistGoalScheduler = process.env.ATOMIST_GOAL_SCHEDULER; + } + process.env.ATOMIST_GOAL_SCHEDULER = "kubernetes"; + }); + afterEach(() => { + if (origRunningInK8s) { + Object.defineProperty(util, "runningInK8s", origRunningInK8s); + } + if (origRunningAsGoogleCloudFunction) { + Object.defineProperty(util, "runningAsGoogleCloudFunction", origRunningAsGoogleCloudFunction); + } + }); + after(() => { + if (origAtomistIsolatedGoal) { + process.env.ATOMIST_ISOLATED_GOAL = origAtomistIsolatedGoal; + } + if (origAtomistGoalScheduler) { + process.env.ATOMIST_GOAL_SCHEDULER = origAtomistGoalScheduler; + } else { + delete process.env.ATOMIST_GOAL_SCHEDULER; + } + }); + + it("should register", () => { + Object.defineProperty(util, "runningInK8s", { value: () => false }); + Object.defineProperty(util, "runningAsGoogleCloudFunction", { value: () => false }); + const c = new Container(); + const s: any = { + configuration: { + sdm: {}, + }, + }; + c.register(s); + assert(!s.configuration.sdm.goalScheduler); + }); + + it("should register in k8s", () => { + Object.defineProperty(util, "runningInK8s", { value: () => true }); + Object.defineProperty(util, "runningAsGoogleCloudFunction", { value: () => false }); + const c = new Container(); + const s: any = { + configuration: { + sdm: {}, + }, + }; + c.register(s); + assert(s.configuration.sdm.goalScheduler); + assert(Array.isArray(s.configuration.sdm.goalScheduler)); + assert(s.configuration.sdm.goalScheduler.length === 1); + assert(s.configuration.sdm.goalScheduler[0] instanceof KubernetesGoalScheduler); + }); + + it("should register in GCF", () => { + Object.defineProperty(util, "runningInK8s", { value: () => false }); + Object.defineProperty(util, "runningAsGoogleCloudFunction", { value: () => true }); + const c = new Container(); + const s: any = { + configuration: { + sdm: {}, + }, + }; + c.register(s); + assert(s.configuration.sdm.goalScheduler); + assert(Array.isArray(s.configuration.sdm.goalScheduler)); + assert(s.configuration.sdm.goalScheduler.length === 1); + assert(s.configuration.sdm.goalScheduler[0] instanceof KubernetesFulfillmentGoalScheduler); + }); + + it("should not add another scheduler in k8s", () => { + Object.defineProperty(util, "runningInK8s", { value: () => true }); + Object.defineProperty(util, "runningAsGoogleCloudFunction", { value: () => false }); + const c = new Container(); + const s: any = { + configuration: { + sdm: { + goalScheduler: [new KubernetesGoalScheduler()], + }, + }, + }; + c.register(s); + assert(s.configuration.sdm.goalScheduler); + assert(Array.isArray(s.configuration.sdm.goalScheduler)); + assert(s.configuration.sdm.goalScheduler.length === 1); + assert(s.configuration.sdm.goalScheduler[0] instanceof KubernetesGoalScheduler); + }); + + it("should add fulfillment scheduler in GCF", () => { + Object.defineProperty(util, "runningInK8s", { value: () => false }); + Object.defineProperty(util, "runningAsGoogleCloudFunction", { value: () => true }); + const c = new Container(); + const s: any = { + configuration: { + sdm: { + goalScheduler: [new KubernetesGoalScheduler()], + }, + }, + }; + c.register(s); + assert(s.configuration.sdm.goalScheduler); + assert(Array.isArray(s.configuration.sdm.goalScheduler)); + assert(s.configuration.sdm.goalScheduler.length === 2); + assert(s.configuration.sdm.goalScheduler[0] instanceof KubernetesGoalScheduler); + assert(s.configuration.sdm.goalScheduler[1] instanceof KubernetesFulfillmentGoalScheduler); + }); + + }); + + describe("Container.with", () => { + + let origRunningInK8s: any; + let origRunningAsGoogleCloudFunction: any; + before(() => { + origRunningInK8s = Object.getOwnPropertyDescriptor(util, "runningInK8s"); + origRunningAsGoogleCloudFunction = Object.getOwnPropertyDescriptor(util, "runningAsGoogleCloudFunction"); + }); + afterEach(() => { + if (origRunningInK8s) { + Object.defineProperty(util, "runningInK8s", origRunningInK8s); + } + if (origRunningAsGoogleCloudFunction) { + Object.defineProperty(util, "runningAsGoogleCloudFunction", origRunningAsGoogleCloudFunction); + } + }); + + it("should with", () => { + Object.defineProperty(util, "runningInK8s", { value: () => false }); + Object.defineProperty(util, "runningAsGoogleCloudFunction", { value: () => false }); + const c = new Container(); + const r = { + containers: [], + name: "container-goal", + }; + c.with(r); + assert(c.details.scheduler); + assert(c.fulfillments); + assert(Array.isArray(c.fulfillments)); + assert(c.fulfillments.length === 1); + }); + + it("should with using provided scheduler", () => { + Object.defineProperty(util, "runningInK8s", { value: () => true }); + Object.defineProperty(util, "runningAsGoogleCloudFunction", { value: () => true }); + let scheduled = false; + const c = new Container({ scheduler: () => { scheduled = true; } }); + const r = { + containers: [], + name: "container-goal", + }; + c.with(r); + assert(scheduled, "provided scheduler not used"); + }); + + it("should with in k8s", () => { + Object.defineProperty(util, "runningInK8s", { value: () => true }); + Object.defineProperty(util, "runningAsGoogleCloudFunction", { value: () => false }); + const c = new Container(); + const r = { + containers: [], + name: "container-goal", + }; + c.with(r); + assert(c.details.scheduler); + assert(c.fulfillments); + assert(Array.isArray(c.fulfillments)); + assert(c.fulfillments.length === 1); + }); + + it("should with in GCF", () => { + Object.defineProperty(util, "runningInK8s", { value: () => false }); + Object.defineProperty(util, "runningAsGoogleCloudFunction", { value: () => true }); + const c = new Container(); + const r = { + containers: [], + name: "container-goal", + }; + c.with(r); + assert(c.details.scheduler); + assert(c.fulfillments); + assert(Array.isArray(c.fulfillments)); + assert(c.fulfillments.length === 1); + }); + + it("should add cach project listeners", () => { + Object.defineProperty(util, "runningInK8s", { value: () => false }); + Object.defineProperty(util, "runningAsGoogleCloudFunction", { value: () => false }); + const c = new Container({ scheduler: () => { } }); + const r = { + containers: [], + input: [{ classifier: "in" }], + name: "container-goal", + output: [{ classifier: "out", pattern: { directory: "." } }], + }; + c.with(r); + assert(c.projectListeners); + assert(Array.isArray(c.projectListeners)); + assert(c.projectListeners.length === 2); + assert(c.projectListeners.some(l => l.name === "restoring inputs")); + assert(c.projectListeners.some(l => l.name === "caching outputs")); + }); + + }); + +}); diff --git a/test/core/goal/container/k8s.test.ts b/test/core/pack/k8s/container.test.ts similarity index 99% rename from test/core/goal/container/k8s.test.ts rename to test/core/pack/k8s/container.test.ts index 298b82c6c..4e044cac3 100644 --- a/test/core/goal/container/k8s.test.ts +++ b/test/core/pack/k8s/container.test.ts @@ -43,11 +43,11 @@ import { executeK8sJob, K8sContainerRegistration, k8sFulfillmentCallback, -} from "../../../../lib/core/goal/container/k8s"; +} from "../../../../lib/core/pack/k8s/container"; import { loadKubeConfig } from "../../../../lib/core/pack/k8s/kubernetes/config"; import { KubernetesGoalScheduler } from "../../../../lib/core/pack/k8s/scheduler/KubernetesGoalScheduler"; import { SdmGoalState } from "../../../../lib/typings/types"; -import { containerTestImage } from "./util"; +import { containerTestImage } from "../../goal/container/util"; /* tslint:disable:max-file-line-count */ diff --git a/test/core/pack/k8s/scheduler/KubernetesFulfillmentGoalScheduler.test.ts b/test/core/pack/k8s/scheduler/KubernetesFulfillmentGoalScheduler.test.ts index 172855239..323b19ada 100644 --- a/test/core/pack/k8s/scheduler/KubernetesFulfillmentGoalScheduler.test.ts +++ b/test/core/pack/k8s/scheduler/KubernetesFulfillmentGoalScheduler.test.ts @@ -20,7 +20,7 @@ import { goal } from "../../../../../lib/api/goal/GoalWithFulfillment"; import { SdmGoalEvent } from "../../../../../lib/api/goal/SdmGoalEvent"; import { SdmGoalFulfillmentMethod } from "../../../../../lib/api/goal/SdmGoalMessage"; import { container } from "../../../../../lib/core/goal/container/container"; -import { K8sContainerFulfillerName } from "../../../../../lib/core/goal/container/k8s"; +import { K8sContainerFulfillerName } from "../../../../../lib/core/pack/k8s/container"; import { KubernetesFulfillmentGoalScheduler } from "../../../../../lib/core/pack/k8s/scheduler/KubernetesFulfillmentGoalScheduler"; import { SdmGoalState } from "../../../../../lib/typings/types";