Skip to content

Commit

Permalink
Consolidate k8s error message handling
Browse files Browse the repository at this point in the history
  • Loading branch information
David Dooling committed Feb 26, 2020
1 parent ab4e388 commit ba77d2b
Show file tree
Hide file tree
Showing 29 changed files with 75 additions and 107 deletions.
2 changes: 1 addition & 1 deletion 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.
Expand Down
2 changes: 1 addition & 1 deletion 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.
Expand Down
2 changes: 1 addition & 1 deletion 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.
Expand Down
@@ -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.
Expand Down
47 changes: 7 additions & 40 deletions lib/core/pack/k8s/container.ts
Expand Up @@ -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 {
Expand Down Expand Up @@ -81,6 +82,7 @@ import {
KubernetesGoalScheduler,
readNamespace,
} from "./scheduler/KubernetesGoalScheduler";
import { k8sErrMsg } from "./support/error";

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

Expand Down Expand Up @@ -189,7 +191,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<any>(newData, ContainerRegistrationGoalDataKey, spec);
Expand Down Expand Up @@ -338,7 +340,7 @@ export function k8sFulfillmentCallback(
};

// Store k8s service registration in goal data
data = JSON.parse(goalEvent.data || "{}");
data = goalData(goalEvent);
newData = {};
_.set<any>(newData, `${ServiceRegistrationGoalDataKey}.${registration.name}`, serviceSpec);
goalEvent.data = JSON.stringify(_.merge(data, newData));
Expand All @@ -355,7 +357,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}`);
Expand Down Expand Up @@ -416,7 +418,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");
}
Expand Down Expand Up @@ -559,24 +561,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
Expand All @@ -591,7 +575,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}`);
}
Expand Down Expand Up @@ -788,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";
}
}
6 changes: 3 additions & 3 deletions lib/core/pack/k8s/kubernetes/application.ts
Expand Up @@ -15,7 +15,7 @@
*/

