Skip to content

Commit

Permalink
fix(k8s): fix issues with helm 2to3 migration
Browse files Browse the repository at this point in the history
The 2to3 plugin doesn't support multiple files in KUBECONFIG, and does
not support/respect the --kube-context parameter at all. I'm sure for
really good reasons because this is not important at all. I mean, what
could possibly go wrong when users think they're interacting with one
cluster but accidentally screw up another one?

/rant
  • Loading branch information
edvald authored and eysi09 committed Dec 20, 2019
1 parent 04ffbbe commit 4b3629b
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 18 deletions.
19 changes: 12 additions & 7 deletions garden-service/src/plugins/kubernetes/helm/helm-cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,24 +48,24 @@ const helm3 = new BinaryCmd({
name: "helm3",
specs: {
darwin: {
url: "https://get.helm.sh/helm-v3.0.1-darwin-amd64.tar.gz",
sha256: "4bffac2b5710fe80d2987efbc046a25968dbf3fb981c499e82fc21fe6178d2f3",
url: "https://get.helm.sh/helm-v3.0.2-darwin-amd64.tar.gz",
sha256: "05c7748da0ea8d5f85576491cd3c615f94063f20986fd82a0f5658ddc286cdb1",
extract: {
format: "tar",
targetPath: ["darwin-amd64", "helm"],
},
},
linux: {
url: "https://get.helm.sh/helm-v3.0.1-linux-amd64.tar.gz",
sha256: "6de3337bb7683fd62f915d156cfc13c1cf73dc183bd39f2fb4644498c7595805",
url: "https://get.helm.sh/helm-v3.0.2-linux-amd64.tar.gz",
sha256: "c6b7aa7e4ffc66e8abb4be328f71d48c643cb8f398d95c74d075cfb348710e1d",
extract: {
format: "tar",
targetPath: ["linux-amd64", "helm"],
},
},
win32: {
url: "https://get.helm.sh/helm-v3.0.1-windows-amd64.zip",
sha256: "60edef2180f94884e6a985c5cf920242fcc3fe8712f2d9768187b14816ed6bd9",
url: "https://get.helm.sh/helm-v3.0.2-windows-amd64.zip",
sha256: "b76dabf4e25166ebf1db7337145b02cc986fcfcee06e195df983c39c36722f46",
extract: {
format: "zip",
targetPath: ["windows-amd64", "helm.exe"],
Expand All @@ -80,12 +80,14 @@ export async function helm({
log,
args,
version = 3,
env = {},
}: {
ctx: KubernetesPluginContext
namespace?: string
log: LogEntry
args: string[]
version?: 2 | 3
env?: { [key: string]: string }
}) {
if (!namespace) {
namespace = await getAppNamespace(ctx, log, ctx.provider)
Expand All @@ -103,7 +105,7 @@ export async function helm({
opts.push("--kubeconfig", ctx.provider.config.kubeconfig)
}

const helmHome = join(GARDEN_GLOBAL_PATH, ".helm")
const helmHome = join(GARDEN_GLOBAL_PATH, `.helm${version}`)
await mkdirp(helmHome)

const cmd = version === 2 ? helm2 : helm3
Expand All @@ -112,7 +114,10 @@ export async function helm({
log,
args: [...opts, ...args],
env: {
...process.env,
...env,
HELM_HOME: helmHome,
TILLER_NAMESPACE: namespace,
},
// Helm itself will time out pretty reliably, so we shouldn't time out early on our side.
timeout: 3600,
Expand Down
94 changes: 83 additions & 11 deletions garden-service/src/plugins/kubernetes/helm/tiller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,18 @@
import { LogEntry } from "../../../logger/log-entry"
import { KubernetesResource } from "../types"
import { helm } from "./helm-cli"
import { safeLoadAll } from "js-yaml"
import { KubeApi } from "../api"
import { safeLoadAll, safeDump } from "js-yaml"
import { KubeApi, getKubeConfig } from "../api"
import { checkResourceStatuses } from "../status/status"
import { combineStates } from "../../../types/service"
import { KubernetesPluginContext } from "../config"
import { convertDeprecatedManifestVersion } from "../util"
import Bluebird from "bluebird"
import tmp from "tmp-promise"
import { findByName, getNames } from "../../../util/util"
import { ConfigurationError } from "../../../exceptions"
import { writeFile } from "fs-extra"
import { resolve } from "path"

// DEPRECATED: remove all this in v0.12.0

Expand Down Expand Up @@ -65,7 +70,14 @@ export async function migrateToHelm3(ctx: KubernetesPluginContext, api: KubeApi,
})
} catch (err) {
// Ugh ffs...
if (!err.message.includes("plugin already exists")) {
if (err.message.includes("plugin already exists")) {
await helm({
ctx,
namespace,
log,
args: ["plugin", "update", "2to3"],
})
} else {
throw err
}
}
Expand All @@ -78,14 +90,74 @@ export async function migrateToHelm3(ctx: KubernetesPluginContext, api: KubeApi,

migrationLog.info(`-> Migrating release ${namespace}/${releaseName} from Tiller to Helm 3`)

log.debug(
await helm({
ctx,
namespace,
log,
args: ["--tiller-ns", namespace, "2to3", "convert", releaseName],
})
)
// The 2to3 plugin doesn't support multiple files in KUBECONFIG, and does not support/respect the
// --kube-context parameter at all. I'm sure for really good reasons because this is not important
// at all. (Yeah I'm pissed off because this has wasted a lot of my time, sorrynotsorry.)
// So (breathe deep) we need to extract a temporary kube config file for the occasion.
const tmpDir = await tmp.dir({ unsafeCleanup: true })

try {
const config = await getKubeConfig(log, ctx.provider)

const contextName = ctx.provider.config.context
const context = findByName(config.contexts, contextName)

if (!context) {
const contextNames = getNames(config.contexts)
throw new ConfigurationError(`Could not find context ${contextName}`, { contextName, contextNames })
}

const clusterName = context.context.cluster
const cluster = findByName(config.clusters, clusterName)

if (!cluster) {
const clusterNames = getNames(config.clusters)
throw new ConfigurationError(`Could not find cluster ${clusterName} referenced in context ${contextName}`, {
clusterName,
contextName,
clusterNames,
})
}

const userName = context.context.user
const user = findByName(config.users, userName)

if (!user) {
const userNames = getNames(config.users)
throw new ConfigurationError(`Could not find user ${userName} referenced in context ${contextName}`, {
userName,
contextName,
clusterNames: userNames,
})
}

const resolvedConfig = {
"apiVersion": "v1",
"kind": "Config",
"preferences": config.preferences || {},
"current-context": context.name,
"contexts": [context],
"clusters": [cluster],
"users": [user],
}

const configPath = resolve(tmpDir.path, "kubeconfig.json")
await writeFile(configPath, safeDump(resolvedConfig))

log.debug(
await helm({
ctx,
namespace,
log,
args: ["2to3", "convert", releaseName, "--tiller-ns", namespace],
env: {
KUBECONFIG: configPath,
},
})
)
} finally {
await tmpDir.cleanup()
}
}

// Remove Tiller
Expand Down
1 change: 1 addition & 0 deletions garden-service/src/util/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,7 @@ export async function loadYamlFile(path: string): Promise<any> {

export interface ObjectWithName {
name: string
[key: string]: any
}

export function getNames<T extends ObjectWithName>(array: T[]) {
Expand Down

0 comments on commit 4b3629b

Please sign in to comment.