Skip to content

Commit

Permalink
feat(k8s): provider-level defaults for dev mode
Browse files Browse the repository at this point in the history
Excludes and permission/owner/group settings can now be set on the
provider level for `kubernetes` and `local-kubernetes`.

Defaults specified there will be extended/overridden by corresponding
settings on individual sync specs.

This helps reduce clutter when the same excludes and permission settings
are commonly applied across services/modules.
thsig committed Sep 28, 2021
1 parent a133e09 commit d6c6228
Showing 16 changed files with 770 additions and 127 deletions.
78 changes: 50 additions & 28 deletions core/src/plugins/container/config.ts
Original file line number Diff line number Diff line change
@@ -29,6 +29,7 @@ import { joiStringMap } from "../../config/common"
import { dedent, deline } from "../../util/string"
import { getModuleTypeUrl } from "../../docs/common"
import { ContainerModuleOutputs } from "./container"
import { devModeGuideLink } from "../kubernetes/dev-mode"

export const defaultContainerLimits: ServiceLimitSpec = {
cpu: 1000, // = 1000 millicpu = 1 CPU
@@ -202,13 +203,52 @@ const permissionsDocs =
const ownerDocs =
"Specify either an integer ID or a string name. See the [Mutagen docs](https://mutagen.io/documentation/synchronization/permissions#owners-and-groups) for more information."

export const syncExcludeSchema = () =>
joi
.array()
.items(joi.posixPath().allowGlobs().subPathOnly())
.description(
dedent`
Specify a list of POSIX-style paths or glob patterns that should be excluded from the sync.
\`.git\` directories and \`.garden\` directories are always ignored.
`
)
.example(["dist/**/*", "*.log"])

export const syncDefaultFileModeSchema = () =>
joi
.number()
.min(0)
.max(0o777)
.description(
"The default permission bits, specified as an octal, to set on files at the sync target. Defaults to 0600 (user read/write). " +
permissionsDocs
)

export const syncDefaultDirectoryModeSchema = () =>
joi
.number()
.min(0)
.max(0o777)
.description(
"The default permission bits, specified as an octal, to set on directories at the sync target. Defaults to 0700 (user read/write). " +
permissionsDocs
)

export const syncDefaultOwnerSchema = () =>
joi
.alternatives(joi.number().integer(), joi.string())
.description("Set the default owner of files and directories at the target. " + ownerDocs)

export const syncDefaultGroupSchema = () =>
joi
.alternatives(joi.number().integer(), joi.string())
.description("Set the default group on files and directories at the target. " + ownerDocs)

const devModeSyncSchema = () =>
hotReloadSyncSchema().keys({
exclude: joi
.array()
.items(joi.posixPath().allowGlobs().subPathOnly())
.description(`Specify a list of POSIX-style paths or glob patterns that should be excluded from the sync.`)
.example(["dist/**/*", "*.log"]),
exclude: syncExcludeSchema(),
mode: joi
.string()
.allow("one-way", "one-way-replica", "two-way")
@@ -217,28 +257,10 @@ const devModeSyncSchema = () =>
.description(
"The sync mode to use for the given paths. Allowed options: `one-way`, `one-way-replica`, `two-way`."
),
defaultFileMode: joi
.number()
.min(0)
.max(0o777)
.description(
"The default permission bits, specified as an octal, to set on files at the sync target. Defaults to 0600 (user read/write). " +
permissionsDocs
),
defaultDirectoryMode: joi
.number()
.min(0)
.max(0o777)
.description(
"The default permission bits, specified as an octal, to set on directories at the sync target. Defaults to 0700 (user read/write). " +
permissionsDocs
),
defaultOwner: joi
.alternatives(joi.number().integer(), joi.string())
.description("Set the default owner of files and directories at the target. " + ownerDocs),
defaultGroup: joi
.alternatives(joi.number().integer(), joi.string())
.description("Set the default group on files and directories at the target. " + ownerDocs),
defaultFileMode: syncDefaultFileModeSchema(),
defaultDirectoryMode: syncDefaultDirectoryModeSchema(),
defaultOwner: syncDefaultOwnerSchema(),
defaultGroup: syncDefaultGroupSchema(),
})

export interface ContainerDevModeSpec {
@@ -263,7 +285,7 @@ export const containerDevModeSchema = () =>
Dev mode is enabled when running the \`garden dev\` command, and by setting the \`--dev\` flag on the \`garden deploy\` command.
See the [Code Synchronization guide](https://docs.garden.io/guides/code-synchronization-dev-mode) for more information.
See the [Code Synchronization guide](${devModeGuideLink}) for more information.
`)

export type ContainerServiceConfig = ServiceConfig<ContainerServiceSpec>
20 changes: 16 additions & 4 deletions core/src/plugins/kubernetes/config.ts
Original file line number Diff line number Diff line change
@@ -37,6 +37,7 @@ import { baseTestSpecSchema, BaseTestSpec } from "../../config/test"
import { ArtifactSpec } from "../../config/validation"
import { V1Toleration } from "@kubernetes/client-node"
import { runPodSpecIncludeFields } from "./run"
import { KubernetesDevModeDefaults, kubernetesDevModeDefaultsSchema } from "./dev-mode"

export const DEFAULT_KANIKO_IMAGE = "gcr.io/kaniko-project/executor:v1.6.0-debug"
export interface ProviderSecretRef {
@@ -126,6 +127,9 @@ export interface KubernetesConfig extends BaseProviderConfig {
defaultHostname?: string
deploymentRegistry?: ContainerRegistryConfig
deploymentStrategy?: DeploymentStrategy
devMode?: {
defaults?: KubernetesDevModeDefaults
}
forceSsl: boolean
imagePullSecrets: ProviderSecretRef[]
ingressHttpPort: number
@@ -437,14 +441,22 @@ export const kubernetesConfigBase = () =>
.allow("rolling", "blue-green")
.description(
dedent`
Defines the strategy for deploying the project services.
Default is "rolling update" and there is experimental support for "blue/green" deployment.
The feature only supports modules of type \`container\`: other types will just deploy using the default strategy.
`
Sets the deployment strategy for \`container\` services.
The default is \`"rolling"\`, which performs rolling updates. There is also experimental support for blue/green deployments (via the \`"blue-green"\` strategy).
Note that this setting only applies to \`container\` services (and not, for example, \`kubernetes\` or \`helm\` services).
`
)
.meta({
experimental: true,
}),
devMode: joi
.object()
.keys({
defaults: kubernetesDevModeDefaultsSchema(),
})
.description("Configuration options for dev mode."),
forceSsl: joi
.boolean()
.default(false)
101 changes: 88 additions & 13 deletions core/src/plugins/kubernetes/dev-mode.ts
Original file line number Diff line number Diff line change
@@ -6,7 +6,16 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

import { containerDevModeSchema, ContainerDevModeSpec } from "../container/config"
import {
containerDevModeSchema,
ContainerDevModeSpec,
DevModeSyncSpec,
syncDefaultDirectoryModeSchema,
syncDefaultFileModeSchema,
syncDefaultGroupSchema,
syncDefaultOwnerSchema,
syncExcludeSchema,
} from "../container/config"
import { dedent, gardenAnnotationKey } from "../../util/string"
import { set } from "lodash"
import { getResourceContainer, getResourcePodSpec } from "./util"
@@ -16,12 +25,22 @@ import { joinWithPosix } from "../../util/fs"
import chalk from "chalk"
import { PluginContext } from "../../plugin-context"
import { ConfigurationError } from "../../exceptions"
import { ensureMutagenSync, getKubectlExecDestination, mutagenAgentPath, mutagenConfigLock } from "./mutagen"
import { joiIdentifier } from "../../config/common"
import { KubernetesPluginContext } from "./config"
import {
ensureMutagenSync,
getKubectlExecDestination,
mutagenAgentPath,
mutagenConfigLock,
SyncConfig,
} from "./mutagen"
import { joi, joiIdentifier } from "../../config/common"
import { KubernetesPluginContext, KubernetesProvider } from "./config"

const syncUtilImageName = "gardendev/k8s-sync:0.1.1"

export const builtInExcludes = ["**/*.git", "**/*.garden"]

export const devModeGuideLink = "https://docs.garden.io/guides/code-synchronization-dev-mode"

interface ConfigureDevModeParams {
target: HotReloadableResource
spec: ContainerDevModeSpec
@@ -32,6 +51,40 @@ export interface KubernetesDevModeSpec extends ContainerDevModeSpec {
containerName?: string
}

export interface KubernetesDevModeDefaults {
exclude?: string[]
fileMode?: number
directoryMode?: number
owner?: number | string
group?: number | string
}

/**
* Provider-level dev mode settings for the local and remote k8s providers.
*/
export const kubernetesDevModeDefaultsSchema = () =>
joi.object().keys({
exclude: syncExcludeSchema().description(dedent`
Specify a list of POSIX-style paths or glob patterns that should be excluded from the sync.
Any exclusion patterns defined in individual dev mode sync specs will be applied in addition to these patterns.
\`.git\` directories and \`.garden\` directories are always ignored.
`),
fileMode: syncDefaultFileModeSchema(),
directoryMode: syncDefaultDirectoryModeSchema(),
owner: syncDefaultOwnerSchema(),
group: syncDefaultGroupSchema(),
}).description(dedent`
Specifies default settings for dev mode syncs (e.g. for \`container\`, \`kubernetes\` and \`helm\` services).
These are overridden/extended by the settings of any individual dev mode sync specs for a given module or service.
Dev mode is enabled when running the \`garden dev\` command, and by setting the \`--dev\` flag on the \`garden deploy\` command.
See the [Code Synchronization guide](${devModeGuideLink}) for more information.
`)

export const kubernetesDevModeSchema = () =>
containerDevModeSchema().keys({
containerName: joiIdentifier().description(
@@ -44,7 +97,7 @@ export const kubernetesDevModeSchema = () =>
Dev mode is enabled when running the \`garden dev\` command, and by setting the \`--dev\` flag on the \`garden deploy\` command.
See the [Code Synchronization guide](https://docs.garden.io/guides/code-synchronization-dev-mode) for more information.
See the [Code Synchronization guide](${devModeGuideLink}) for more information.
`)

/**
@@ -152,14 +205,16 @@ export async function startDevModeSync({
}

const k8sCtx = <KubernetesPluginContext>ctx
const k8sProvider = <KubernetesProvider>k8sCtx.provider
const defaults = k8sProvider.config.devMode?.defaults || {}

let i = 0

for (const s of spec.sync) {
const key = `${keyBase}-${i}`

const alpha = joinWithPosix(moduleRoot, s.source)
const beta = await getKubectlExecDestination({
const localPath = joinWithPosix(moduleRoot, s.source)
const remoteDestination = await getKubectlExecDestination({
ctx: k8sCtx,
log,
namespace,
@@ -181,15 +236,35 @@ export async function startDevModeSync({
logSection: serviceName,
sourceDescription,
targetDescription,
config: {
alpha,
beta,
mode: s.mode,
ignore: s.exclude || [],
},
config: makeSyncConfig({ defaults, spec: s, localPath, remoteDestination }),
})

i++
}
})
}

export function makeSyncConfig({
localPath,
remoteDestination,
defaults,
spec,
}: {
localPath: string
remoteDestination: string
defaults: KubernetesDevModeDefaults | null
spec: DevModeSyncSpec
}): SyncConfig {
const s = spec
const d = defaults || {}
return {
alpha: localPath,
beta: remoteDestination,
mode: s.mode,
ignore: [...builtInExcludes, ...(d["exclude"] || []), ...(s.exclude || [])],
defaultOwner: s.defaultOwner === undefined ? d["owner"] : s.defaultOwner,
defaultGroup: s.defaultGroup === undefined ? d["group"] : s.defaultGroup,
defaultDirectoryMode: s.defaultDirectoryMode === undefined ? d["directoryMode"] : s.defaultDirectoryMode,
defaultFileMode: s.defaultFileMode === undefined ? d["fileMode"] : s.defaultFileMode,
}
}
2 changes: 1 addition & 1 deletion core/src/plugins/kubernetes/mutagen.ts
Original file line number Diff line number Diff line change
@@ -35,7 +35,7 @@ export const mutagenModeMap = {
"two-way": "two-way-safe",
}

interface SyncConfig {
export interface SyncConfig {
alpha: string
beta: string
mode: keyof typeof mutagenModeMap
2 changes: 1 addition & 1 deletion core/test/data/test-projects/container/dev-mode/garden.yml
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ services:
command: [sh, -c, "echo Server running... && nc -l -p 8080"]
devMode:
sync:
- target: /tmp
- target: /tmp/
mode: two-way
healthCheck:
command: ["echo", "ok"]
4 changes: 4 additions & 0 deletions core/test/data/test-projects/container/garden.yml
Original file line number Diff line number Diff line change
@@ -17,6 +17,10 @@ environments:
providers:
- name: local-kubernetes
environments: [local]
devMode:
defaults:
exclude:
- "**/prefix-*"
- name: local-kubernetes
deploymentRegistry: &deploymentRegistry
hostname: index.docker.io
Loading

0 comments on commit d6c6228

Please sign in to comment.