From b6368f763016a9ef37948f0090617e080729801f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ey=C3=BE=C3=B3r=20Magn=C3=BAsson?= Date: Mon, 8 Jul 2019 23:16:18 +0200 Subject: [PATCH] fix(k8s): always flatten resources of kind List --- .../src/plugins/kubernetes/helm/common.ts | 6 +- garden-service/src/plugins/kubernetes/util.ts | 15 ++ .../test/unit/src/plugins/kubernetes/util.ts | 136 +++++++++++++++++- 3 files changed, 154 insertions(+), 3 deletions(-) diff --git a/garden-service/src/plugins/kubernetes/helm/common.ts b/garden-service/src/plugins/kubernetes/helm/common.ts index bf1ea02603..152262c330 100644 --- a/garden-service/src/plugins/kubernetes/helm/common.ts +++ b/garden-service/src/plugins/kubernetes/helm/common.ts @@ -24,7 +24,7 @@ import { ConfigurationError, PluginError } from "../../../exceptions" import { Module } from "../../../types/module" import { findByName } from "../../../util/util" import { deline } from "../../../util/string" -import { getAnnotation } from "../util" +import { getAnnotation, flattenResources } from "../util" import { KubernetesPluginContext } from "../config" /** @@ -60,7 +60,7 @@ export async function getChartResources(ctx: PluginContext, module: Module, log: chartPath, )) - return objects + const resources = objects .filter(obj => { const helmHook = getAnnotation(obj, "helm.sh/hook") if (helmHook && helmHook.startsWith("test-")) { @@ -69,6 +69,8 @@ export async function getChartResources(ctx: PluginContext, module: Module, log: return true }) + + return flattenResources(resources) } /** diff --git a/garden-service/src/plugins/kubernetes/util.ts b/garden-service/src/plugins/kubernetes/util.ts index 2bf9b235c8..f51e8f8bd4 100644 --- a/garden-service/src/plugins/kubernetes/util.ts +++ b/garden-service/src/plugins/kubernetes/util.ts @@ -259,3 +259,18 @@ export async function upsertConfigMap( } } } + +/** + * Flattens an array of Kubernetes resources that contain `List` resources. + * + * If an array of resources contains a resource of kind `List`, the list items of that resource are + * flattened and included with the top-level resources. + * + * For example (simplified): + * `[{ metadata: { name: a }}, { kind: "List", items: [{ metadata: { name: b }}, { metadata: { name: c }}]}]` + * becomes + * `[{ metadata: { name: a }}, { metadata: { name: b }}, { metadata: { name: b }}]` + */ +export function flattenResources(resources: KubernetesResource[]) { + return flatten(resources.map((r: any) => r.apiVersion === "v1" && r.kind === "List" ? r.items : [r])) +} diff --git a/garden-service/test/unit/src/plugins/kubernetes/util.ts b/garden-service/test/unit/src/plugins/kubernetes/util.ts index f548735061..06378a6bc3 100644 --- a/garden-service/test/unit/src/plugins/kubernetes/util.ts +++ b/garden-service/test/unit/src/plugins/kubernetes/util.ts @@ -1,5 +1,5 @@ import { expect } from "chai" -import { millicpuToString, kilobytesToString } from "../../../../../src/plugins/kubernetes/util" +import { millicpuToString, kilobytesToString, flattenResources } from "../../../../../src/plugins/kubernetes/util" describe("millicpuToString", () => { it("should return a string suffixed with 'm'", () => { @@ -44,3 +44,137 @@ describe("kilobytesToString", () => { expect(kilobytesToString(100.5)).to.equal("100Ki") }) }) + +describe("flattenResources", () => { + it("should return resources that don't include resources of kind List as they were", () => { + const resources = [ + { + apiVersion: "v1", + kind: "ServiceAccount", + metadata: { + name: "a", + }, + }, + { + apiVersion: "v1", + kind: "ServiceAccount", + metadata: { + name: "b", + }, + }, + ] + expect(flattenResources(resources).map(r => r.metadata.name)).to.eql(["a", "b"]) + }) + it("should flatten resourcess that contain resources of kind List", () => { + const resources = [ + { + apiVersion: "v1", + items: [ + { + apiVersion: "v1", + kind: "ServiceAccount", + metadata: { + name: "a", + }, + }, + { + apiVersion: "v1", + kind: "ServiceAccount", + metadata: { + name: "b", + }, + }, + ], + kind: "List", + metadata: { + name: "foo", + }, + }, + ] + expect(flattenResources(resources).map(r => r.metadata.name)).to.eql(["a", "b"]) + }) + it("should flatten resources that contain List and non-List resources", () => { + const resources = [ + { + apiVersion: "v1", + kind: "ServiceAccount", + metadata: { + name: "a", + }, + }, + { + apiVersion: "v1", + items: [ + { + apiVersion: "v1", + kind: "ServiceAccount", + metadata: { + name: "b", + }, + }, + { + apiVersion: "v1", + kind: "ServiceAccount", + metadata: { + name: "c", + }, + }, + ], + kind: "List", + metadata: { + name: "foo", + }, + }, + { + apiVersion: "v1", + kind: "ServiceAccount", + metadata: { + name: "d", + }, + }, + ] + expect(flattenResources(resources).map(r => r.metadata.name)).to.eql(["a", "b", "c", "d"]) + }) + it("should not flatten List resources that don't have apiVersion v1", () => { + const resources = [ + { + apiVersion: "v1", + kind: "ServiceAccount", + metadata: { + name: "a", + }, + }, + { + apiVersion: "v2", + items: [ + { + apiVersion: "v1", + kind: "ServiceAccount", + metadata: { + name: "b", + }, + }, + { + apiVersion: "v1", + kind: "ServiceAccount", + metadata: { + name: "c", + }, + }, + ], + kind: "List", + metadata: { + name: "d", + }, + }, + { + apiVersion: "v2", + kind: "ServiceAccount", + metadata: { + name: "e", + }, + }, + ] + expect(flattenResources(resources).map(r => r.metadata.name)).to.eql(["a", "d", "e"]) + }) +})