Skip to content

Commit

Permalink
feat(k8s): allow setting annotations and labels on project namespace
Browse files Browse the repository at this point in the history
You can now configure both annotations and labels on namespaces. The
`namespace` field on the `kubernetes` provider now also accepts an
object instead of just a string, so you can specify the desired
annotations and/or labels there.

These will be applied when creating namespaces, and Garden will also
attempt to ensure those annotations and labels are on existing
namespaces (emitting a warning if e.g. not allowed to modify the
namespace).
  • Loading branch information
edvald authored and thsig committed Feb 22, 2021
1 parent 6247cef commit 6f24bee
Show file tree
Hide file tree
Showing 38 changed files with 1,513 additions and 835 deletions.
5 changes: 4 additions & 1 deletion cli/src/generate-docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { Logger } from "@garden-io/core/build/src/logger/logger"
import { LogLevel } from "@garden-io/core/build/src/logger/log-node"
import { GARDEN_CLI_ROOT } from "@garden-io/core/build/src/constants"
import { getBundledPlugins } from "./cli"
import { getSupportedPlugins } from "@garden-io/core/build/src/plugins/plugins"

require("source-map-support").install()

Expand All @@ -24,7 +25,9 @@ try {
})
} catch (_) {}

generateDocs(resolve(GARDEN_CLI_ROOT, "..", "docs"), getBundledPlugins())
const plugins = [...getBundledPlugins(), ...getSupportedPlugins()]

