Skip to content

Commit

Permalink
Move k8s container goal implementation to pack
Browse files Browse the repository at this point in the history
Move Kubernetes container goal implementation to k8s pack.  Remove
reference to KubernetesJobDeletingGoalCompletionListenerFactory.  Add
tests for container goal methods to check failing runtime require
statements.
  • Loading branch information
David Dooling committed Feb 26, 2020
1 parent 1ed71c2 commit 2eb516b
Show file tree
Hide file tree
Showing 6 changed files with 271 additions and 24 deletions.
6 changes: 2 additions & 4 deletions lib/core/goal/container/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,8 +274,6 @@ export class Container extends FulfillableGoalWithRegistrations<ContainerRegistr
if (!goalSchedulers.some(gs => 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()) {
Expand All @@ -295,10 +293,10 @@ export class Container extends FulfillableGoalWithRegistrations<ContainerRegistr
registration.name = (registration.name || `container-${this.definition.displayName}`).replace(/\.+/g, "-");
if (!this.details.scheduler) {
if (runningInK8s()) {
const k8sContainerScheduler = require("./k8s").k8sContainerScheduler;
const k8sContainerScheduler = require("../../pack/k8s/container").k8sContainerScheduler;
this.details.scheduler = k8sContainerScheduler;
} else if (runningAsGoogleCloudFunction()) {
const k8sSkillContainerScheduler = require("./k8s").k8sSkillContainerScheduler;
const k8sSkillContainerScheduler = require("../../pack/k8s/container").k8sSkillContainerScheduler;
this.details.scheduler = k8sSkillContainerScheduler;
} else {
this.details.scheduler = dockerContainerScheduler;
Expand Down
38 changes: 22 additions & 16 deletions lib/core/goal/container/k8s.ts → lib/core/pack/k8s/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,24 +48,12 @@ import { GoalScheduler } from "../../../api/goal/support/GoalScheduler";
import { ServiceRegistrationGoalDataKey } from "../../../api/registration/ServiceRegistration";
import { ProgressLog } from "../../../spi/log/ProgressLog";
import { SdmGoalState } from "../../../typings/types";
import { loadKubeConfig } from "../../pack/k8s/kubernetes/config";
import {
k8sJobEnv,
KubernetesGoalScheduler,
readNamespace,
} from "../../pack/k8s/scheduler/KubernetesGoalScheduler";
import {
K8sServiceRegistrationType,
K8sServiceSpec,
} from "../../pack/k8s/scheduler/service";
import { k8sErrMsg } from "../../pack/k8s/support/error";
import { toArray } from "../../util/misc/array";
import {
CacheEntry,
CacheOutputGoalDataKey,
cachePut,
cacheRestore,
} from "../cache/goalCaching";
} from "../../goal/cache/goalCaching";
import {
Container,
ContainerInput,
Expand All @@ -76,13 +64,25 @@ import {
ContainerScheduler,
GoalContainer,
GoalContainerVolume,
} from "./container";
import { prepareSecrets } from "./provider";
} from "../../goal/container/container";
import { prepareSecrets } from "../../goal/container/provider";
import {
containerEnvVars,
prepareInputAndOutput,
processResult,
} from "./util";
} from "../../goal/container/util";
import { toArray } from "../../util/misc/array";
import { loadKubeConfig } from "./kubernetes/config";
import {
k8sJobEnv,
KubernetesGoalScheduler,
readNamespace,
} from "./scheduler/KubernetesGoalScheduler";
import {
K8sServiceRegistrationType,
K8sServiceSpec,
} from "./scheduler/service";
import { k8sErrMsg } from "./support/error";

// tslint:disable:max-file-line-count

Expand Down Expand Up @@ -143,6 +143,9 @@ export interface K8sContainerRegistration extends ContainerRegistration {
volumes?: K8sGoalContainerVolume[];
}

/**
* Container scheduler to use when running in Kubernetes.
*/
export const k8sContainerScheduler: ContainerScheduler = (goal, registration: K8sContainerRegistration) => {
goal.addFulfillment({
goalExecutor: executeK8sJob(),
Expand All @@ -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(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
}

Expand Down
243 changes: 243 additions & 0 deletions test/core/goal/container/container.test.ts
Original file line number Diff line number Diff line change
@@ -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"));
});

});

});
Original file line number Diff line number Diff line change
Expand Up @@ -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 */

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down

0 comments on commit 2eb516b

Please sign in to comment.