import * as k8s from "@kubernetes/client-node";
import { errMsg } from "../support/error";
import { k8sErrMsg } from "../support/error";
import {
KubernetesClients,
makeApiClients,
Expand Down Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -170,7 +170,7 @@ export async function deleteApplication(del: KubernetesDelete): Promise<k8s.Kube
const x = await deleteAppResources({ ...rd, req });
deleted.push(...x);
} catch (e) {
e.message = `Failed to delete ${rd.kind} for ${slug}: ${errMsg(e)}`;
e.message = `Failed to delete ${rd.kind} for ${slug}: ${k8sErrMsg(e)}`;
errs.push(e);
}
}
Expand Down
6 changes: 3 additions & 3 deletions lib/core/pack/k8s/kubernetes/apply.ts
Expand Up @@ -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 {
K8sObjectApi,
Expand All @@ -43,13 +43,13 @@ export async function applySpec(spec: k8s.KubernetesObject): Promise<K8sObjectRe
const kc = loadKubeConfig();
client = kc.makeApiClient(K8sObjectApi);
} catch (e) {
e.message = `Failed to create Kubernetes client: ${errMsg(e)}`;
e.message = `Failed to create Kubernetes client: ${k8sErrMsg(e)}`;
throw e;
}
try {
await client.read(spec);
} catch (e) {
logger.debug(`Failed to read resource ${slug}: ${errMsg(e)}`);
logger.debug(`Failed to read resource ${slug}: ${k8sErrMsg(e)}`);
logger.info(`Creating resource ${slug} using '${logObject(spec)}'`);
return logRetry(() => client.create(spec), `create resource ${slug}`);
}
Expand Down
10 changes: 5 additions & 5 deletions lib/core/pack/k8s/kubernetes/delete.ts
Expand Up @@ -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,
Expand Down Expand Up @@ -46,13 +46,13 @@ export async function deleteSpec(spec: k8s.KubernetesObject): Promise<K8sDeleteR
const kc = loadKubeConfig();
client = kc.makeApiClient(K8sObjectApi);
} catch (e) {
e.message = `Failed to create Kubernetes client: ${errMsg(e)}`;
e.message = `Failed to create Kubernetes client: ${k8sErrMsg(e)}`;
throw e;
}
try {
await client.read(spec);
} catch (e) {
logger.debug(`Kubernetes resource ${slug} does not exist: ${errMsg(e)}`);
logger.debug(`Kubernetes resource ${slug} does not exist: ${k8sErrMsg(e)}`);
return undefined;
}
logger.info(`Deleting resource ${slug} using '${logObject(spec)}'`);
Expand Down Expand Up @@ -171,7 +171,7 @@ export async function deleteAppResources(arg: DeleteAppResourcesArg): Promise<k8
continu = listResp.body.metadata._continue;
} while (!!continu);
} catch (e) {
e.message = `Failed to list ${arg.kind} for ${slug}: ${errMsg(e)}`;
e.message = `Failed to list ${arg.kind} for ${slug}: ${k8sErrMsg(e)}`;
throw e;
}
const deleted: k8s.KubernetesObject[] = [];
Expand All @@ -190,7 +190,7 @@ export async function deleteAppResources(arg: DeleteAppResourcesArg): Promise<k8
}
deleted.push(resource);
} catch (e) {
e.message = `Failed to delete ${resourceSlug} for ${slug}: ${errMsg(e)}`;
e.message = `Failed to delete ${resourceSlug} for ${slug}: ${k8sErrMsg(e)}`;
errs.push(e);
}
}
Expand Down
4 changes: 2 additions & 2 deletions lib/core/pack/k8s/kubernetes/deployment.ts
Expand Up @@ -19,7 +19,7 @@ import { logger } from "@atomist/automation-client/lib/util/logger";
import * as k8s from "@kubernetes/client-node";
import * as stringify from "json-stringify-safe";
import * as _ from "lodash";
import { errMsg } from "../support/error";
import { k8sErrMsg } from "../support/error";
import { logRetry } from "../support/retry";
import {
applicationLabels,
Expand Down Expand Up @@ -49,7 +49,7 @@ export async function upsertDeployment(req: KubernetesResourceRequest): Promise<
try {
await req.clients.apps.readNamespacedDeployment(spec.metadata.name, spec.metadata.namespace);
} catch (e) {
logger.debug(`Failed to read deployment ${slug}, creating: ${errMsg(e)}`);
logger.debug(`Failed to read deployment ${slug}, creating: ${k8sErrMsg(e)}`);
logger.info(`Creating deployment ${slug} using '${logObject(spec)}'`);
await logRetry(() => req.clients.apps.createNamespacedDeployment(spec.metadata.namespace, spec),
`create deployment ${slug}`);
Expand Down
4 changes: 2 additions & 2 deletions lib/core/pack/k8s/kubernetes/fetch.ts
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
}

Expand Down
4 changes: 2 additions & 2 deletions lib/core/pack/k8s/kubernetes/ingress.ts
Expand Up @@ -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";
Expand Down Expand Up @@ -52,7 +52,7 @@ export async function upsertIngress(req: KubernetesResourceRequest): Promise<k8s
try {
await req.clients.ext.readNamespacedIngress(spec.metadata.name, spec.metadata.namespace);
} catch (e) {
logger.debug(`Failed to read ingress ${slug}, creating: ${errMsg(e)}`);
logger.debug(`Failed to read ingress ${slug}, creating: ${k8sErrMsg(e)}`);
logger.info(`Creating ingress ${slug} using '${logObject(spec)}'`);
await logRetry(() => req.clients.ext.createNamespacedIngress(spec.metadata.namespace, spec), `create ingress ${slug}`);
return spec;
Expand Down
6 changes: 3 additions & 3 deletions lib/core/pack/k8s/kubernetes/namespace.ts
Expand Up @@ -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";
Expand All @@ -41,7 +41,7 @@ export async function upsertNamespace(req: KubernetesResourceRequest): Promise<k
await req.clients.core.readNamespace(spec.metadata.name);
logger.debug(`Namespace ${slug} exists`);
} catch (e) {
logger.debug(`Failed to get namespace ${slug}, creating: ${errMsg(e)}`);
logger.debug(`Failed to get namespace ${slug}, creating: ${k8sErrMsg(e)}`);
logger.info(`Creating namespace ${slug} using '${logObject(spec)}'`);
await logRetry(() => req.clients.core.createNamespace(spec), `create namespace ${slug}`);
return spec;
Expand All @@ -51,7 +51,7 @@ export async function upsertNamespace(req: KubernetesResourceRequest): Promise<k
await logRetry(() => 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;
}
Expand Down
6 changes: 3 additions & 3 deletions lib/core/pack/k8s/kubernetes/role.ts
Expand Up @@ -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";
Expand All @@ -43,7 +43,7 @@ export async function upsertRole(req: KubernetesResourceRequest): Promise<k8s.V1
try {
await req.clients.rbac.readClusterRole(spec.metadata.name);
} catch (e) {
logger.debug(`Failed to read cluster role ${slug}, creating: ${errMsg(e)}`);
logger.debug(`Failed to read cluster role ${slug}, creating: ${k8sErrMsg(e)}`);
logger.info(`Creating cluster role ${slug} using '${logObject(spec)}'`);
await logRetry(() => req.clients.rbac.createClusterRole(spec), `create cluster role ${slug}`);
return spec;
Expand All @@ -57,7 +57,7 @@ export async function upsertRole(req: KubernetesResourceRequest): Promise<k8s.V1
try {
await req.clients.rbac.readNamespacedRole(spec.metadata.name, spec.metadata.namespace);
} catch (e) {
logger.debug(`Failed to read role ${slug}, creating: ${errMsg(e)}`);
logger.debug(`Failed to read role ${slug}, creating: ${k8sErrMsg(e)}`);
logger.info(`Creating role ${slug} using '${logObject(spec)}'`);
await logRetry(() => req.clients.rbac.createNamespacedRole(spec.metadata.namespace, spec), `create role ${slug}`);
return spec;
Expand Down
6 changes: 3 additions & 3 deletions lib/core/pack/k8s/kubernetes/roleBinding.ts
Expand Up @@ -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";
Expand All @@ -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}`);
Expand All @@ -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}`);
Expand Down
4 changes: 2 additions & 2 deletions lib/core/pack/k8s/kubernetes/secret.ts
Expand Up @@ -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";
Expand Down Expand Up @@ -55,7 +55,7 @@ export async function upsertSecrets(req: KubernetesResourceRequest): Promise<k8s
try {
await req.clients.core.readNamespacedSecret(secret.metadata.name, spec.metadata.namespace);
} catch (e) {
logger.debug(`Failed to read secret ${secretName}, creating: ${errMsg(e)}`);
logger.debug(`Failed to read secret ${secretName}, creating: ${k8sErrMsg(e)}`);
logger.info(`Creating secret ${slug} using '${logObject(spec)}'`);
await logRetry(() => req.clients.core.createNamespacedSecret(spec.metadata.namespace, spec),
`create secret ${secretName} for ${slug}`);
Expand Down

0 comments on commit ba77d2b

Please sign in to comment.