generateDocs(resolve(GARDEN_CLI_ROOT, "..", "docs"), plugins)
.then(() => {
// tslint:disable-next-line: no-console
console.log("Done!")
Expand Down
64 changes: 50 additions & 14 deletions core/src/docs/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,69 @@ import { padEnd, max } from "lodash"
import { DOCS_BASE_URL } from "../constants"
import { getPackageVersion } from "../util/util"

export interface NormalizedSchemaDescription {
export abstract class BaseKeyDescription<T = any> {
type: string
name: string
allowedValuesOnly: boolean
allowedValues?: string
defaultValue?: string
deprecated: boolean
description?: string
experimental: boolean
formattedExample?: string
formattedName: string
formattedType: string
fullKey: string
hasChildren: boolean
internal: boolean
level: number
parent?: NormalizedSchemaDescription
required: boolean
example?: T

constructor(public name: string | undefined, public level: number, public parent?: BaseKeyDescription) {
this.name = name
this.level = level
this.parent = parent
}

abstract getChildren(renderPatternKeys?: boolean): BaseKeyDescription[]
abstract getDefaultValue(): T | undefined
abstract formatExample(): string | undefined
abstract formatAllowedValues(): string | undefined

formatName() {
return this.name
}

formatType() {
return this.type
}

hasChildren(renderPatternKeys = false) {
return this.getChildren(renderPatternKeys).length > 0
}

fullKey() {
const formattedName = this.formatName()
const parentKey = this.parent?.fullKey()

if (parentKey && formattedName) {
return `${parentKey}.${this.formatName()}`
} else {
return parentKey || formattedName || ""
}
}
}

export interface NormalizeOptions {
level?: number
name?: string
parent?: NormalizedSchemaDescription
renderPatternKeys?: boolean
}

// Maps a schema description into an array of descriptions and normalizes each entry.
// Filters out internal descriptions.
export function flattenSchema(
schemaDescription: BaseKeyDescription,
opts: NormalizeOptions = {}
): BaseKeyDescription[] {
const { renderPatternKeys = false } = opts

const childDescriptions = schemaDescription.getChildren(renderPatternKeys).flatMap((c) => flattenSchema(c, opts))

const items = schemaDescription.name ? [schemaDescription, ...childDescriptions] : childDescriptions
return items.filter((key) => !key.internal)
}

export function indent(lines: string[], level: number) {
const prefix = padEnd("", level * 2, " ")
return lines.map((line) => prefix + line)
Expand Down
87 changes: 50 additions & 37 deletions core/src/docs/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ import {
indent,
renderMarkdownTable,
convertMarkdownLinks,
NormalizedSchemaDescription,
BaseKeyDescription,
NormalizeOptions,
flattenSchema,
} from "./common"
import { normalizeJoiSchemaDescription, JoiDescription } from "./joi-schema"
import { JoiDescription, JoiKeyDescription } from "./joi-schema"
import { safeDumpYaml } from "../util/util"

export const TEMPLATES_DIR = resolve(STATIC_DIR, "docs", "templates")
Expand All @@ -38,43 +39,47 @@ export function sanitizeYamlStringForGitBook(yamlStr: string) {
}

function getParentDescriptions(
schemaDescription: NormalizedSchemaDescription,
schemaDescriptions: NormalizedSchemaDescription[] = []
): NormalizedSchemaDescription[] {
schemaDescription: BaseKeyDescription,
schemaDescriptions: BaseKeyDescription[] = []
): BaseKeyDescription[] {
if (schemaDescription.parent) {
return getParentDescriptions(schemaDescription.parent, [schemaDescription.parent, ...schemaDescriptions])
}
return schemaDescriptions
}

export function renderMarkdownLink(description: NormalizedSchemaDescription) {
const path = description.fullKey
export function renderMarkdownLink(description: BaseKeyDescription) {
const path = description
.fullKey()
.replace(/\s+/g, "-") // Replace " " with "-""
.replace(/[\.\[\]\<\>]/g, "") // Replace ".", "[]" and "<>" with ""
.toLowerCase()
return `[${description.name}](#${path})`
}

function makeMarkdownDescription(description: NormalizedSchemaDescription, { showRequiredColumn = true } = {}) {
const { formattedType, required, allowedValues, defaultValue, fullKey } = description
function makeMarkdownDescription(description: BaseKeyDescription, { showRequiredColumn = true } = {}) {
const { required } = description

const parentDescriptions = getParentDescriptions(description)
const breadCrumbs =
parentDescriptions.length > 0
? parentDescriptions.map(renderMarkdownLink).concat(description.name).join(" > ")
? parentDescriptions.map(renderMarkdownLink).concat(description.name!).join(" > ")
: null

let formattedExample: string | undefined
if (description.formattedExample) {
if (description.formatExample()) {
formattedExample = renderSchemaDescriptionYaml([...parentDescriptions, description], {
renderFullDescription: false,
renderValue: "example",
renderEllipsisBetweenKeys: true,
}).replace(/\n$/, "") // strip trailing new line
}

const defaultValue = description.getDefaultValue()
const allowedValues = description.formatAllowedValues()

const tableData: any = {
Type: "`" + formattedType + "`",
Type: "`" + description.formatType() + "`",
...(allowedValues ? { "Allowed Values": allowedValues } : {}),
...(defaultValue !== undefined ? { Default: "`" + JSON.stringify(defaultValue) + "`" } : {}),
}
Expand All @@ -90,7 +95,7 @@ function makeMarkdownDescription(description: NormalizedSchemaDescription, { sho
breadCrumbs,
experimentalFeature: description.experimental,
formattedExample,
title: fullKey,
title: description.fullKey(),
table,
}
}
Expand All @@ -111,7 +116,7 @@ interface RenderYamlOpts {
}

export function renderSchemaDescriptionYaml(
schemaDescriptions: NormalizedSchemaDescription[],
schemaDescriptions: BaseKeyDescription[],
{
commentOutEmpty = false,
filterMarkdown = false,
Expand All @@ -123,28 +128,16 @@ export function renderSchemaDescriptionYaml(
renderValue = "default",
}: RenderYamlOpts
) {
let prevDesc: NormalizedSchemaDescription
let prevDesc: BaseKeyDescription

// Skip all deprecated fields
schemaDescriptions = schemaDescriptions.filter((desc) => !desc.deprecated)

// This is a little hacky, but works for our purposes
const getPresetValue = (desc: NormalizedSchemaDescription) => get(presetValues, desc.fullKey.replace(/\[\]/g, "[0]"))
const getPresetValue = (desc: BaseKeyDescription) => get(presetValues, desc.fullKey().replace(/\[\]/g, "[0]"))

const output = schemaDescriptions.map((desc) => {
let {
description,
formattedExample: example,
formattedType,
hasChildren,
allowedValues,
required,
name,
level,
defaultValue,
type,
parent,
} = desc
let { description, required, name, level, type, parent } = desc
const indentSpaces = level * 2
const width = maxWidth - indentSpaces - 2
const comment: string[] = []
Expand All @@ -158,8 +151,11 @@ export function renderSchemaDescriptionYaml(

let value: string | string[] | undefined
let usingExampleForValue = false
const defaultValue = desc.getDefaultValue()
const renderedDefault = isFunction(defaultValue) ? defaultValue() : defaultValue

const example = desc.formatExample()

if (presetValue) {
// Prefer preset value if given
value = presetValue
Expand Down Expand Up @@ -195,7 +191,7 @@ export function renderSchemaDescriptionYaml(
}

// Print "..." between keys. Only used when rendering markdown for examples.
if (renderEllipsisBetweenKeys && parent && parent.hasChildren && !isArrayItem) {
if (renderEllipsisBetweenKeys && parent && parent.hasChildren() && !isArrayItem) {
out.push("...")
}

Expand All @@ -210,7 +206,7 @@ export function renderSchemaDescriptionYaml(
// Print the description, type, examples, etc
} else if (renderFullDescription) {
description && comment.push(description, "")
comment.push(`Type: ${formattedType}`, "")
comment.push(`Type: ${desc.formatType()}`, "")
if (example && !usingExampleForValue) {
if (isPrimitive) {
// Render example inline
Expand All @@ -221,6 +217,8 @@ export function renderSchemaDescriptionYaml(
}
}
renderRequired && comment.push(required ? "Required." : "Optional.")

const allowedValues = desc.formatAllowedValues()
allowedValues && comment.push(`Allowed values: ${allowedValues}`, "")
}

Expand All @@ -234,14 +232,16 @@ export function renderSchemaDescriptionYaml(
}

// Render key name and value
const children = desc.getChildren()
const formattedName = name
const stringifiedValue = JSON.stringify(value)
const exceptionallyTreatAsPrimitive = !hasChildren && (stringifiedValue === "[]" || stringifiedValue === "{}")
const exceptionallyTreatAsPrimitive =
(!children.length || children[0].type !== "object") && (stringifiedValue === "[]" || stringifiedValue === "{}")

let formattedValue: string | string[]

if (example && usingExampleForValue) {
const levels = type === "object" ? 2 : 1
const levels = desc.type === "object" ? 2 : 1
formattedValue = isPrimitive || exceptionallyTreatAsPrimitive ? example : indent(example.split("\n"), levels)
} else {
// Non-primitive values get rendered in the line below, indented by one
Expand All @@ -259,7 +259,7 @@ export function renderSchemaDescriptionYaml(
if (isPrimitive || exceptionallyTreatAsPrimitive) {
// For primitives we render the value or example inline
keyAndValue.push(`${formattedName}: ${formattedValue}`)
} else if (!hasChildren || (example && usingExampleForValue)) {
} else if (!children.length || (example && usingExampleForValue)) {
// For arrays or objects without children, or non-primitive examples, we render the value in the line below
keyAndValue.push(`${formattedName}:`, ...formattedValue)
} else {
Expand Down Expand Up @@ -331,7 +331,13 @@ export function renderConfigReference(
configSchema: Joi.ObjectSchema,
{ normalizeOpts = {}, yamlOpts = {} }: RenderConfigOpts = {}
) {
const normalizedDescriptions = normalizeJoiSchemaDescription(configSchema.describe() as JoiDescription, normalizeOpts)
const joiDescription = configSchema.describe() as JoiDescription
const desc = new JoiKeyDescription({
joiDescription,
name: undefined,
level: 0,
})
const normalizedDescriptions = flattenSchema(desc, normalizeOpts)

const yaml = renderSchemaDescriptionYaml(normalizedDescriptions, { renderBasicDescription: true, ...yamlOpts })
const keys = normalizedDescriptions.map((d) => makeMarkdownDescription(d))
Expand All @@ -354,7 +360,14 @@ export function renderTemplateStringReference({
placeholder?: string
exampleName?: string
}): string {
const normalizedSchemaDescriptions = normalizeJoiSchemaDescription(schema.describe() as JoiDescription, {
const joiDescription = schema.describe() as JoiDescription
const desc = new JoiKeyDescription({
joiDescription,
name: undefined,
level: 0,
})

const normalizedSchemaDescriptions = flattenSchema(desc, {
renderPatternKeys: true,
})

Expand All @@ -372,7 +385,7 @@ export function renderTemplateStringReference({
d.title = `${prefix}.${d.title}`
}

if (d.type === "object") {
if (d.type === "object" || d.type === "customObject") {
d.title += ".*"
d.formattedExample = ""
} else if (d.formattedExample) {
Expand Down
Loading

0 comments on commit 6f24bee

Please sign in to comment.