diff --git a/packages/base-ui/src/components/command-list/CommandList.module.css b/packages/base-ui/src/components/command-list/CommandList.module.css
index 5dc53ed..93ecf2c 100644
--- a/packages/base-ui/src/components/command-list/CommandList.module.css
+++ b/packages/base-ui/src/components/command-list/CommandList.module.css
@@ -45,6 +45,7 @@
* --------------------------------------------------------------------------- */
.Input {
+ padding: var(--ov-space-inline-xs, 2px);
border-bottom: 1px solid var(--ov-color-border-muted);
}
diff --git a/packages/base-ui/src/components/separator/Separator.module.css b/packages/base-ui/src/components/separator/Separator.module.css
index e971ed5..2a16dc0 100644
--- a/packages/base-ui/src/components/separator/Separator.module.css
+++ b/packages/base-ui/src/components/separator/Separator.module.css
@@ -1,5 +1,5 @@
.Root {
- --_ov-separator-color: color-mix(in srgb, var(--ov-color-fg-muted) 56%, transparent 44%);
+ --_ov-separator-color: var(--ov-color-border-default);
--_ov-separator-thickness: var(--ov-size-separator-thickness-md, 2px);
--_ov-separator-inset: var(--ov-size-separator-inset-md, 12px);
--_ov-separator-label-color: var(--ov-color-fg-muted);
diff --git a/packages/base-ui/src/theme/styles.css b/packages/base-ui/src/theme/styles.css
index 1a17114..e600edb 100644
--- a/packages/base-ui/src/theme/styles.css
+++ b/packages/base-ui/src/theme/styles.css
@@ -27,10 +27,46 @@
--ov-primitive-amber-500: #cf9030;
--ov-primitive-amber-600: #b17727;
+ --ov-primitive-red-300: #e89a9a;
--ov-primitive-red-400: #d97777;
--ov-primitive-red-500: #c45f5f;
--ov-primitive-red-600: #ad4f4f;
+ --ov-primitive-purple-300: #b89aee;
+ --ov-primitive-purple-400: #a67de8;
+ --ov-primitive-purple-500: #9363db;
+ --ov-primitive-purple-600: #7c4ec4;
+
+ --ov-primitive-cyan-300: #7ad4e8;
+ --ov-primitive-cyan-400: #5ac4db;
+ --ov-primitive-cyan-500: #3fb0c9;
+ --ov-primitive-cyan-600: #2e97ae;
+
+ --ov-primitive-blue-200: #a4bdf3;
+
+ --ov-primitive-coral-400: #ff7b72;
+ --ov-primitive-orange-300: #ffa657;
+ --ov-primitive-sky-200: #a5d6ff;
+ --ov-primitive-sky-300: #79c0ff;
+ --ov-primitive-lavender-300: #d2a8ff;
+
+ --ov-primitive-ansi-black: #3b4252;
+ --ov-primitive-ansi-red: #bf616a;
+ --ov-primitive-ansi-green: #a3be8c;
+ --ov-primitive-ansi-yellow: #ebcb8b;
+ --ov-primitive-ansi-blue: #81a1c1;
+ --ov-primitive-ansi-magenta: #b48ead;
+ --ov-primitive-ansi-cyan: #88c0d0;
+ --ov-primitive-ansi-white: #e5e9f0;
+ --ov-primitive-ansi-bright-black: #4c566a;
+ --ov-primitive-ansi-bright-red: #d08770;
+ --ov-primitive-ansi-bright-green: #a3be8c;
+ --ov-primitive-ansi-bright-yellow: #ebcb8b;
+ --ov-primitive-ansi-bright-blue: #81a1c1;
+ --ov-primitive-ansi-bright-magenta: #b48ead;
+ --ov-primitive-ansi-bright-cyan: #8fbcbb;
+ --ov-primitive-ansi-bright-white: #eceff4;
+
--ov-primitive-font-sans: 'Inter Variable', 'SF Pro Text', 'Segoe UI', -apple-system, sans-serif;
--ov-primitive-font-mono: 'JetBrains Mono Variable', 'SF Mono', 'Cascadia Mono', monospace;
--ov-primitive-font-size-11: 0.6875rem;
@@ -385,6 +421,94 @@
--ov-color-editor-group-header-bg: var(--ov-color-bg-surface-raised);
--ov-color-editor-group-drop-border: var(--ov-color-brand-400);
--ov-size-tab-height: 34px;
+
+ /* Editor tokens */
+ --ov-color-editor-bg: var(--ov-color-bg-base);
+ --ov-color-editor-fg: var(--ov-color-fg-default);
+ --ov-color-editor-cursor: var(--ov-color-accent-strong);
+ --ov-color-editor-selection-bg: var(--ov-color-accent-soft);
+ --ov-color-editor-selection-inactive-bg: rgb(161 169 182 / 0.1);
+ --ov-color-editor-line-highlight-bg: rgb(255 255 255 / 0.04);
+ --ov-color-editor-line-highlight-border: transparent;
+ --ov-color-editor-line-number: var(--ov-color-fg-subtle);
+ --ov-color-editor-line-number-active: var(--ov-color-fg-muted);
+ --ov-color-editor-whitespace: var(--ov-primitive-gray-400);
+ --ov-color-editor-indent-guide: var(--ov-primitive-gray-300);
+ --ov-color-editor-indent-guide-active: var(--ov-primitive-gray-500);
+ --ov-color-editor-ruler: var(--ov-primitive-gray-300);
+ --ov-color-editor-find-match-bg: rgb(207 144 48 / 0.35);
+ --ov-color-editor-find-match-border: var(--ov-color-warning);
+ --ov-color-editor-find-range-bg: rgb(207 144 48 / 0.15);
+ --ov-color-editor-link: var(--ov-color-brand-300);
+ --ov-color-editor-bracket-match-bg: rgb(90 127 226 / 0.2);
+ --ov-color-editor-bracket-match-border: var(--ov-color-brand-400);
+ --ov-color-editor-bracket-1: #ffd700;
+ --ov-color-editor-bracket-2: #da70d6;
+ --ov-color-editor-bracket-3: #179fff;
+ --ov-color-editor-bracket-4: #ffd700;
+ --ov-color-editor-bracket-5: #da70d6;
+ --ov-color-editor-bracket-6: #179fff;
+ --ov-color-gutter-bg: var(--ov-color-editor-bg);
+ --ov-color-gutter-added: var(--ov-color-success);
+ --ov-color-gutter-modified: var(--ov-color-info);
+ --ov-color-gutter-deleted: var(--ov-color-danger);
+ --ov-color-diff-insert-bg: var(--ov-color-success-soft);
+ --ov-color-diff-remove-bg: var(--ov-color-danger-soft);
+ --ov-color-minimap-bg: var(--ov-color-bg-surface);
+ --ov-color-minimap-selection: var(--ov-color-accent-soft);
+ --ov-color-minimap-error: var(--ov-color-danger);
+ --ov-color-minimap-warning: var(--ov-color-warning);
+ --ov-color-minimap-find-match: var(--ov-color-warning);
+
+ /* Terminal tokens */
+ --ov-color-terminal-bg: var(--ov-color-bg-inset);
+ --ov-color-terminal-fg: var(--ov-color-fg-default);
+ --ov-color-terminal-cursor: var(--ov-color-accent-strong);
+ --ov-color-terminal-selection-bg: var(--ov-color-accent-soft);
+ --ov-color-terminal-selection-fg: var(--ov-color-fg-default);
+ --ov-color-terminal-border: var(--ov-color-border-default);
+ --ov-color-ansi-black: var(--ov-primitive-ansi-black);
+ --ov-color-ansi-red: var(--ov-primitive-ansi-red);
+ --ov-color-ansi-green: var(--ov-primitive-ansi-green);
+ --ov-color-ansi-yellow: var(--ov-primitive-ansi-yellow);
+ --ov-color-ansi-blue: var(--ov-primitive-ansi-blue);
+ --ov-color-ansi-magenta: var(--ov-primitive-ansi-magenta);
+ --ov-color-ansi-cyan: var(--ov-primitive-ansi-cyan);
+ --ov-color-ansi-white: var(--ov-primitive-ansi-white);
+ --ov-color-ansi-bright-black: var(--ov-primitive-ansi-bright-black);
+ --ov-color-ansi-bright-red: var(--ov-primitive-ansi-bright-red);
+ --ov-color-ansi-bright-green: var(--ov-primitive-ansi-bright-green);
+ --ov-color-ansi-bright-yellow: var(--ov-primitive-ansi-bright-yellow);
+ --ov-color-ansi-bright-blue: var(--ov-primitive-ansi-bright-blue);
+ --ov-color-ansi-bright-magenta: var(--ov-primitive-ansi-bright-magenta);
+ --ov-color-ansi-bright-cyan: var(--ov-primitive-ansi-bright-cyan);
+ --ov-color-ansi-bright-white: var(--ov-primitive-ansi-bright-white);
+
+ /* Syntax highlighting tokens */
+ --ov-syntax-comment: #8b949e;
+ --ov-syntax-string: var(--ov-primitive-sky-200);
+ --ov-syntax-string-escape: var(--ov-primitive-sky-300);
+ --ov-syntax-number: var(--ov-primitive-sky-300);
+ --ov-syntax-keyword: var(--ov-primitive-coral-400);
+ --ov-syntax-keyword-control: var(--ov-primitive-coral-400);
+ --ov-syntax-keyword-operator: var(--ov-primitive-coral-400);
+ --ov-syntax-type: var(--ov-primitive-orange-300);
+ --ov-syntax-class: var(--ov-primitive-orange-300);
+ --ov-syntax-interface: var(--ov-primitive-orange-300);
+ --ov-syntax-function: var(--ov-primitive-lavender-300);
+ --ov-syntax-method: var(--ov-primitive-lavender-300);
+ --ov-syntax-variable: var(--ov-color-fg-default);
+ --ov-syntax-parameter: var(--ov-primitive-orange-300);
+ --ov-syntax-property: var(--ov-primitive-sky-300);
+ --ov-syntax-namespace: var(--ov-primitive-orange-300);
+ --ov-syntax-decorator: var(--ov-primitive-lavender-300);
+ --ov-syntax-regexp: var(--ov-primitive-sky-200);
+ --ov-syntax-operator: var(--ov-primitive-coral-400);
+ --ov-syntax-punctuation: #e6edf3;
+ --ov-syntax-style-comment: italic;
+ --ov-syntax-style-keyword: normal;
+ --ov-syntax-style-function: normal;
+ --ov-syntax-style-type: normal;
}
:root[data-ov-theme='light'] {
@@ -426,6 +550,71 @@
--ov-color-state-pressed: rgb(75 85 100 / 0.14);
--ov-color-state-selected: rgb(71 109 206 / 0.14);
--ov-color-state-focus-ring: #476dce;
+
+ /* Editor tokens — light */
+ --ov-color-editor-bg: var(--ov-color-bg-base);
+ --ov-color-editor-fg: var(--ov-color-fg-default);
+ --ov-color-editor-cursor: var(--ov-color-accent-strong);
+ --ov-color-editor-selection-bg: var(--ov-color-accent-soft);
+ --ov-color-editor-selection-inactive-bg: rgb(75 85 100 / 0.1);
+ --ov-color-editor-line-highlight-bg: rgb(0 0 0 / 0.04);
+ --ov-color-editor-line-highlight-border: transparent;
+ --ov-color-editor-line-number: var(--ov-color-fg-subtle);
+ --ov-color-editor-line-number-active: var(--ov-color-fg-muted);
+ --ov-color-editor-whitespace: #c8ced8;
+ --ov-color-editor-indent-guide: #d4d8de;
+ --ov-color-editor-indent-guide-active: #9ea6b3;
+ --ov-color-editor-ruler: #d4d8de;
+ --ov-color-editor-find-match-bg: rgb(148 98 33 / 0.3);
+ --ov-color-editor-find-match-border: var(--ov-color-warning);
+ --ov-color-editor-find-range-bg: rgb(148 98 33 / 0.12);
+ --ov-color-editor-link: var(--ov-color-brand-500);
+
+ /* Terminal tokens — light */
+ --ov-color-terminal-bg: var(--ov-color-bg-inset);
+ --ov-color-terminal-fg: var(--ov-color-fg-default);
+ --ov-color-terminal-cursor: var(--ov-color-accent-strong);
+ --ov-color-terminal-selection-bg: var(--ov-color-accent-soft);
+ --ov-color-terminal-selection-fg: var(--ov-color-fg-default);
+ --ov-color-terminal-border: var(--ov-color-border-default);
+ --ov-color-ansi-black: #3b4252;
+ --ov-color-ansi-red: #a3171a;
+ --ov-color-ansi-green: #2f7d4f;
+ --ov-color-ansi-yellow: #946221;
+ --ov-color-ansi-blue: #2560a8;
+ --ov-color-ansi-magenta: #7c3e8e;
+ --ov-color-ansi-cyan: #1a7d8d;
+ --ov-color-ansi-white: #4b5564;
+ --ov-color-ansi-bright-black: #6b7481;
+ --ov-color-ansi-bright-red: #c4554d;
+ --ov-color-ansi-bright-green: #2f9161;
+ --ov-color-ansi-bright-yellow: #b17727;
+ --ov-color-ansi-bright-blue: #476dce;
+ --ov-color-ansi-bright-magenta: #9363db;
+ --ov-color-ansi-bright-cyan: #2e97ae;
+ --ov-color-ansi-bright-white: #1b1d22;
+
+ /* Syntax highlighting tokens — light */
+ --ov-syntax-comment: #6a737d;
+ --ov-syntax-string: #032f62;
+ --ov-syntax-string-escape: #22863a;
+ --ov-syntax-number: #005cc5;
+ --ov-syntax-keyword: #d73a49;
+ --ov-syntax-keyword-control: #d73a49;
+ --ov-syntax-keyword-operator: #d73a49;
+ --ov-syntax-type: #e36209;
+ --ov-syntax-class: #e36209;
+ --ov-syntax-interface: #e36209;
+ --ov-syntax-function: #6f42c1;
+ --ov-syntax-method: #6f42c1;
+ --ov-syntax-variable: #1b1d22;
+ --ov-syntax-parameter: #e36209;
+ --ov-syntax-property: #005cc5;
+ --ov-syntax-namespace: #e36209;
+ --ov-syntax-decorator: #6f42c1;
+ --ov-syntax-regexp: #032f62;
+ --ov-syntax-operator: #d73a49;
+ --ov-syntax-punctuation: #24292e;
}
:root[data-ov-theme='high-contrast-dark'] {
@@ -466,6 +655,63 @@
--ov-color-state-pressed: rgb(255 255 255 / 0.24);
--ov-color-state-selected: rgb(127 179 255 / 0.34);
--ov-color-state-focus-ring: #ffffff;
+
+ /* Editor tokens — high-contrast dark */
+ --ov-color-editor-bg: #000000;
+ --ov-color-editor-fg: #ffffff;
+ --ov-color-editor-cursor: #ffffff;
+ --ov-color-editor-selection-bg: rgb(127 179 255 / 0.4);
+ --ov-color-editor-selection-inactive-bg: rgb(255 255 255 / 0.15);
+ --ov-color-editor-line-highlight-bg: rgb(255 255 255 / 0.08);
+ --ov-color-editor-line-highlight-border: #f0f0f0;
+ --ov-color-editor-line-number: #d7d7d7;
+ --ov-color-editor-line-number-active: #ffffff;
+
+ /* Terminal tokens — high-contrast dark */
+ --ov-color-terminal-bg: #000000;
+ --ov-color-terminal-fg: #ffffff;
+ --ov-color-terminal-cursor: #ffffff;
+ --ov-color-terminal-selection-bg: rgb(127 179 255 / 0.4);
+ --ov-color-terminal-selection-fg: #ffffff;
+ --ov-color-terminal-border: #f0f0f0;
+ --ov-color-ansi-black: #000000;
+ --ov-color-ansi-red: #ff9f9f;
+ --ov-color-ansi-green: #73ffa2;
+ --ov-color-ansi-yellow: #ffd27d;
+ --ov-color-ansi-blue: #a6c8ff;
+ --ov-color-ansi-magenta: #d2a8ff;
+ --ov-color-ansi-cyan: #7ad4e8;
+ --ov-color-ansi-white: #ffffff;
+ --ov-color-ansi-bright-black: #8d8d8d;
+ --ov-color-ansi-bright-red: #ffb3b3;
+ --ov-color-ansi-bright-green: #9dffbd;
+ --ov-color-ansi-bright-yellow: #ffe2a3;
+ --ov-color-ansi-bright-blue: #c8deff;
+ --ov-color-ansi-bright-magenta: #e3c8ff;
+ --ov-color-ansi-bright-cyan: #a4e5f2;
+ --ov-color-ansi-bright-white: #ffffff;
+
+ /* Syntax highlighting tokens — high-contrast dark */
+ --ov-syntax-comment: #bcbcbc;
+ --ov-syntax-string: #a5d6ff;
+ --ov-syntax-string-escape: #79c0ff;
+ --ov-syntax-number: #79c0ff;
+ --ov-syntax-keyword: #ff9f9f;
+ --ov-syntax-keyword-control: #ff9f9f;
+ --ov-syntax-keyword-operator: #ff9f9f;
+ --ov-syntax-type: #ffa657;
+ --ov-syntax-class: #ffa657;
+ --ov-syntax-interface: #ffa657;
+ --ov-syntax-function: #d2a8ff;
+ --ov-syntax-method: #d2a8ff;
+ --ov-syntax-variable: #ffffff;
+ --ov-syntax-parameter: #ffa657;
+ --ov-syntax-property: #79c0ff;
+ --ov-syntax-namespace: #ffa657;
+ --ov-syntax-decorator: #d2a8ff;
+ --ov-syntax-regexp: #a5d6ff;
+ --ov-syntax-operator: #ff9f9f;
+ --ov-syntax-punctuation: #ffffff;
}
:root[data-ov-theme='high-contrast-light'] {
@@ -506,6 +752,63 @@
--ov-color-state-pressed: rgb(0 0 0 / 0.14);
--ov-color-state-selected: rgb(0 74 215 / 0.2);
--ov-color-state-focus-ring: #000000;
+
+ /* Editor tokens — high-contrast light */
+ --ov-color-editor-bg: #ffffff;
+ --ov-color-editor-fg: #000000;
+ --ov-color-editor-cursor: #000000;
+ --ov-color-editor-selection-bg: rgb(0 74 215 / 0.25);
+ --ov-color-editor-selection-inactive-bg: rgb(0 0 0 / 0.1);
+ --ov-color-editor-line-highlight-bg: rgb(0 0 0 / 0.06);
+ --ov-color-editor-line-highlight-border: #000000;
+ --ov-color-editor-line-number: #2d2d2d;
+ --ov-color-editor-line-number-active: #000000;
+
+ /* Terminal tokens — high-contrast light */
+ --ov-color-terminal-bg: #f4f4f4;
+ --ov-color-terminal-fg: #000000;
+ --ov-color-terminal-cursor: #000000;
+ --ov-color-terminal-selection-bg: rgb(0 74 215 / 0.25);
+ --ov-color-terminal-selection-fg: #000000;
+ --ov-color-terminal-border: #000000;
+ --ov-color-ansi-black: #000000;
+ --ov-color-ansi-red: #8f0000;
+ --ov-color-ansi-green: #006b2f;
+ --ov-color-ansi-yellow: #8a3f00;
+ --ov-color-ansi-blue: #004ad7;
+ --ov-color-ansi-magenta: #6b20a8;
+ --ov-color-ansi-cyan: #006d7d;
+ --ov-color-ansi-white: #2d2d2d;
+ --ov-color-ansi-bright-black: #5c5c5c;
+ --ov-color-ansi-bright-red: #b30000;
+ --ov-color-ansi-bright-green: #008a3e;
+ --ov-color-ansi-bright-yellow: #a85400;
+ --ov-color-ansi-bright-blue: #1d4ed8;
+ --ov-color-ansi-bright-magenta: #7c3eb8;
+ --ov-color-ansi-bright-cyan: #0088a0;
+ --ov-color-ansi-bright-white: #000000;
+
+ /* Syntax highlighting tokens — high-contrast light */
+ --ov-syntax-comment: #5c5c5c;
+ --ov-syntax-string: #032f62;
+ --ov-syntax-string-escape: #006b2f;
+ --ov-syntax-number: #004ad7;
+ --ov-syntax-keyword: #8f0000;
+ --ov-syntax-keyword-control: #8f0000;
+ --ov-syntax-keyword-operator: #8f0000;
+ --ov-syntax-type: #8a3f00;
+ --ov-syntax-class: #8a3f00;
+ --ov-syntax-interface: #8a3f00;
+ --ov-syntax-function: #6b20a8;
+ --ov-syntax-method: #6b20a8;
+ --ov-syntax-variable: #000000;
+ --ov-syntax-parameter: #8a3f00;
+ --ov-syntax-property: #004ad7;
+ --ov-syntax-namespace: #8a3f00;
+ --ov-syntax-decorator: #6b20a8;
+ --ov-syntax-regexp: #032f62;
+ --ov-syntax-operator: #8f0000;
+ --ov-syntax-punctuation: #000000;
}
:root[data-ov-density='compact'] {
diff --git a/packages/editors/.storybook/main.ts b/packages/editors/.storybook/main.ts
new file mode 100644
index 0000000..cf94686
--- /dev/null
+++ b/packages/editors/.storybook/main.ts
@@ -0,0 +1,53 @@
+import type { StorybookConfig } from '@storybook/react-vite';
+import type { Plugin } from 'vite';
+
+/**
+ * Strip the broken `//# sourceMappingURL=marked.umd.js.map` reference from
+ * monaco-editor@0.52's bundled `marked.js`. The .map file isn't shipped in
+ * the npm package, so Vite logs a noisy warning every time it transforms it.
+ */
+function stripBrokenSourcemaps(): Plugin {
+ return {
+ name: 'strip-broken-sourcemaps',
+ transform(code, id) {
+ if (id.includes('marked') && code.includes('sourceMappingURL=marked.umd.js.map')) {
+ return {
+ code: code.replace('//# sourceMappingURL=marked.umd.js.map', ''),
+ map: null,
+ };
+ }
+ },
+ };
+}
+
+const config: StorybookConfig = {
+ stories: ['../src/**/*.stories.@(ts|tsx)'],
+ addons: ['@storybook/addon-docs', '@storybook/addon-a11y'],
+ framework: {
+ name: '@storybook/react-vite',
+ options: {},
+ },
+ docs: {
+ autodocs: 'tag',
+ },
+ viteFinal(config) {
+ config.optimizeDeps ??= {};
+
+ // monaco-editor is huge — skip esbuild pre-bundling, let Vite serve on-the-fly.
+ config.optimizeDeps.exclude ??= [];
+ config.optimizeDeps.exclude.push('monaco-editor');
+
+ // Pre-bundle the yaml worker entry so esbuild transforms its CJS deps
+ // (path-browserify, etc.) into ESM — required for native ESM workers in dev.
+ config.optimizeDeps.include ??= [];
+ config.optimizeDeps.include.push('monaco-yaml/yaml.worker.js');
+
+ // Strip broken sourcemap reference from monaco's bundled marked.js
+ config.plugins ??= [];
+ (config.plugins as Plugin[]).push(stripBrokenSourcemaps());
+
+ return config;
+ },
+};
+
+export default config;
diff --git a/packages/editors/.storybook/preview.tsx b/packages/editors/.storybook/preview.tsx
new file mode 100644
index 0000000..32afd51
--- /dev/null
+++ b/packages/editors/.storybook/preview.tsx
@@ -0,0 +1,48 @@
+import type { Preview } from '@storybook/react';
+import { ThemeProvider } from '../../base-ui/src/theme/ThemeProvider';
+import '../../base-ui/src/theme/styles.css';
+// Side-effect import — configures Monaco workers + local build before any editor mounts
+import '../src/setup/setupMonacoWorkers';
+
+const preview: Preview = {
+ globalTypes: {
+ theme: {
+ name: 'Theme',
+ defaultValue: 'dark',
+ toolbar: {
+ icon: 'paintbrush',
+ items: ['dark', 'light', 'high-contrast-dark', 'high-contrast-light'],
+ },
+ },
+ },
+ decorators: [
+ (Story, context) => {
+ const theme = context.globals.theme ?? 'dark';
+
+ return (
+
+
+
+
+
+ );
+ },
+ ],
+ parameters: {
+ controls: {
+ expanded: true,
+ },
+ layout: 'fullscreen',
+ },
+};
+
+export default preview;
diff --git a/packages/editors/docs/MONACO_YAML_COMPAT.md b/packages/editors/docs/MONACO_YAML_COMPAT.md
new file mode 100644
index 0000000..5f03262
--- /dev/null
+++ b/packages/editors/docs/MONACO_YAML_COMPAT.md
@@ -0,0 +1,155 @@
+# Monaco + YAML: Compatibility Notes
+
+This document records the compatibility constraints and known issues between
+`monaco-editor`, `monaco-yaml`, and `monaco-worker-manager` so that future
+maintainers (or a potential fork) have full context.
+
+---
+
+## Version Matrix (as of March 2025)
+
+| Package | Version | Notes |
+| ---------------------- | -------- | ----------------------------------------------- |
+| `monaco-editor` | `0.52.2` | Latest in the 0.52 line; **required** for yaml |
+| `monaco-yaml` | `5.4.1` | Peer dep says `>=0.36` but only tested on 0.52 |
+| `monaco-worker-manager`| `2.0.1` | Transitive dep of `monaco-yaml` |
+
+## Why Not Monaco 0.55?
+
+Monaco 0.55 introduced two breaking changes that are **fundamentally
+incompatible** with `monaco-yaml@5.4.1` (via `monaco-worker-manager@2.0.1`):
+
+### 1. `WebWorker` constructor crash
+
+In `vs/base/browser/webWorkerFactory.js`, the `WebWorker` class constructor
+tries `'then' in descriptorOrWorker` (line ~110). When `descriptorOrWorker` is
+`undefined` — which happens because `monaco-worker-manager` doesn't pass
+`opts.worker` — this crashes with:
+
+```text
+TypeError: Cannot use 'in' operator to search for 'then' in undefined
+```
+
+**Workaround attempted:** Monkey-patching `monaco.editor.createWebWorker` to
+inject `opts.worker` from `MonacoEnvironment.getWorker`. This prevents the
+crash but doesn't fix completions.
+
+### 2. Worker protocol mismatch (`$initialize` consumed)
+
+Monaco 0.55's `WebWorkerClient` (in `vs/base/common/worker/webWorker.js`) sends
+a `$initialize` message to the worker. Inside the worker, two different
+initialization paths compete:
+
+- **Default workers** (`editor.worker.js`): Use a single-message protocol via
+ `start()` → `WebWorkerServer` — this works fine.
+- **monaco-worker-manager workers** (yaml, etc.): Use `common/initialize.js`
+ which creates a **two-message** protocol. The first message is consumed by
+ `initialize()` before `WebWorkerServer` is even created. The `$initialize`
+ message from `WebWorkerClient` is therefore never processed by
+ `WebWorkerServer`, causing the proxy to hang forever at "Loading...".
+
+**This is not patchable.** The worker initialization protocol in 0.55 is
+structurally incompatible with how `monaco-worker-manager` bootstraps workers.
+
+### Summary
+
+```text
+monaco-editor@0.55 + monaco-yaml@5.4.1 = broken (hangs on "Loading...")
+monaco-editor@0.52 + monaco-yaml@5.4.1 = working (completions + validation)
+```
+
+## Vite Worker Loading
+
+Monaco workers in Vite require special handling:
+
+### `?worker` imports (current approach)
+
+```ts
+import YamlWorker from './yaml.worker?worker';
+```
+
+Vite bundles each worker with its dependencies, transforming CJS → ESM. This is
+necessary because `yaml-language-server` (used by `monaco-yaml`) depends on
+CJS-only packages like `path-browserify`. Without bundling, native ESM workers
+fail with `module is not defined`.
+
+### Storybook `viteFinal` config
+
+```ts
+viteFinal(config) {
+ config.optimizeDeps ??= {};
+ // monaco-editor is too large for esbuild pre-bundling
+ config.optimizeDeps.exclude ??= [];
+ config.optimizeDeps.exclude.push('monaco-editor');
+ // Pre-bundle yaml worker so CJS deps become ESM
+ config.optimizeDeps.include ??= [];
+ config.optimizeDeps.include.push('monaco-yaml/yaml.worker.js');
+ return config;
+}
+```
+
+## Schema Registration
+
+Schemas are registered at runtime via the `EditorSchemaRegistry` singleton.
+The `configureMonacoYaml()` call happens once at setup time (side-effect import),
+returning a `{ update, dispose }` handle. The registry calls `handle.update()`
+whenever schemas change.
+
+### Schema URI Bug — YAML(768) "No schema request service"
+
+```text
+Unable to load schema from '...'. No schema request service available YAML(768)
+```
+
+This error has **two** causes:
+
+#### 1. Custom protocol URIs (e.g. `k8s://`)
+
+Schema URIs must use `https://`. Custom protocols cause `monaco-yaml` to attempt
+a fetch even with `enableSchemaRequest: false`.
+
+#### 2. URI normalization mismatch (the deeper bug)
+
+`yaml-language-server`'s `JSONSchemaService` has a bug: it stores schema handles
+keyed by `normalizeId(uri)` (which runs `URI.parse(uri).toString()`) but looks
+them up via `createCombinedSchema` → `getOrAddSchemaHandle` using the **raw** URI
+from `filePatternAssociations`. If `URI.parse` changes the URI (e.g. encoding
+`::` to `%3A%3A`), the lookup misses the stored handle and creates a new one
+**without** the inline schema content, triggering a fetch attempt.
+
+**Fix:** Schema URIs must be "URI-safe" — avoid characters that `URI.parse`
+normalizes differently. Use `encodeURIComponent()` for dynamic segments:
+
+```ts
+// BAD — :: gets normalized by URI.parse, causing handle lookup miss
+uri: `https://omniview.dev/schemas/k8s/core::v1::Pod`
+
+// GOOD — encoded segment survives URI normalization
+uri: `https://omniview.dev/schemas/k8s/${encodeURIComponent('core::v1::Pod')}`
+```
+
+## If We Fork `monaco-yaml`
+
+Key areas to consider:
+
+1. **Drop `monaco-worker-manager` dependency** — use Monaco's built-in
+ `createWebWorker` directly with explicit worker injection. This would fix
+ Monaco 0.55+ compatibility.
+
+2. **Worker initialization** — replace the two-message `common/initialize.js`
+ protocol with the single-message `start()` pattern that Monaco's own workers
+ use (compatible across all versions).
+
+3. **Schema URI handling** — fix the `normalizeId` mismatch bug in
+ `JSONSchemaService` (store and look up with the same normalized key).
+ Allow custom URI protocols without requiring `enableSchemaRequest: true`.
+ The schema object is already provided inline; the URI should just be an
+ identifier, not a fetch target.
+
+4. **`yaml-language-server` bundling** — the LSP server has heavy CJS
+ dependencies. A fork could pre-bundle or replace them to simplify worker
+ loading.
+
+5. **Singleton enforcement** — `configureMonacoYaml` currently throws if called
+ twice. A fork could support reconfiguration or multiple instances for
+ different editor contexts.
diff --git a/packages/editors/package.json b/packages/editors/package.json
new file mode 100644
index 0000000..277d760
--- /dev/null
+++ b/packages/editors/package.json
@@ -0,0 +1,70 @@
+{
+ "name": "@omniview/editors",
+ "version": "0.1.0",
+ "private": true,
+ "description": "Omniview editor components — code editor, diff viewer, terminal, markdown preview, command palette, object inspector.",
+ "type": "module",
+ "main": "./dist/index.js",
+ "module": "./dist/index.js",
+ "types": "./dist/index.d.ts",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.js"
+ },
+ "./styles.css": "./dist/style.css"
+ },
+ "files": [
+ "dist"
+ ],
+ "sideEffects": [
+ "**/*.css"
+ ],
+ "scripts": {
+ "clean": "rm -rf dist storybook-static coverage",
+ "build": "vite build",
+ "typecheck": "tsc -p tsconfig.json --noEmit",
+ "test": "vitest run",
+ "test:watch": "vitest",
+ "storybook": "storybook dev -p 6007",
+ "build-storybook": "storybook build"
+ },
+ "peerDependencies": {
+ "@omniview/base-ui": "workspace:*",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0"
+ },
+ "dependencies": {
+ "@xterm/addon-fit": "^0.11.0",
+ "@xterm/addon-search": "^0.16.0",
+ "@xterm/addon-serialize": "^0.14.0",
+ "@xterm/addon-unicode11": "^0.9.0",
+ "@xterm/addon-web-links": "^0.12.0",
+ "@xterm/addon-webgl": "^0.19.0",
+ "@xterm/xterm": "^6.0.0",
+ "monaco-editor": "^0.52.2",
+ "monaco-yaml": "^5.4.1",
+ "react-markdown": "^9.0.3",
+ "rehype-raw": "^7.0.0",
+ "rehype-sanitize": "^6.0.0",
+ "remark-gfm": "^4.0.0"
+ },
+ "devDependencies": {
+ "@storybook/addon-a11y": "^10.2.15",
+ "@storybook/addon-docs": "^10.2.15",
+ "@storybook/react": "^10.2.15",
+ "@storybook/react-vite": "^10.2.15",
+ "@testing-library/jest-dom": "^6.6.3",
+ "@testing-library/react": "^16.2.0",
+ "@testing-library/user-event": "^14.6.1",
+ "@types/react": "^19.0.10",
+ "@types/react-dom": "^19.0.4",
+ "@vitejs/plugin-react": "^4.3.4",
+ "jsdom": "^25.0.1",
+ "storybook": "^10.2.15",
+ "typescript": "^5.9.2",
+ "vite": "^5.4.14",
+ "vite-plugin-dts": "^4.5.1",
+ "vitest": "^2.1.8"
+ }
+}
diff --git a/packages/editors/src/components/code-editor/CodeEditor.module.css b/packages/editors/src/components/code-editor/CodeEditor.module.css
new file mode 100644
index 0000000..932d145
--- /dev/null
+++ b/packages/editors/src/components/code-editor/CodeEditor.module.css
@@ -0,0 +1,18 @@
+.Root {
+ position: relative;
+ overflow: hidden;
+ border-radius: var(--ov-radius-sm, 4px);
+ border: 1px solid var(--ov-color-editor-border, var(--ov-color-border-default));
+ background: var(--ov-color-editor-bg, var(--ov-color-bg-surface));
+}
+
+.Loading {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 100%;
+ width: 100%;
+ color: var(--ov-color-fg-muted);
+ font-family: var(--ov-font-family-sans, sans-serif);
+ font-size: var(--ov-font-size-sm, 13px);
+}
diff --git a/packages/editors/src/components/code-editor/CodeEditor.stories.tsx b/packages/editors/src/components/code-editor/CodeEditor.stories.tsx
new file mode 100644
index 0000000..3adc6d9
--- /dev/null
+++ b/packages/editors/src/components/code-editor/CodeEditor.stories.tsx
@@ -0,0 +1,686 @@
+import { useState, useEffect, useCallback, useRef } from 'react';
+import type { Meta, StoryObj } from '@storybook/react';
+import { CodeEditor, type CodeEditorProps, type EditorDiagnostic, type CursorPosition } from './CodeEditor';
+import { editorSchemas, type EditorSchema } from '../../schemas';
+import { k8sSchemas, gvrFileMatch, type K8sSchemaEntry } from '../../schemas/kubernetes';
+
+const sampleCode = `import { useState } from 'react';
+
+export function Counter() {
+ const [count, setCount] = useState(0);
+
+ return (
+
+
Count: {count}
+
setCount(c => c + 1)}>
+ Increment
+
+
+ );
+}`;
+
+const meta: Meta = {
+ title: 'Editors/CodeEditor',
+ component: CodeEditor,
+ tags: ['autodocs'],
+ args: {
+ value: sampleCode,
+ language: 'typescript',
+ height: 400,
+ },
+ argTypes: {
+ language: {
+ control: 'select',
+ options: ['auto', 'typescript', 'javascript', 'python', 'json', 'css', 'html', 'yaml', 'go', 'rust', 'markdown'],
+ },
+ readOnly: { control: 'boolean' },
+ lineNumbers: { control: 'boolean' },
+ minimap: { control: 'boolean' },
+ wordWrap: { control: 'boolean' },
+ height: { control: { type: 'number', min: 100, max: 800, step: 50 } },
+ },
+};
+
+export default meta;
+type Story = StoryObj;
+
+export const Playground: Story = {};
+
+export const ReadOnly: Story = {
+ args: {
+ readOnly: true,
+ },
+};
+
+export const WithMinimap: Story = {
+ args: {
+ minimap: true,
+ value: Array.from({ length: 100 }, (_, i) => `// Line ${i + 1}: sample code content`).join(
+ '\n',
+ ),
+ },
+};
+
+export const WordWrap: Story = {
+ args: {
+ wordWrap: true,
+ value: `// This is a very long line that should wrap when wordWrap is enabled. ${'Lorem ipsum dolor sit amet, consectetur adipiscing elit. '.repeat(5)}
+// Another long line follows. ${'Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. '.repeat(3)}
+const shortLine = true;`,
+ },
+};
+
+export const NoLineNumbers: Story = {
+ args: {
+ lineNumbers: false,
+ value: '# Markdown-style preview\n\nNo line numbers shown.\n\n- Item 1\n- Item 2\n- Item 3',
+ language: 'markdown',
+ },
+};
+
+export const JSONContent: Story = {
+ args: {
+ value: JSON.stringify({
+ name: 'omniview',
+ version: '1.0.0',
+ dependencies: { react: '^19.0.0', 'react-dom': '^19.0.0' },
+ devDependencies: { typescript: '^5.9.0', vite: '^5.4.0' },
+ }),
+ language: 'json',
+ },
+};
+
+export const LanguageDetection: Story = {
+ render: (args) => {
+ const { language, ...rest } = args;
+ return ;
+ },
+ args: {
+ value: 'package main\n\nimport "fmt"\n\nfunc main() {\n\tfmt.Println("Hello")\n}',
+ filename: 'main.go',
+ language: 'auto' as unknown as undefined,
+ },
+};
+
+export const PythonCode: Story = {
+ args: {
+ value: `import os
+from typing import List, Optional
+
+class FileProcessor:
+ """Process files in a directory."""
+
+ def __init__(self, root: str) -> None:
+ self.root = root
+ self._files: List[str] = []
+
+ def scan(self, extensions: Optional[List[str]] = None) -> List[str]:
+ for dirpath, _, filenames in os.walk(self.root):
+ for f in filenames:
+ if extensions is None or any(f.endswith(ext) for ext in extensions):
+ self._files.append(os.path.join(dirpath, f))
+ return self._files
+
+if __name__ == "__main__":
+ proc = FileProcessor("/tmp")
+ print(proc.scan([".py", ".txt"]))`,
+ language: 'python',
+ },
+};
+
+export const YAMLContent: Story = {
+ args: {
+ value: `apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: web-server
+ namespace: production
+ labels:
+ app: web
+ tier: frontend
+spec:
+ replicas: 3
+ selector:
+ matchLabels:
+ app: web
+ template:
+ metadata:
+ labels:
+ app: web
+ spec:
+ containers:
+ - name: nginx
+ image: nginx:1.25
+ ports:
+ - containerPort: 80
+ resources:
+ limits:
+ cpu: "500m"
+ memory: "128Mi"`,
+ language: 'yaml',
+ },
+};
+
+export const CSSContent: Story = {
+ args: {
+ value: `:root {
+ --ov-color-bg-base: #1e1e1e;
+ --ov-color-fg-default: #d4d4d4;
+ --ov-radius-sm: 4px;
+}
+
+.container {
+ display: flex;
+ flex-direction: column;
+ gap: var(--ov-space-2);
+ padding: var(--ov-space-4);
+ background: var(--ov-color-bg-base);
+ border-radius: var(--ov-radius-sm);
+}
+
+.container:hover {
+ border-color: var(--ov-color-border-focus);
+}
+
+@media (prefers-reduced-motion: reduce) {
+ * { transition: none !important; }
+}`,
+ language: 'css',
+ },
+};
+
+export const SyntaxHighlightingOff: Story = {
+ args: {
+ value: sampleCode,
+ language: 'typescript',
+ syntaxHighlighting: false,
+ },
+};
+
+/** Demonstrates toggling syntax highlighting at runtime. */
+function SyntaxToggleStory(args: CodeEditorProps) {
+ const [enabled, setEnabled] = useState(true);
+ return (
+
+
+ setEnabled(e.target.checked)} />
+ Syntax Highlighting
+
+
+
+ );
+}
+
+export const SyntaxHighlightingToggle: Story = {
+ render: (args) => ,
+ args: {
+ value: sampleCode,
+ language: 'typescript',
+ height: 400,
+ },
+};
+
+/** Demonstrates the onChange callback with a live character count. */
+function ControlledStory(args: CodeEditorProps) {
+ const [value, setValue] = useState(args.value);
+ return (
+
+
+ Characters: {value.length} | Lines: {value.split('\n').length}
+
+
+
+ );
+}
+
+export const Controlled: Story = {
+ render: (args) => ,
+ args: {
+ value: '// Type here to see live stats\nconst x = 1;\n',
+ },
+};
+
+export const EmptyEditor: Story = {
+ args: {
+ value: '',
+ language: 'typescript',
+ },
+};
+
+export const CustomDimensions: Story = {
+ args: {
+ height: 200,
+ width: 500,
+ value: 'const small = true;\n',
+ language: 'typescript',
+ },
+};
+
+// ---------------------------------------------------------------------------
+// YAML Schema Completion — Real Kubernetes Schemas (demo-only)
+// ---------------------------------------------------------------------------
+
+const podSampleYaml = `apiVersion: v1
+kind: Pod
+metadata:
+ name: my-app
+ namespace: default
+ labels:
+ app: my-app
+spec:
+ containers:
+ - name: app
+ image: nginx:1.25
+ ports:
+ - containerPort: 80
+ resources:
+ limits:
+ cpu: "500m"
+ memory: "128Mi"
+ restartPolicy: Always
+`;
+
+const deploymentSampleYaml = `apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: web-server
+ namespace: production
+spec:
+ replicas: 3
+ selector:
+ matchLabels:
+ app: web
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxSurge: "25%"
+ maxUnavailable: "25%"
+ template:
+ metadata:
+ labels:
+ app: web
+ spec:
+ containers:
+ - name: nginx
+ image: nginx:1.25
+ ports:
+ - containerPort: 80
+`;
+
+const serviceSampleYaml = `apiVersion: v1
+kind: Service
+metadata:
+ name: web-svc
+ namespace: production
+spec:
+ type: ClusterIP
+ selector:
+ app: web
+ ports:
+ - port: 80
+ targetPort: 8080
+`;
+
+/**
+ * Sample YAML content keyed by GVR, matching the structured filename convention:
+ * //::::.yaml
+ */
+const sampleYamlByGvr: Record = {
+ 'core::v1::Pod': podSampleYaml,
+ 'apps::v1::Deployment': deploymentSampleYaml,
+ 'core::v1::Service': serviceSampleYaml,
+ 'core::v1::ConfigMap': `apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: app-config
+ namespace: default
+data:
+ APP_ENV: production
+ LOG_LEVEL: info
+`,
+ 'networking.k8s.io::v1::Ingress': `apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: web-ingress
+ namespace: production
+spec:
+ rules:
+ - host: example.com
+ http:
+ paths:
+ - path: /
+ pathType: Prefix
+ backend:
+ service:
+ name: web-svc
+ port:
+ number: 80
+`,
+};
+
+/** Simulated plugin/connection context for structured filenames. */
+const DEMO_PLUGIN = 'kubernetes';
+const DEMO_CONNECTION = 'demo-cluster';
+
+/** Build a structured filename for a GVR, e.g. "kubernetes/demo-cluster/core::v1::Pod.yaml" */
+function gvrFilename(gvr: string): string {
+ return `${DEMO_PLUGIN}/${DEMO_CONNECTION}/${gvr}.yaml`;
+}
+
+/**
+ * Demonstrates runtime schema registration with real Kubernetes JSON schemas.
+ *
+ * This mirrors what happens in production: schemas are loaded per-cluster at
+ * runtime. Before loading, the editor has no autocompletion. After loading,
+ * full schema-driven completions appear as you type.
+ *
+ * Click "Load" buttons to dynamically register schemas. Click "Unload All"
+ * to remove them. The editor updates in real time.
+ */
+function RuntimeSchemaStory(args: CodeEditorProps) {
+ const [value, setValue] = useState(args.value);
+ const [loaded, setLoaded] = useState>({});
+ const [loading, setLoading] = useState>({});
+ const [activeGvr, setActiveGvr] = useState('core::v1::Pod');
+ const [diagnosticsList, setDiagnosticsList] = useState([]);
+ const [cursor, setCursor] = useState({ lineNumber: 1, column: 1 });
+ const registeredUris = useRef>(new Set());
+
+ // Clean up only URIs registered by this story on unmount
+ useEffect(() => {
+ return () => {
+ for (const uri of registeredUris.current) {
+ editorSchemas.unregister(uri);
+ }
+ registeredUris.current.clear();
+ };
+ }, []);
+
+ const loadSchema = useCallback(async (entry: K8sSchemaEntry) => {
+ setLoading((prev) => ({ ...prev, [entry.gvr]: true }));
+ try {
+ const mod = await entry.load();
+ const uri = `https://omniview.dev/schemas/k8s/${encodeURIComponent(entry.gvr)}`;
+ const schema: EditorSchema = {
+ uri,
+ fileMatch: gvrFileMatch(entry.gvr),
+ schema: mod.default,
+ name: `${entry.kind} (${entry.apiVersion})`,
+ description: `Kubernetes ${entry.kind} resource schema`,
+ };
+ editorSchemas.register(schema);
+ registeredUris.current.add(uri);
+ setLoaded((prev) => ({ ...prev, [entry.gvr]: true }));
+ } finally {
+ setLoading((prev) => ({ ...prev, [entry.gvr]: false }));
+ }
+ }, []);
+
+ const unloadAll = useCallback(() => {
+ for (const uri of registeredUris.current) {
+ editorSchemas.unregister(uri);
+ }
+ registeredUris.current.clear();
+ setLoaded({});
+ }, []);
+
+ const loadedCount = Object.values(loaded).filter(Boolean).length;
+
+ function schemaStatusLabel(): string {
+ if (loadedCount === 0) return 'none — no completions available';
+ const count = `${loadedCount} schema(s)`;
+ if (loaded[activeGvr]) return `${count} — active file has schema`;
+ return `${count} — active file has NO schema loaded`;
+ }
+
+ return (
+
+
+ Runtime Schema Loading
+
+
+ Simulates per-cluster schema loading. Schemas are fetched lazily and registered at runtime.
+ Each schema matches a structured filename:{' '}
+ <plugin>/<connectionId>/<group>::<version>::<resource>.yaml
+
+
+
+ {k8sSchemas.map((entry) => (
+ {
+ void loadSchema(entry);
+ setActiveGvr(entry.gvr);
+ setValue(sampleYamlByGvr[entry.gvr] ?? '');
+ }}
+ disabled={!!loaded[entry.gvr] || !!loading[entry.gvr]}
+ style={{
+ padding: '4px 12px',
+ fontSize: 12,
+ borderRadius: 4,
+ border: '1px solid var(--ov-color-border-default)',
+ background: loaded[entry.gvr]
+ ? 'var(--ov-color-success-soft, #2d4a2d)'
+ : 'var(--ov-color-bg-surface)',
+ color: 'var(--ov-color-fg-default)',
+ cursor: loaded[entry.gvr] || loading[entry.gvr] ? 'default' : 'pointer',
+ opacity: loaded[entry.gvr] ? 0.7 : 1,
+ }}
+ >
+ {loading[entry.gvr]
+ ? `Loading ${entry.kind}...`
+ : loaded[entry.gvr]
+ ? `${entry.kind} (${entry.apiVersion})`
+ : `Load ${entry.kind}`}
+
+ ))}
+ {loadedCount > 0 && (
+
+ Unload All
+
+ )}
+
+
+
+ {k8sSchemas.map((entry) => (
+ {
+ setActiveGvr(entry.gvr);
+ setValue(sampleYamlByGvr[entry.gvr] ?? '');
+ }}
+ style={{
+ padding: '2px 8px',
+ fontSize: 11,
+ borderRadius: 3,
+ border: '1px solid var(--ov-color-border-default)',
+ background:
+ activeGvr === entry.gvr
+ ? 'var(--ov-color-bg-elevated, #2a2a2a)'
+ : 'transparent',
+ color: 'var(--ov-color-fg-default)',
+ cursor: 'pointer',
+ opacity: activeGvr === entry.gvr ? 1 : 0.6,
+ }}
+ >
+ {entry.kind}
+
+ ))}
+
+
+
+ file: {gvrFilename(activeGvr)}
+
+
+ Loaded: {schemaStatusLabel()}
+
+
+
+
+ {/* Status bar */}
+
+ Ln {cursor.lineNumber}, Col {cursor.column}
+
+ {diagnosticsList.length === 0
+ ? 'No problems'
+ : `${diagnosticsList.filter(d => d.severity === 'error').length} errors, ${diagnosticsList.filter(d => d.severity === 'warning').length} warnings`}
+
+
+
+ {/* Problems panel */}
+ {diagnosticsList.length > 0 && (
+
+ {diagnosticsList.map((d, i) => (
+
+ [{d.severity}] Ln {d.startLineNumber}:{d.startColumn} — {d.message}
+ {d.source && ({d.source}) }
+
+ ))}
+
+ )}
+
+ );
+}
+
+export const RuntimeSchemaRegistration: Story = {
+ render: (args) => ,
+ args: {
+ value: podSampleYaml,
+ language: 'yaml',
+ height: 500,
+ },
+};
+
+/**
+ * Demonstrates the onMount callback for advanced Monaco configuration.
+ * Plugins can use this to register custom completion providers, set up
+ * language workers, or perform any Monaco API call.
+ */
+function OnMountCallbackStory(args: CodeEditorProps) {
+ const [value, setValue] = useState(args.value);
+ const [log, setLog] = useState([]);
+ const completionDisposable = useRef<{ dispose(): void } | null>(null);
+
+ // Dispose the completion provider on unmount
+ useEffect(() => {
+ return () => {
+ completionDisposable.current?.dispose();
+ completionDisposable.current = null;
+ };
+ }, []);
+
+ const handleMount = (_editor: unknown, monaco: unknown) => {
+ setLog((prev) => [...prev, 'Editor mounted — Monaco instance available']);
+
+ const m = monaco as {
+ languages: {
+ registerCompletionItemProvider: (
+ language: string,
+ provider: Record,
+ ) => { dispose(): void };
+ CompletionItemKind: Record;
+ };
+ };
+
+ completionDisposable.current = m.languages.registerCompletionItemProvider('yaml', {
+ provideCompletionItems: () => ({
+ suggestions: [
+ {
+ label: 'apiVersion: v1',
+ kind: m.languages.CompletionItemKind.Snippet,
+ insertText: 'apiVersion: v1',
+ documentation: 'Core API version',
+ },
+ {
+ label: 'apiVersion: apps/v1',
+ kind: m.languages.CompletionItemKind.Snippet,
+ insertText: 'apiVersion: apps/v1',
+ documentation: 'Apps API version',
+ },
+ ],
+ }),
+ });
+
+ setLog((prev) => [...prev, 'Custom YAML completion provider registered']);
+ };
+
+ return (
+
+
+ Uses onMount to register a custom completion provider via raw Monaco API.
+
+
+
+ {log.map((entry, i) => (
+
{entry}
+ ))}
+
+
+ );
+}
+
+export const OnMountCallback: Story = {
+ render: (args) => ,
+ args: {
+ value: `# Type here and press Ctrl+Space to see custom completions\n`,
+ language: 'yaml',
+ height: 300,
+ },
+};
diff --git a/packages/editors/src/components/code-editor/CodeEditor.test.tsx b/packages/editors/src/components/code-editor/CodeEditor.test.tsx
new file mode 100644
index 0000000..a663346
--- /dev/null
+++ b/packages/editors/src/components/code-editor/CodeEditor.test.tsx
@@ -0,0 +1,213 @@
+import { render, screen, act } from '@testing-library/react';
+import { createRef } from 'react';
+import { describe, expect, it, vi, beforeEach } from 'vitest';
+import { CodeEditor, type CodeEditorHandle } from './CodeEditor';
+
+let mockEditorInstance: Record;
+let mockModel: Record;
+
+const mockCreate = vi.fn();
+const mockGetModel = vi.fn();
+const mockCreateModel = vi.fn();
+const mockSetModelLanguage = vi.fn();
+const mockDefineTheme = vi.fn();
+const mockSetTheme = vi.fn();
+
+vi.mock('monaco-editor', () => {
+ const Uri = {
+ parse: (s: string) => ({ toString: () => s, path: s }),
+ };
+
+ return {
+ default: undefined,
+ Uri,
+ editor: {
+ create: (...args: unknown[]) => {
+ mockCreate(...args);
+ mockModel = {
+ getFullModelRange: vi.fn(() => ({})),
+ uri: { toString: () => 'test', path: 'test' },
+ dispose: vi.fn(),
+ };
+ mockEditorInstance = {
+ getValue: vi.fn(() => ''),
+ setValue: vi.fn(),
+ getModel: vi.fn(() => mockModel),
+ updateOptions: vi.fn(),
+ executeEdits: vi.fn(),
+ pushUndoStop: vi.fn(),
+ setModel: vi.fn(),
+ onDidChangeModelContent: vi.fn(() => ({ dispose: vi.fn() })),
+ dispose: vi.fn(),
+ focus: vi.fn(),
+ getOption: vi.fn(() => false),
+ };
+ return mockEditorInstance;
+ },
+ getModel: (...args: unknown[]) => mockGetModel(...args),
+ createModel: (...args: unknown[]) => {
+ mockCreateModel(...args);
+ return {
+ getFullModelRange: vi.fn(() => ({})),
+ uri: { toString: () => 'test', path: 'test' },
+ dispose: vi.fn(),
+ };
+ },
+ setModelLanguage: (...args: unknown[]) => mockSetModelLanguage(...args),
+ defineTheme: (...args: unknown[]) => mockDefineTheme(...args),
+ setTheme: (...args: unknown[]) => mockSetTheme(...args),
+ EditorOption: { readOnly: 81 },
+ },
+ languages: {
+ register: vi.fn(),
+ typescript: {
+ typescriptDefaults: {
+ setDiagnosticsOptions: vi.fn(),
+ setCompilerOptions: vi.fn(),
+ },
+ javascriptDefaults: {
+ setDiagnosticsOptions: vi.fn(),
+ },
+ },
+ },
+ };
+});
+
+vi.mock('../../schemas', () => ({
+ editorSchemas: {
+ applyYamlSchemas: vi.fn(),
+ applyJsonSchemas: vi.fn(),
+ onChange: vi.fn(() => vi.fn()),
+ },
+}));
+
+vi.mock('../../themes/useEditorTheme', () => ({
+ useEditorTheme: () => ({ isDark: false }),
+}));
+
+vi.mock('../../themes/monaco', () => ({
+ buildMonacoTheme: () => ({ base: 'vs', inherit: true, rules: [], colors: {} }),
+ OV_MONACO_THEME: 'ov-theme',
+}));
+
+beforeEach(() => {
+ vi.clearAllMocks();
+ mockGetModel.mockReturnValue(null);
+});
+
+describe('CodeEditor', () => {
+ it('renders the editor container', () => {
+ render( );
+ expect(screen.getByTestId('code-editor')).toBeInTheDocument();
+ });
+
+ it('creates a monaco editor on mount', () => {
+ render( );
+ expect(mockCreate).toHaveBeenCalled();
+ });
+
+ it('detects language from filename', () => {
+ render( );
+ expect(mockCreateModel).toHaveBeenCalledWith(
+ '',
+ 'typescript',
+ expect.anything(),
+ );
+ });
+
+ it('uses explicit language over filename detection', () => {
+ render( );
+ expect(mockCreateModel).toHaveBeenCalledWith(
+ '',
+ 'python',
+ expect.anything(),
+ );
+ });
+
+ it('passes readOnly to editor options', () => {
+ render( );
+ expect(mockCreate).toHaveBeenCalledWith(
+ expect.anything(),
+ expect.objectContaining({ readOnly: true }),
+ );
+ });
+
+ it('pretty-prints JSON content', () => {
+ const json = '{"a":1}';
+ render( );
+ const formatted = JSON.stringify({ a: 1 }, null, 2);
+ expect(mockCreateModel).toHaveBeenCalledWith(
+ formatted,
+ 'json',
+ );
+ });
+
+ it('passes through invalid JSON unchanged', () => {
+ const badJson = '{not valid json}';
+ render( );
+ expect(mockCreateModel).toHaveBeenCalledWith(
+ badJson,
+ 'json',
+ );
+ });
+
+ it('merges className', () => {
+ render( );
+ expect(screen.getByTestId('code-editor')).toHaveClass('my-editor');
+ });
+
+ it('disables line numbers via options', () => {
+ render( );
+ expect(mockCreate).toHaveBeenCalledWith(
+ expect.anything(),
+ expect.objectContaining({ lineNumbers: 'off' }),
+ );
+ });
+
+ it('enables minimap', () => {
+ render( );
+ expect(mockCreate).toHaveBeenCalledWith(
+ expect.anything(),
+ expect.objectContaining({ minimap: { enabled: true } }),
+ );
+ });
+
+ it('enables word wrap', () => {
+ render( );
+ expect(mockCreate).toHaveBeenCalledWith(
+ expect.anything(),
+ expect.objectContaining({ wordWrap: 'on' }),
+ );
+ });
+
+ it('applies height and width styles', () => {
+ render( );
+ const container = screen.getByTestId('code-editor');
+ expect(container).toHaveStyle({ height: '400px', width: '600px' });
+ });
+
+ it('exposes imperative handle via ref', async () => {
+ const ref = createRef();
+ render( );
+
+ // Editor is created synchronously in useEffect, wait for it
+ await act(async () => {});
+
+ expect(ref.current?.getEditor()).toBeTruthy();
+ expect(ref.current?.getMonaco()).toBeTruthy();
+ ref.current?.focus();
+ expect(mockEditorInstance.focus).toHaveBeenCalled();
+ });
+
+ it('handles empty value', () => {
+ render( );
+ expect(mockCreate).toHaveBeenCalled();
+ });
+
+ it('disposes editor and model on unmount', () => {
+ const { unmount } = render( );
+ unmount();
+ expect(mockModel.dispose).toHaveBeenCalled();
+ expect(mockEditorInstance.dispose).toHaveBeenCalled();
+ });
+});
diff --git a/packages/editors/src/components/code-editor/CodeEditor.tsx b/packages/editors/src/components/code-editor/CodeEditor.tsx
new file mode 100644
index 0000000..3f47381
--- /dev/null
+++ b/packages/editors/src/components/code-editor/CodeEditor.tsx
@@ -0,0 +1,608 @@
+import {
+ forwardRef,
+ useEffect,
+ useImperativeHandle,
+ useMemo,
+ useRef,
+ useState,
+} from 'react';
+import * as monaco from 'monaco-editor';
+import { useEditorTheme } from '../../themes/useEditorTheme';
+import { buildMonacoTheme, OV_MONACO_THEME } from '../../themes/monaco';
+import { detectLanguage } from '../../utils/language';
+import { editorSchemas } from '../../schemas';
+import styles from './CodeEditor.module.css';
+
+// ---------------------------------------------------------------------------
+// Debug logging (set to false for production)
+// ---------------------------------------------------------------------------
+
+const DEBUG = false;
+function log(...args: unknown[]) {
+ if (DEBUG) console.log('[CodeEditor]', ...args);
+}
+
+// ---------------------------------------------------------------------------
+// Types
+// ---------------------------------------------------------------------------
+
+/** Severity levels matching Monaco's MarkerSeverity enum. */
+export type DiagnosticSeverity = 'error' | 'warning' | 'info' | 'hint';
+
+/** A parsed diagnostic from the editor's validation markers. */
+export interface EditorDiagnostic {
+ /** Human-readable error message. */
+ message: string;
+ /** Severity level. */
+ severity: DiagnosticSeverity;
+ /** 1-based line number. */
+ startLineNumber: number;
+ /** 1-based column number. */
+ startColumn: number;
+ /** 1-based end line number. */
+ endLineNumber: number;
+ /** 1-based end column number. */
+ endColumn: number;
+ /** The owner/source of the marker (e.g. "yaml", "typescript"). */
+ source?: string;
+ /** Optional error code. */
+ code?: string;
+}
+
+/** Cursor position info passed to `onCursorChange`. */
+export interface CursorPosition {
+ /** 1-based line number. */
+ lineNumber: number;
+ /** 1-based column number. */
+ column: number;
+}
+
+export interface CodeEditorProps {
+ /** The editor content. Controlled — updates push new text into the editor. */
+ value: string;
+ /** Fires when the user edits content. Omit for a read-only display. */
+ onChange?: (value: string) => void;
+
+ // -- File identity ----------------------------------------------------------
+
+ /** Explicit Monaco language id (e.g. `"yaml"`, `"typescript"`). */
+ language?: string;
+ /**
+ * Virtual filename / path used to derive the language (when `language` is
+ * omitted) and, more importantly, to set the Monaco model URI so that
+ * `monaco-yaml` / JSON schema `fileMatch` patterns can match.
+ *
+ * For structured resources use the convention:
+ * `//::::.yaml`
+ *
+ * @example "kubernetes/demo-cluster/core::v1::Pod.yaml"
+ */
+ filename?: string;
+
+ // -- Editor behaviour -------------------------------------------------------
+
+ readOnly?: boolean;
+ lineNumbers?: boolean;
+ minimap?: boolean;
+ wordWrap?: boolean;
+ tabSize?: number;
+ /** Show TypeScript / JavaScript diagnostics (squiggly error markers). @default false */
+ diagnostics?: boolean;
+ /** Trigger completions automatically as the user types. @default false */
+ quickSuggestions?: boolean;
+ /**
+ * Enable syntax highlighting. When false the editor still renders text but
+ * without any token colouring — useful for plain log output.
+ * @default true
+ */
+ syntaxHighlighting?: boolean;
+
+ // -- Lifecycle callbacks ----------------------------------------------------
+
+ /**
+ * Called once after the Monaco editor mounts, exposing the raw editor and
+ * `monaco` namespace for advanced configuration (custom keybindings,
+ * language registration, LSP server attachment, etc.).
+ */
+ onMount?: (
+ editor: monaco.editor.IStandaloneCodeEditor,
+ monacoInstance: typeof monaco,
+ ) => void;
+
+ /**
+ * Called whenever the editor's validation markers change. Provides a parsed
+ * array of diagnostics that can be displayed in a problems panel, status bar,
+ * or custom UI.
+ *
+ * Fires for all marker owners (YAML, TypeScript, JSON, etc.).
+ */
+ onDiagnostics?: (diagnostics: EditorDiagnostic[]) => void;
+
+ /**
+ * Called when the cursor position changes. Useful for breadcrumb navigation,
+ * status bar line/column display, or position tracking.
+ */
+ onCursorChange?: (position: CursorPosition) => void;
+
+ // -- Layout -----------------------------------------------------------------
+
+ height?: string | number;
+ width?: string | number;
+ className?: string;
+}
+
+/**
+ * Snapshot of editor internal state for debug panels / troubleshooting.
+ * Obtained via `handle.getDebugState()`.
+ */
+export interface EditorDebugState {
+ // -- Model ------------------------------------------------------------------
+ /** The model URI string (drives schema matching). */
+ modelUri: string | null;
+ /** The resolved language id (e.g. "yaml", "typescript"). */
+ modelLanguage: string | null;
+ /** Total number of lines. */
+ modelLineCount: number;
+ /** Total character count. */
+ modelContentLength: number;
+ /** Monaco's internal version id — incremented on every edit including undo. */
+ modelVersionId: number;
+ /** End-of-line sequence (`\n` or `\r\n`). */
+ modelEOL: string | null;
+
+ // -- Cursor / Selection -----------------------------------------------------
+ cursorPosition: CursorPosition | null;
+ /** All active selections (multi-cursor). */
+ selections: Array<{
+ startLineNumber: number;
+ startColumn: number;
+ endLineNumber: number;
+ endColumn: number;
+ }>;
+
+ // -- Diagnostics ------------------------------------------------------------
+ /** Total marker count across all owners. */
+ diagnosticsTotal: number;
+ /** Breakdown by severity. */
+ diagnosticsBySeverity: Record;
+ /** Breakdown by owner/source (e.g. { yaml: 2, typescript: 1 }). */
+ diagnosticsBySource: Record;
+
+ // -- Schema -----------------------------------------------------------------
+ /** Total registered schemas in the EditorSchemaRegistry. */
+ registeredSchemaCount: number;
+ /** Schemas whose fileMatch patterns match the current model URI. */
+ matchingSchemas: Array<{ uri: string; name?: string; fileMatch: string[] }>;
+
+ // -- Editor -----------------------------------------------------------------
+ /** Whether the editor is read-only. */
+ isReadOnly: boolean;
+ /** Whether the editor or its widgets have focus. */
+ isFocused: boolean;
+
+ // -- Monaco environment -----------------------------------------------------
+ /** Total number of Monaco models in memory. */
+ totalModelCount: number;
+ /** Number of registered languages. */
+ registeredLanguageCount: number;
+ /** Whether the YAML handle is set (worker initialized). */
+ yamlHandleSet: boolean;
+}
+
+export interface CodeEditorHandle {
+ /** The underlying Monaco standalone code editor, or null before mount. */
+ getEditor: () => monaco.editor.IStandaloneCodeEditor | null;
+ /** The `monaco-editor` module namespace. */
+ getMonaco: () => typeof monaco;
+ /** Focus the editor. */
+ focus: () => void;
+ /** Get the current diagnostics (validation markers) for the editor model. */
+ getDiagnostics: () => EditorDiagnostic[];
+ /**
+ * Snapshot the full editor debug state — model info, cursor, diagnostics,
+ * schemas, environment. Designed for debug panels and troubleshooting.
+ */
+ getDebugState: () => EditorDebugState;
+}
+
+// ---------------------------------------------------------------------------
+// Helpers
+// ---------------------------------------------------------------------------
+
+function cn(...parts: Array): string {
+ return parts.filter(Boolean).join(' ');
+}
+
+function getOrCreateModel(
+ value: string,
+ language: string | undefined,
+ path: string | undefined,
+): monaco.editor.ITextModel {
+ if (path) {
+ const uri = monaco.Uri.parse(path);
+ log('getOrCreateModel — parsed URI:', uri.toString(), '| language:', language);
+ const existing = monaco.editor.getModel(uri);
+ if (existing) {
+ log(' reusing existing model');
+ return existing;
+ }
+ log(' creating new model with URI');
+ return monaco.editor.createModel(value, language, uri);
+ }
+ log('getOrCreateModel — no path, creating anonymous model | language:', language);
+ return monaco.editor.createModel(value, language);
+}
+
+function severityToString(severity: monaco.MarkerSeverity): DiagnosticSeverity {
+ switch (severity) {
+ case monaco.MarkerSeverity.Error: return 'error';
+ case monaco.MarkerSeverity.Warning: return 'warning';
+ case monaco.MarkerSeverity.Info: return 'info';
+ default: return 'hint';
+ }
+}
+
+function markersTodiagnostics(model: monaco.editor.ITextModel): EditorDiagnostic[] {
+ return monaco.editor.getModelMarkers({ resource: model.uri }).map((m) => ({
+ message: m.message,
+ severity: severityToString(m.severity),
+ startLineNumber: m.startLineNumber,
+ startColumn: m.startColumn,
+ endLineNumber: m.endLineNumber,
+ endColumn: m.endColumn,
+ source: m.source ?? undefined,
+ code: typeof m.code === 'string' ? m.code : m.code?.value,
+ }));
+}
+
+// ---------------------------------------------------------------------------
+// Component
+// ---------------------------------------------------------------------------
+
+export const CodeEditor = forwardRef(
+ function CodeEditor(
+ {
+ value,
+ onChange,
+ onMount: onMountProp,
+ onDiagnostics,
+ onCursorChange,
+ language,
+ filename,
+ readOnly = false,
+ lineNumbers = true,
+ minimap = false,
+ wordWrap = false,
+ tabSize = 2,
+ diagnostics = false,
+ quickSuggestions = false,
+ syntaxHighlighting = true,
+ height = '100%',
+ width = '100%',
+ className,
+ },
+ ref,
+ ) {
+ const theme = useEditorTheme();
+ const editorRef = useRef(null);
+ const containerRef = useRef(null);
+ const [isReady, setIsReady] = useState(false);
+ const preventTriggerChangeEvent = useRef(false);
+ const subscriptionRef = useRef(null);
+ const markerSubscriptionRef = useRef(null);
+ const cursorSubscriptionRef = useRef(null);
+
+ const resolvedLanguage = language ?? (filename ? detectLanguage(filename) : undefined);
+
+ log('render — filename:', filename, '| resolvedLanguage:', resolvedLanguage);
+
+ // Pretty-print JSON when the language is JSON
+ const displayValue = useMemo(() => {
+ if (resolvedLanguage === 'json' && value) {
+ try {
+ return JSON.stringify(JSON.parse(value), null, 2);
+ } catch {
+ return value;
+ }
+ }
+ return value;
+ }, [resolvedLanguage, value]);
+
+ // -----------------------------------------------------------------------
+ // Mount / Unmount
+ // -----------------------------------------------------------------------
+
+ useEffect(() => {
+ if (!containerRef.current) return;
+
+ log('=== MOUNT ===');
+
+ // Theme
+ monaco.editor.defineTheme(OV_MONACO_THEME, buildMonacoTheme(theme));
+ monaco.editor.setTheme(OV_MONACO_THEME);
+
+ // TypeScript / JavaScript compiler options (global — JSX & target config)
+ monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
+ jsx: 2, // JsxEmit.React
+ jsxFactory: 'React.createElement',
+ allowNonTsExtensions: true,
+ allowJs: true,
+ target: 99, // ESNext
+ });
+
+ // Model — the URI drives schema fileMatch
+ const model = getOrCreateModel(
+ displayValue,
+ syntaxHighlighting ? resolvedLanguage : 'plaintext',
+ filename,
+ );
+ log(' model created — uri:', model.uri.toString());
+
+ const editor = monaco.editor.create(containerRef.current, {
+ model,
+ readOnly,
+ lineNumbers: lineNumbers ? 'on' : 'off',
+ minimap: { enabled: minimap },
+ wordWrap: wordWrap ? 'on' : 'off',
+ quickSuggestions: quickSuggestions
+ ? { other: true, strings: true, comments: false }
+ : false,
+ scrollBeyondLastLine: false,
+ automaticLayout: true,
+ fontSize: 13,
+ fontFamily: 'var(--ov-font-family-mono, monospace)',
+ tabSize,
+ renderValidationDecorations: diagnostics ? 'on' : 'off',
+ });
+
+ editorRef.current = editor;
+
+ // Apply registered schemas so completions/validation are live
+ editorSchemas.applyYamlSchemas();
+ editorSchemas.applyJsonSchemas(monaco);
+
+ onMountProp?.(editor, monaco);
+ setIsReady(true);
+ log('=== MOUNT COMPLETE ===');
+
+ return () => {
+ log('=== UNMOUNT ===');
+ subscriptionRef.current?.dispose();
+ subscriptionRef.current = null;
+ markerSubscriptionRef.current?.dispose();
+ markerSubscriptionRef.current = null;
+ cursorSubscriptionRef.current?.dispose();
+ cursorSubscriptionRef.current = null;
+ // Detach model before disposing to avoid WordHighlighter "Canceled" errors
+ const m = editor.getModel();
+ editor.setModel(null);
+ m?.dispose();
+ editor.dispose();
+ editorRef.current = null;
+ setIsReady(false);
+ };
+ // Intentional mount-once effect — props like value, language, readOnly,
+ // theme, filename, and callbacks are synced via dedicated effects below.
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ // -----------------------------------------------------------------------
+ // Prop → editor syncing
+ // -----------------------------------------------------------------------
+
+ // Value
+ useEffect(() => {
+ const editor = editorRef.current;
+ if (!editor || !isReady || displayValue === undefined) return;
+ if (editor.getOption(monaco.editor.EditorOption.readOnly)) {
+ editor.setValue(displayValue);
+ } else if (displayValue !== editor.getValue()) {
+ const model = editor.getModel();
+ if (!model) return;
+ preventTriggerChangeEvent.current = true;
+ editor.executeEdits('', [
+ {
+ range: model.getFullModelRange(),
+ text: displayValue,
+ forceMoveMarkers: true,
+ },
+ ]);
+ editor.pushUndoStop();
+ preventTriggerChangeEvent.current = false;
+ }
+ }, [displayValue, isReady]);
+
+ // Options
+ useEffect(() => {
+ if (!editorRef.current || !isReady) return;
+ editorRef.current.updateOptions({
+ readOnly,
+ lineNumbers: lineNumbers ? 'on' : 'off',
+ minimap: { enabled: minimap },
+ wordWrap: wordWrap ? 'on' : 'off',
+ quickSuggestions: quickSuggestions
+ ? { other: true, strings: true, comments: false }
+ : false,
+ tabSize,
+ renderValidationDecorations: diagnostics ? 'on' : 'off',
+ });
+ }, [readOnly, lineNumbers, minimap, wordWrap, quickSuggestions, tabSize, diagnostics, isReady]);
+
+ // Language (including syntax highlighting toggle)
+ useEffect(() => {
+ if (!editorRef.current || !isReady) return;
+ const model = editorRef.current.getModel();
+ if (model) {
+ const lang = syntaxHighlighting ? (resolvedLanguage ?? 'plaintext') : 'plaintext';
+ log('setModelLanguage →', lang);
+ monaco.editor.setModelLanguage(model, lang);
+ }
+ }, [resolvedLanguage, syntaxHighlighting, isReady]);
+
+ // Filename → model URI swap (for schema fileMatch)
+ const prevFilenameRef = useRef(filename);
+ useEffect(() => {
+ const editor = editorRef.current;
+ if (!editor || !isReady) return;
+ if (prevFilenameRef.current === filename) return;
+ log('filename changed:', prevFilenameRef.current, '→', filename);
+ prevFilenameRef.current = filename;
+
+ const currentModel = editor.getModel();
+ const currentValue = currentModel?.getValue?.() ?? displayValue;
+ // Detach model before disposing to avoid WordHighlighter "Canceled" errors
+ editor.setModel(null);
+ currentModel?.dispose();
+ const newModel = getOrCreateModel(currentValue, syntaxHighlighting ? resolvedLanguage : 'plaintext', filename);
+ editor.setModel(newModel);
+ log(' new model set — uri:', newModel.uri.toString());
+ }, [filename, isReady, resolvedLanguage, syntaxHighlighting, displayValue]);
+
+ // Theme
+ useEffect(() => {
+ if (!isReady) return;
+ monaco.editor.defineTheme(OV_MONACO_THEME, buildMonacoTheme(theme));
+ monaco.editor.setTheme(OV_MONACO_THEME);
+ }, [theme, isReady]);
+
+ // onChange subscription
+ useEffect(() => {
+ if (!editorRef.current || !isReady) return;
+ subscriptionRef.current?.dispose();
+ if (onChange) {
+ subscriptionRef.current = editorRef.current.onDidChangeModelContent(() => {
+ if (!preventTriggerChangeEvent.current) {
+ onChange(editorRef.current!.getValue());
+ }
+ });
+ }
+ }, [onChange, isReady]);
+
+ // onDiagnostics subscription — listen to marker changes on the model
+ useEffect(() => {
+ if (!isReady) return;
+ markerSubscriptionRef.current?.dispose();
+ if (onDiagnostics) {
+ markerSubscriptionRef.current = monaco.editor.onDidChangeMarkers((uris) => {
+ const model = editorRef.current?.getModel();
+ if (!model) return;
+ const modelUri = model.uri.toString();
+ if (uris.some((u) => u.toString() === modelUri)) {
+ onDiagnostics(markersTodiagnostics(model));
+ }
+ });
+ }
+ }, [onDiagnostics, isReady]);
+
+ // onCursorChange subscription
+ useEffect(() => {
+ if (!editorRef.current || !isReady) return;
+ cursorSubscriptionRef.current?.dispose();
+ if (onCursorChange) {
+ cursorSubscriptionRef.current = editorRef.current.onDidChangeCursorPosition((e) => {
+ onCursorChange({
+ lineNumber: e.position.lineNumber,
+ column: e.position.column,
+ });
+ });
+ }
+ }, [onCursorChange, isReady]);
+
+ // Live schema updates — re-apply whenever the registry changes
+ useEffect(() => {
+ if (!isReady) return;
+ return editorSchemas.onChange(() => {
+ editorSchemas.applyYamlSchemas();
+ editorSchemas.applyJsonSchemas(monaco);
+ });
+ }, [isReady]);
+
+ // -----------------------------------------------------------------------
+ // Imperative handle
+ // -----------------------------------------------------------------------
+
+ useImperativeHandle(ref, () => ({
+ getEditor: () => editorRef.current,
+ getMonaco: () => monaco,
+ focus: () => editorRef.current?.focus(),
+ getDiagnostics: () => {
+ const model = editorRef.current?.getModel();
+ return model ? markersTodiagnostics(model) : [];
+ },
+ getDebugState: (): EditorDebugState => {
+ const editor = editorRef.current;
+ const model = editor?.getModel() ?? null;
+ const markers = model
+ ? monaco.editor.getModelMarkers({ resource: model.uri })
+ : [];
+ const modelUri = model?.uri.toString() ?? null;
+
+ // Diagnostics breakdown
+ const diagnosticsBySeverity: Record = {
+ error: 0, warning: 0, info: 0, hint: 0,
+ };
+ const diagnosticsBySource: Record = {};
+ for (const m of markers) {
+ const sev = severityToString(m.severity);
+ diagnosticsBySeverity[sev]++;
+ const src = m.source ?? 'unknown';
+ diagnosticsBySource[src] = (diagnosticsBySource[src] ?? 0) + 1;
+ }
+
+ // Schema matching
+ const matchingSchemas = modelUri
+ ? editorSchemas.getSchemasForUri(modelUri).map((s) => ({
+ uri: s.uri,
+ name: s.name,
+ fileMatch: s.fileMatch,
+ }))
+ : [];
+
+ const pos = editor?.getPosition() ?? null;
+ const sels = editor?.getSelections() ?? [];
+
+ return {
+ modelUri,
+ modelLanguage: model?.getLanguageId() ?? null,
+ modelLineCount: model?.getLineCount() ?? 0,
+ modelContentLength: model?.getValueLength() ?? 0,
+ modelVersionId: model?.getVersionId() ?? 0,
+ modelEOL: model?.getEOL() ?? null,
+ cursorPosition: pos ? { lineNumber: pos.lineNumber, column: pos.column } : null,
+ selections: sels.map((s) => ({
+ startLineNumber: s.startLineNumber,
+ startColumn: s.startColumn,
+ endLineNumber: s.endLineNumber,
+ endColumn: s.endColumn,
+ })),
+ diagnosticsTotal: markers.length,
+ diagnosticsBySeverity,
+ diagnosticsBySource,
+ registeredSchemaCount: editorSchemas.schemas.length,
+ matchingSchemas,
+ isReadOnly: editor?.getOption(monaco.editor.EditorOption.readOnly) ?? false,
+ isFocused: editor?.hasWidgetFocus() ?? false,
+ totalModelCount: monaco.editor.getModels().length,
+ registeredLanguageCount: monaco.languages.getLanguages().length,
+ yamlHandleSet: editorSchemas.isYamlReady,
+ };
+ },
+ }));
+
+ // -----------------------------------------------------------------------
+ // Render
+ // -----------------------------------------------------------------------
+
+ return (
+
+ );
+ },
+);
+
+CodeEditor.displayName = 'CodeEditor';
diff --git a/packages/editors/src/components/code-editor/index.ts b/packages/editors/src/components/code-editor/index.ts
new file mode 100644
index 0000000..c65fb66
--- /dev/null
+++ b/packages/editors/src/components/code-editor/index.ts
@@ -0,0 +1,9 @@
+export { CodeEditor } from './CodeEditor';
+export type {
+ CodeEditorProps,
+ CodeEditorHandle,
+ EditorDiagnostic,
+ DiagnosticSeverity,
+ CursorPosition,
+ EditorDebugState,
+} from './CodeEditor';
diff --git a/packages/editors/src/components/command-palette/CommandPalette.module.css b/packages/editors/src/components/command-palette/CommandPalette.module.css
new file mode 100644
index 0000000..d8765ed
--- /dev/null
+++ b/packages/editors/src/components/command-palette/CommandPalette.module.css
@@ -0,0 +1,31 @@
+.Overlay {
+ position: fixed;
+ inset: 0;
+ z-index: var(--ov-z-modal, 1000);
+ display: flex;
+ justify-content: center;
+ padding-top: 20vh;
+ background: var(--ov-color-overlay-bg, rgba(0, 0, 0, 0.4));
+}
+
+.Root {
+ width: min(600px, 90vw);
+ max-height: 60vh;
+ display: flex;
+ flex-direction: column;
+ background: var(--ov-color-quickpick-bg, var(--ov-color-bg-surface));
+ border: 1px solid var(--ov-color-quickpick-border, var(--ov-color-border-default));
+ border-radius: var(--ov-radius-md, 8px);
+ box-shadow: var(--ov-color-quickpick-shadow, 0 8px 32px rgba(0, 0, 0, 0.4));
+ overflow: hidden;
+}
+
+/* CommandList fills the palette; its results scroll within the palette's max-height. */
+.CommandList {
+ min-height: 0;
+ flex: 1 1 auto;
+}
+
+.Results {
+ max-block-size: none;
+}
diff --git a/packages/editors/src/components/command-palette/CommandPalette.stories.tsx b/packages/editors/src/components/command-palette/CommandPalette.stories.tsx
new file mode 100644
index 0000000..5901a6d
--- /dev/null
+++ b/packages/editors/src/components/command-palette/CommandPalette.stories.tsx
@@ -0,0 +1,251 @@
+import { useState } from 'react';
+import type { Meta, StoryObj } from '@storybook/react';
+import { CommandPalette, type CommandItem, type CommandPaletteProps } from './CommandPalette';
+
+const sampleCommands: CommandItem[] = [
+ { id: 'new-file', label: 'New File', shortcut: 'Ctrl+N', group: 'File' },
+ { id: 'open-file', label: 'Open File', shortcut: 'Ctrl+O', group: 'File' },
+ { id: 'save', label: 'Save', shortcut: 'Ctrl+S', group: 'File' },
+ { id: 'save-as', label: 'Save As…', shortcut: 'Ctrl+Shift+S', group: 'File' },
+ { id: 'close-tab', label: 'Close Tab', shortcut: 'Ctrl+W', group: 'File' },
+ { id: 'find', label: 'Find', shortcut: 'Ctrl+F', group: 'Edit' },
+ { id: 'replace', label: 'Replace', shortcut: 'Ctrl+H', group: 'Edit' },
+ { id: 'undo', label: 'Undo', shortcut: 'Ctrl+Z', group: 'Edit' },
+ { id: 'redo', label: 'Redo', shortcut: 'Ctrl+Shift+Z', group: 'Edit' },
+ { id: 'find-files', label: 'Find in Files', shortcut: 'Ctrl+Shift+F', group: 'Search' },
+ { id: 'toggle-terminal', label: 'Toggle Terminal', shortcut: 'Ctrl+`', group: 'View' },
+ { id: 'toggle-sidebar', label: 'Toggle Sidebar', shortcut: 'Ctrl+B', group: 'View' },
+ { id: 'zoom-in', label: 'Zoom In', shortcut: 'Ctrl++', group: 'View' },
+ { id: 'zoom-out', label: 'Zoom Out', shortcut: 'Ctrl+-', group: 'View' },
+ { id: 'git-commit', label: 'Git: Commit', group: 'Source Control' },
+ { id: 'git-push', label: 'Git: Push', group: 'Source Control' },
+ { id: 'git-pull', label: 'Git: Pull', group: 'Source Control' },
+ { id: 'git-stash', label: 'Git: Stash Changes', group: 'Source Control' },
+];
+
+const meta: Meta = {
+ title: 'Editors/CommandPalette',
+ component: CommandPalette,
+ tags: ['autodocs'],
+ args: {
+ open: true,
+ onClose: () => {},
+ onSelect: () => {},
+ commands: sampleCommands,
+ placeholder: 'Type a command…',
+ },
+ argTypes: {
+ open: { table: { disable: true } },
+ placeholder: { control: 'text' },
+ },
+};
+
+export default meta;
+type Story = StoryObj;
+
+function PlaygroundStory(args: CommandPaletteProps) {
+ const [open, setOpen] = useState(true);
+ const [lastSelected, setLastSelected] = useState(null);
+ return (
+
+
setOpen(true)}>Open Command Palette
+ {lastSelected && (
+
Last selected: {lastSelected}
+ )}
+
setOpen(false)}
+ onSelect={(cmd) => {
+ setLastSelected(cmd.label);
+ setOpen(false);
+ }}
+ />
+
+ );
+}
+
+export const Playground: Story = {
+ render: (args) => ,
+};
+
+function ManyCommandsStory(args: CommandPaletteProps) {
+ const [open, setOpen] = useState(true);
+ const manyCommands = Array.from({ length: 100 }, (_, i) => ({
+ id: `cmd-${i}`,
+ label: `Command ${i + 1}`,
+ description: `Description for command ${i + 1}`,
+ group: `Group ${Math.floor(i / 10) + 1}`,
+ }));
+ return (
+
+ setOpen(true)}>Open (100 commands)
+ setOpen(false)}
+ onSelect={() => setOpen(false)}
+ />
+
+ );
+}
+
+export const WithManyCommands: Story = {
+ render: (args) => ,
+};
+
+/** Commands with descriptions showing additional context. */
+function WithDescriptionsStory(args: CommandPaletteProps) {
+ const [open, setOpen] = useState(true);
+ const commands: CommandItem[] = [
+ {
+ id: 'k8s-pods',
+ label: 'Kubernetes: List Pods',
+ description: 'Show all pods in the current namespace',
+ group: 'Kubernetes',
+ },
+ {
+ id: 'k8s-deploy',
+ label: 'Kubernetes: Create Deployment',
+ description: 'Deploy a new application to the cluster',
+ group: 'Kubernetes',
+ },
+ {
+ id: 'k8s-logs',
+ label: 'Kubernetes: View Logs',
+ description: 'Stream logs from a running pod',
+ group: 'Kubernetes',
+ },
+ {
+ id: 'docker-build',
+ label: 'Docker: Build Image',
+ description: 'Build a container image from Dockerfile',
+ group: 'Docker',
+ },
+ {
+ id: 'docker-push',
+ label: 'Docker: Push Image',
+ description: 'Push image to container registry',
+ group: 'Docker',
+ },
+ ];
+ return (
+
+ setOpen(true)}>Open
+ setOpen(false)}
+ onSelect={() => setOpen(false)}
+ />
+
+ );
+}
+
+export const WithDescriptions: Story = {
+ render: (args) => ,
+};
+
+/** Demonstrates disabled commands being filtered out. */
+function WithDisabledStory(args: CommandPaletteProps) {
+ const [open, setOpen] = useState(true);
+ const commands: CommandItem[] = [
+ { id: 'save', label: 'Save', shortcut: 'Ctrl+S', group: 'File' },
+ { id: 'save-as', label: 'Save As…', shortcut: 'Ctrl+Shift+S', group: 'File' },
+ { id: 'revert', label: 'Revert File', group: 'File', disabled: true },
+ { id: 'close', label: 'Close', shortcut: 'Ctrl+W', group: 'File' },
+ { id: 'deploy', label: 'Deploy to Staging', group: 'Actions', disabled: true },
+ { id: 'publish', label: 'Publish Package', group: 'Actions', disabled: true },
+ { id: 'test', label: 'Run Tests', shortcut: 'Ctrl+Shift+T', group: 'Actions' },
+ ];
+ return (
+
+ setOpen(true)}>Open (disabled items hidden)
+ setOpen(false)}
+ onSelect={() => setOpen(false)}
+ />
+
+ );
+}
+
+export const WithDisabledCommands: Story = {
+ render: (args) => ,
+};
+
+/** Empty command list edge case. */
+function EmptyStory(args: CommandPaletteProps) {
+ const [open, setOpen] = useState(true);
+ return (
+
+ setOpen(true)}>Open (empty)
+ setOpen(false)}
+ onSelect={() => setOpen(false)}
+ />
+
+ );
+}
+
+export const EmptyCommands: Story = {
+ render: (args) => ,
+};
+
+/** Commands without groups — flat list. */
+function UngroupedStory(args: CommandPaletteProps) {
+ const [open, setOpen] = useState(true);
+ const commands: CommandItem[] = [
+ { id: 'action-1', label: 'Format Document', shortcut: 'Shift+Alt+F' },
+ { id: 'action-2', label: 'Toggle Word Wrap', shortcut: 'Alt+Z' },
+ { id: 'action-3', label: 'Sort Lines Ascending' },
+ { id: 'action-4', label: 'Sort Lines Descending' },
+ { id: 'action-5', label: 'Transform to Uppercase' },
+ { id: 'action-6', label: 'Transform to Lowercase' },
+ { id: 'action-7', label: 'Trim Trailing Whitespace' },
+ ];
+ return (
+
+ setOpen(true)}>Open (ungrouped)
+ setOpen(false)}
+ onSelect={() => setOpen(false)}
+ />
+
+ );
+}
+
+export const UngroupedCommands: Story = {
+ render: (args) => ,
+};
+
+/** Custom placeholder text. */
+function CustomPlaceholderStory(args: CommandPaletteProps) {
+ const [open, setOpen] = useState(true);
+ return (
+
+ setOpen(true)}>Open
+ setOpen(false)}
+ onSelect={() => setOpen(false)}
+ />
+
+ );
+}
+
+export const CustomPlaceholder: Story = {
+ render: (args) => ,
+};
diff --git a/packages/editors/src/components/command-palette/CommandPalette.test.tsx b/packages/editors/src/components/command-palette/CommandPalette.test.tsx
new file mode 100644
index 0000000..b00f965
--- /dev/null
+++ b/packages/editors/src/components/command-palette/CommandPalette.test.tsx
@@ -0,0 +1,395 @@
+import { render, screen, fireEvent } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { describe, expect, it, vi } from 'vitest';
+import { CommandPalette, type CommandItem } from './CommandPalette';
+
+// ---------------------------------------------------------------------------
+// Mock @omniview/base-ui — CommandList compound component
+// ---------------------------------------------------------------------------
+
+vi.mock('@omniview/base-ui', async () => {
+ const React = await vi.importActual('react');
+
+ // Simple CommandList mock that renders items directly and supports filtering
+ function MockRoot({
+ items,
+ itemKey,
+ renderItem,
+ filterFn,
+ groupBy,
+ onAction,
+ onDismiss,
+ placeholder,
+ children,
+ }: {
+ items: CommandItem[];
+ itemKey: (item: CommandItem) => string;
+ renderItem: (item: CommandItem, meta: { key: string; isActive: boolean; isDisabled: boolean }) => React.ReactNode;
+ filterFn?: (item: CommandItem, query: string) => boolean;
+ groupBy?: (item: CommandItem) => string;
+ onAction?: (key: string, item: CommandItem) => void;
+ onDismiss?: () => void;
+ placeholder?: string;
+ children?: React.ReactNode;
+ }) {
+ const [query, setQuery] = React.useState('');
+ const [activeIndex, setActiveIndex] = React.useState(0);
+
+ const filtered = React.useMemo(() => {
+ if (!query || !filterFn) return items;
+ return items.filter((item) => filterFn(item, query));
+ }, [items, query, filterFn]);
+
+ // Group items
+ const groups = React.useMemo(() => {
+ if (!groupBy) return new Map([['', filtered]]);
+ const map = new Map();
+ for (const item of filtered) {
+ const group = groupBy(item);
+ const list = map.get(group);
+ if (list) list.push(item);
+ else map.set(group, [item]);
+ }
+ return map;
+ }, [filtered, groupBy]);
+
+ const handleKeyDown = React.useCallback(
+ (e: React.KeyboardEvent) => {
+ switch (e.key) {
+ case 'ArrowDown':
+ e.preventDefault();
+ setActiveIndex((i: number) => (i + 1) % Math.max(filtered.length, 1));
+ break;
+ case 'ArrowUp':
+ e.preventDefault();
+ setActiveIndex((i: number) => (i - 1 + filtered.length) % Math.max(filtered.length, 1));
+ break;
+ case 'Enter': {
+ e.preventDefault();
+ const selected = filtered[activeIndex];
+ if (selected && onAction) onAction(itemKey(selected), selected);
+ break;
+ }
+ case 'Escape':
+ e.preventDefault();
+ onDismiss?.();
+ break;
+ }
+ },
+ [filtered, activeIndex, onAction, onDismiss, itemKey],
+ );
+
+ // Provide context to children via data attributes on root
+ const contextValue = React.useMemo(
+ () => ({ query, setQuery, setActiveIndex, handleKeyDown, placeholder }),
+ [query, handleKeyDown, placeholder],
+ );
+
+ let flatIndex = 0;
+
+ // Build results content
+ const resultsContent = filtered.length > 0 ? (
+ Array.from(groups.entries()).map(([group, groupItems]: [string, CommandItem[]]) => (
+
+ {group &&
{group}
}
+ {groupItems.map((item: CommandItem) => {
+ const idx = flatIndex++;
+ return renderItem(item, {
+ key: itemKey(item),
+ isActive: idx === activeIndex,
+ isDisabled: false,
+ });
+ })}
+
+ ))
+ ) : null;
+
+ return (
+
+
+ {React.Children.map(children, (child: React.ReactNode) => {
+ if (!React.isValidElement(child)) return child;
+ const displayName = (child.type as { displayName?: string })?.displayName;
+ if (displayName === 'CommandList.Input') return child;
+ if (displayName === 'CommandList.Results') {
+ const childProps = child.props as Record
;
+ return resultsContent ? (
+ {resultsContent}
+ ) : null;
+ }
+ if (displayName === 'CommandList.Empty') {
+ return filtered.length === 0 ? child : null;
+ }
+ return child;
+ })}
+
+
+ );
+ }
+ MockRoot.displayName = 'CommandList.Root';
+
+ const MockRootContext = React.createContext<{
+ query: string;
+ setQuery: (q: string) => void;
+ setActiveIndex: (i: number) => void;
+ handleKeyDown: (e: React.KeyboardEvent) => void;
+ placeholder?: string;
+ } | null>(null);
+
+ function MockInput(props: Record) {
+ const ctx = React.useContext(MockRootContext);
+ return (
+ ) => {
+ ctx?.setQuery(e.target.value);
+ ctx?.setActiveIndex(0);
+ }}
+ onKeyDown={ctx?.handleKeyDown}
+ placeholder={ctx?.placeholder}
+ {...props}
+ />
+ );
+ }
+ MockInput.displayName = 'CommandList.Input';
+
+ function MockResults(props: Record) {
+ return
;
+ }
+ MockResults.displayName = 'CommandList.Results';
+
+ function MockItem({ children, itemKey: _itemKey, ...props }: Record) {
+ return (
+
+ {children as React.ReactNode}
+
+ );
+ }
+ MockItem.displayName = 'CommandList.Item';
+
+ function MockItemIcon({ children, ...props }: Record) {
+ return {children as React.ReactNode} ;
+ }
+ MockItemIcon.displayName = 'CommandList.ItemIcon';
+
+ function MockItemLabel({ children, ...props }: Record) {
+ return {children as React.ReactNode} ;
+ }
+ MockItemLabel.displayName = 'CommandList.ItemLabel';
+
+ function MockItemDescription({ children, ...props }: Record) {
+ return {children as React.ReactNode} ;
+ }
+ MockItemDescription.displayName = 'CommandList.ItemDescription';
+
+ function MockItemShortcut({ children, ...props }: Record) {
+ return {children as React.ReactNode} ;
+ }
+ MockItemShortcut.displayName = 'CommandList.ItemShortcut';
+
+ function MockEmpty({ children, ...props }: Record) {
+ return {children as React.ReactNode}
;
+ }
+ MockEmpty.displayName = 'CommandList.Empty';
+
+ function MockLoading({ children, ...props }: Record) {
+ return {children as React.ReactNode}
;
+ }
+ MockLoading.displayName = 'CommandList.Loading';
+
+ const CommandList = Object.assign(MockRoot, {
+ Root: MockRoot,
+ Input: MockInput,
+ Results: MockResults,
+ Item: MockItem,
+ ItemIcon: MockItemIcon,
+ ItemLabel: MockItemLabel,
+ ItemDescription: MockItemDescription,
+ ItemShortcut: MockItemShortcut,
+ Empty: MockEmpty,
+ Loading: MockLoading,
+ });
+
+ return { CommandList };
+});
+
+// ---------------------------------------------------------------------------
+// Test data
+// ---------------------------------------------------------------------------
+
+const commands: CommandItem[] = [
+ { id: 'open', label: 'Open File', shortcut: 'Ctrl+O', group: 'File' },
+ { id: 'save', label: 'Save File', shortcut: 'Ctrl+S', group: 'File' },
+ { id: 'find', label: 'Find in Files', shortcut: 'Ctrl+Shift+F', group: 'Search' },
+ {
+ id: 'terminal',
+ label: 'Toggle Terminal',
+ shortcut: 'Ctrl+`',
+ group: 'View',
+ description: 'Show or hide the integrated terminal',
+ },
+ { id: 'disabled', label: 'Disabled Command', disabled: true },
+];
+
+// ---------------------------------------------------------------------------
+// Tests
+// ---------------------------------------------------------------------------
+
+describe('CommandPalette', () => {
+ const defaultProps = {
+ open: true,
+ onClose: vi.fn(),
+ commands,
+ onSelect: vi.fn(),
+ };
+
+ it('renders when open', () => {
+ render( );
+ expect(screen.getByTestId('command-palette')).toBeInTheDocument();
+ });
+
+ it('does not render when closed', () => {
+ render( );
+ expect(screen.queryByTestId('command-palette')).not.toBeInTheDocument();
+ });
+
+ it('shows command items', () => {
+ render( );
+ expect(screen.getByTestId('command-item-open')).toBeInTheDocument();
+ expect(screen.getByTestId('command-item-save')).toBeInTheDocument();
+ expect(screen.getByTestId('command-item-find')).toBeInTheDocument();
+ });
+
+ it('filters out disabled commands', () => {
+ render( );
+ expect(screen.queryByTestId('command-item-disabled')).not.toBeInTheDocument();
+ });
+
+ it('filters commands by search text', async () => {
+ const user = userEvent.setup();
+ render( );
+ const input = screen.getByTestId('command-palette-input');
+ await user.type(input, 'terminal');
+ expect(screen.getByTestId('command-item-terminal')).toBeInTheDocument();
+ expect(screen.queryByTestId('command-item-open')).not.toBeInTheDocument();
+ });
+
+ it('filters by description', async () => {
+ const user = userEvent.setup();
+ render( );
+ await user.type(screen.getByTestId('command-palette-input'), 'integrated');
+ expect(screen.getByTestId('command-item-terminal')).toBeInTheDocument();
+ });
+
+ it('filters by group name', async () => {
+ const user = userEvent.setup();
+ render( );
+ await user.type(screen.getByTestId('command-palette-input'), 'search');
+ expect(screen.getByTestId('command-item-find')).toBeInTheDocument();
+ });
+
+ it('shows empty state when no matches', async () => {
+ const user = userEvent.setup();
+ render( );
+ await user.type(screen.getByTestId('command-palette-input'), 'zzzzz');
+ expect(screen.getByTestId('command-palette-empty')).toBeInTheDocument();
+ });
+
+ it('selects command on Enter after arrow navigation', () => {
+ const onSelect = vi.fn();
+ const onClose = vi.fn();
+ render( );
+ const input = screen.getByTestId('command-palette-input');
+ fireEvent.keyDown(input, { key: 'ArrowDown' });
+ fireEvent.keyDown(input, { key: 'Enter' });
+ expect(onSelect).toHaveBeenCalledWith(expect.objectContaining({ id: 'save' }));
+ expect(onClose).toHaveBeenCalled();
+ });
+
+ it('closes on Escape', () => {
+ const onClose = vi.fn();
+ render( );
+ fireEvent.keyDown(screen.getByTestId('command-palette-input'), { key: 'Escape' });
+ expect(onClose).toHaveBeenCalled();
+ });
+
+ it('closes on overlay click', () => {
+ const onClose = vi.fn();
+ render( );
+ fireEvent.click(screen.getByTestId('command-palette-overlay'));
+ expect(onClose).toHaveBeenCalled();
+ });
+
+ it('does not propagate click from dialog to overlay', () => {
+ const onClose = vi.fn();
+ render( );
+ fireEvent.click(screen.getByTestId('command-palette'));
+ expect(onClose).not.toHaveBeenCalled();
+ });
+
+ it('renders group labels', () => {
+ render( );
+ expect(screen.getByText('File')).toBeInTheDocument();
+ expect(screen.getByText('Search')).toBeInTheDocument();
+ expect(screen.getByText('View')).toBeInTheDocument();
+ });
+
+ it('displays keyboard shortcuts', () => {
+ render( );
+ expect(screen.getByText('Ctrl+O')).toBeInTheDocument();
+ });
+
+ it('shows description text for items that have one', () => {
+ render( );
+ expect(screen.getByText('Show or hide the integrated terminal')).toBeInTheDocument();
+ });
+
+ it('uses custom placeholder text', () => {
+ render( );
+ expect(screen.getByPlaceholderText('Search resources…')).toBeInTheDocument();
+ });
+
+ it('has proper dialog accessibility attributes', () => {
+ render( );
+ const dialog = screen.getByTestId('command-palette');
+ expect(dialog).toHaveAttribute('role', 'dialog');
+ expect(dialog).toHaveAttribute('aria-modal', 'true');
+ expect(dialog).toHaveAttribute('aria-label', 'Command palette');
+ });
+
+ it('handles empty commands array', () => {
+ render( );
+ expect(screen.getByTestId('command-palette-empty')).toBeInTheDocument();
+ });
+
+ it('wraps ArrowDown from last to first item', () => {
+ const onSelect = vi.fn();
+ render( );
+ const input = screen.getByTestId('command-palette-input');
+ // 4 non-disabled commands, press down 4 times to wrap
+ for (let i = 0; i < 4; i++) {
+ fireEvent.keyDown(input, { key: 'ArrowDown' });
+ }
+ fireEvent.keyDown(input, { key: 'Enter' });
+ expect(onSelect).toHaveBeenCalledWith(expect.objectContaining({ id: 'open' }));
+ });
+
+ it('wraps ArrowUp from first to last item', () => {
+ const onSelect = vi.fn();
+ render( );
+ const input = screen.getByTestId('command-palette-input');
+ fireEvent.keyDown(input, { key: 'ArrowUp' });
+ fireEvent.keyDown(input, { key: 'Enter' });
+ expect(onSelect).toHaveBeenCalledWith(expect.objectContaining({ id: 'terminal' }));
+ });
+
+ it('does not call onSelect on Enter with empty results', async () => {
+ const user = userEvent.setup();
+ const onSelect = vi.fn();
+ render( );
+ await user.type(screen.getByTestId('command-palette-input'), 'zzzzz');
+ fireEvent.keyDown(screen.getByTestId('command-palette-input'), { key: 'Enter' });
+ expect(onSelect).not.toHaveBeenCalled();
+ });
+});
diff --git a/packages/editors/src/components/command-palette/CommandPalette.tsx b/packages/editors/src/components/command-palette/CommandPalette.tsx
new file mode 100644
index 0000000..396b1d9
--- /dev/null
+++ b/packages/editors/src/components/command-palette/CommandPalette.tsx
@@ -0,0 +1,116 @@
+import { forwardRef, useMemo, type ReactNode } from 'react';
+import { CommandList } from '@omniview/base-ui';
+import styles from './CommandPalette.module.css';
+
+export interface CommandItem {
+ id: string;
+ label: string;
+ description?: string;
+ icon?: ReactNode;
+ shortcut?: string;
+ group?: string;
+ disabled?: boolean;
+}
+
+export interface CommandPaletteProps {
+ open: boolean;
+ onClose: () => void;
+ commands: CommandItem[];
+ onSelect: (command: CommandItem) => void;
+ placeholder?: string;
+}
+
+function fuzzyMatch(text: string, query: string): boolean {
+ const lower = text.toLowerCase();
+ const q = query.toLowerCase();
+ let qi = 0;
+ for (let i = 0; i < lower.length && qi < q.length; i++) {
+ if (lower[i] === q[qi]) qi++;
+ }
+ return qi === q.length;
+}
+
+export const CommandPalette = forwardRef(
+ function CommandPalette(
+ { open, onClose, commands, onSelect, placeholder = 'Type a command…' },
+ ref,
+ ) {
+ const enabledCommands = useMemo(
+ () => commands.filter((c) => !c.disabled),
+ [commands],
+ );
+
+ if (!open) return null;
+
+ return (
+ {
+ if (e.key === 'Escape') onClose();
+ }}
+ data-testid="command-palette-overlay"
+ >
+
e.stopPropagation()}
+ onKeyDown={(e) => e.stopPropagation()}
+ role="dialog"
+ aria-modal="true"
+ aria-label="Command palette"
+ data-testid="command-palette"
+ >
+ cmd.id}
+ renderItem={(cmd, meta) => (
+
+ {cmd.icon && (
+ {cmd.icon}
+ )}
+ {cmd.label}
+ {cmd.description && (
+
+ {cmd.description}
+
+ )}
+ {cmd.shortcut && (
+
+ {cmd.shortcut}
+
+ )}
+
+ )}
+ filterFn={(cmd, query) =>
+ fuzzyMatch(cmd.label, query) ||
+ (cmd.description ? fuzzyMatch(cmd.description, query) : false) ||
+ (cmd.group ? fuzzyMatch(cmd.group, query) : false)
+ }
+ groupBy={(cmd) => cmd.group || ''}
+ onAction={(_key, cmd) => {
+ onSelect(cmd);
+ onClose();
+ }}
+ onDismiss={onClose}
+ placeholder={placeholder}
+ density="compact"
+ >
+
+
+
+ No matching commands
+
+
+
+
+ );
+ },
+);
+
+CommandPalette.displayName = 'CommandPalette';
diff --git a/packages/editors/src/components/command-palette/index.ts b/packages/editors/src/components/command-palette/index.ts
new file mode 100644
index 0000000..9c3711c
--- /dev/null
+++ b/packages/editors/src/components/command-palette/index.ts
@@ -0,0 +1,2 @@
+export { CommandPalette } from './CommandPalette';
+export type { CommandPaletteProps, CommandItem } from './CommandPalette';
diff --git a/packages/editors/src/components/diff-viewer/DiffViewer.module.css b/packages/editors/src/components/diff-viewer/DiffViewer.module.css
new file mode 100644
index 0000000..932d145
--- /dev/null
+++ b/packages/editors/src/components/diff-viewer/DiffViewer.module.css
@@ -0,0 +1,18 @@
+.Root {
+ position: relative;
+ overflow: hidden;
+ border-radius: var(--ov-radius-sm, 4px);
+ border: 1px solid var(--ov-color-editor-border, var(--ov-color-border-default));
+ background: var(--ov-color-editor-bg, var(--ov-color-bg-surface));
+}
+
+.Loading {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 100%;
+ width: 100%;
+ color: var(--ov-color-fg-muted);
+ font-family: var(--ov-font-family-sans, sans-serif);
+ font-size: var(--ov-font-size-sm, 13px);
+}
diff --git a/packages/editors/src/components/diff-viewer/DiffViewer.stories.tsx b/packages/editors/src/components/diff-viewer/DiffViewer.stories.tsx
new file mode 100644
index 0000000..707789c
--- /dev/null
+++ b/packages/editors/src/components/diff-viewer/DiffViewer.stories.tsx
@@ -0,0 +1,164 @@
+import type { Meta, StoryObj } from '@storybook/react';
+import { DiffViewer } from './DiffViewer';
+
+const original = `function greet(name) {
+ console.log("Hello, " + name);
+ return true;
+}`;
+
+const modified = `function greet(name: string) {
+ console.log(\`Hello, \${name}\`);
+ return name.length > 0;
+}`;
+
+const meta: Meta = {
+ title: 'Editors/DiffViewer',
+ component: DiffViewer,
+ tags: ['autodocs'],
+ args: {
+ original,
+ modified,
+ language: 'typescript',
+ height: 400,
+ },
+ argTypes: {
+ mode: { control: 'radio', options: ['side-by-side', 'inline'] },
+ readOnly: { control: 'boolean' },
+ language: {
+ control: 'select',
+ options: ['typescript', 'javascript', 'python', 'json', 'yaml', 'css', 'go'],
+ },
+ height: { control: { type: 'number', min: 200, max: 800, step: 50 } },
+ },
+};
+
+export default meta;
+type Story = StoryObj;
+
+export const Playground: Story = {};
+
+export const InlineMode: Story = {
+ args: { mode: 'inline' },
+};
+
+export const SideBySide: Story = {
+ args: { mode: 'side-by-side' },
+};
+
+export const Editable: Story = {
+ args: {
+ readOnly: false,
+ },
+};
+
+export const LargeDiff: Story = {
+ args: {
+ original: Array.from({ length: 50 }, (_, i) => `// Original line ${i + 1}`).join('\n'),
+ modified: Array.from({ length: 50 }, (_, i) =>
+ i % 5 === 0 ? `// Modified line ${i + 1}` : `// Original line ${i + 1}`,
+ ).join('\n'),
+ },
+};
+
+export const NoDifferences: Story = {
+ args: {
+ original: 'const x = 1;\nconst y = 2;\n',
+ modified: 'const x = 1;\nconst y = 2;\n',
+ language: 'typescript',
+ },
+};
+
+export const CompletelyDifferent: Story = {
+ args: {
+ original: `package main
+
+import "fmt"
+
+func main() {
+ fmt.Println("Hello, World!")
+}`,
+ modified: `from typing import Optional
+
+def greet(name: Optional[str] = None) -> str:
+ if name:
+ return f"Hello, {name}!"
+ return "Hello, World!"
+
+if __name__ == "__main__":
+ print(greet())`,
+ language: undefined,
+ },
+};
+
+export const JSONDiff: Story = {
+ args: {
+ original: JSON.stringify(
+ {
+ apiVersion: 'v1',
+ kind: 'ConfigMap',
+ metadata: { name: 'app-config', namespace: 'default' },
+ data: { DATABASE_URL: 'postgres://localhost:5432/mydb', LOG_LEVEL: 'info' },
+ },
+ null,
+ 2,
+ ),
+ modified: JSON.stringify(
+ {
+ apiVersion: 'v1',
+ kind: 'ConfigMap',
+ metadata: { name: 'app-config', namespace: 'production' },
+ data: {
+ DATABASE_URL: 'postgres://prod-host:5432/mydb',
+ LOG_LEVEL: 'warn',
+ CACHE_TTL: '3600',
+ },
+ },
+ null,
+ 2,
+ ),
+ language: 'json',
+ },
+};
+
+export const YAMLDiff: Story = {
+ args: {
+ original: `replicas: 1
+image: nginx:1.24
+resources:
+ limits:
+ cpu: "250m"
+ memory: "64Mi"
+env:
+ - name: NODE_ENV
+ value: development`,
+ modified: `replicas: 3
+image: nginx:1.25
+resources:
+ limits:
+ cpu: "500m"
+ memory: "128Mi"
+ requests:
+ cpu: "100m"
+ memory: "32Mi"
+env:
+ - name: NODE_ENV
+ value: production
+ - name: LOG_LEVEL
+ value: warn`,
+ language: 'yaml',
+ },
+};
+
+export const EmptyOriginal: Story = {
+ args: {
+ original: '',
+ modified: 'const newFile = true;\nexport default newFile;\n',
+ language: 'typescript',
+ },
+};
+
+export const CompactHeight: Story = {
+ args: {
+ height: 200,
+ },
+};
diff --git a/packages/editors/src/components/diff-viewer/DiffViewer.test.tsx b/packages/editors/src/components/diff-viewer/DiffViewer.test.tsx
new file mode 100644
index 0000000..6496b7f
--- /dev/null
+++ b/packages/editors/src/components/diff-viewer/DiffViewer.test.tsx
@@ -0,0 +1,161 @@
+import { render, screen } from '@testing-library/react';
+import { describe, expect, it, vi, beforeEach } from 'vitest';
+import { DiffViewer } from './DiffViewer';
+
+const mockCreateDiffEditor = vi.fn();
+const mockDefineTheme = vi.fn();
+const mockSetTheme = vi.fn();
+const mockSetModelLanguage = vi.fn();
+
+let mockDiffEditorInstance: Record;
+
+/** Shared model instances used by both createModel and getModel. */
+const sharedModels: Array>> = [];
+
+vi.mock('monaco-editor', () => ({
+ default: undefined,
+ Uri: {
+ parse: (s: string) => ({ toString: () => s, path: s }),
+ },
+ editor: {
+ createDiffEditor: (...args: unknown[]) => {
+ mockCreateDiffEditor(...args);
+ // DiffViewer creates original (index 0) then modified (index 1) via createModel,
+ // then calls setModel and later getModel — all must reference the same objects.
+ mockDiffEditorInstance = {
+ setModel: vi.fn(),
+ getModel: vi.fn(() => ({
+ original: sharedModels[0],
+ modified: sharedModels[1],
+ })),
+ getModifiedEditor: vi.fn(() => ({
+ getValue: () => sharedModels[1]?.getValue?.() ?? '',
+ })),
+ updateOptions: vi.fn(),
+ dispose: vi.fn(),
+ };
+ return mockDiffEditorInstance;
+ },
+ createModel: vi.fn(() => {
+ const model = {
+ setValue: vi.fn(),
+ getValue: vi.fn(() => ''),
+ dispose: vi.fn(),
+ };
+ sharedModels.push(model);
+ return model;
+ }),
+ getModel: vi.fn(() => null),
+ setModelLanguage: (...args: unknown[]) => mockSetModelLanguage(...args),
+ defineTheme: (...args: unknown[]) => mockDefineTheme(...args),
+ setTheme: (...args: unknown[]) => mockSetTheme(...args),
+ EditorOption: { readOnly: 81 },
+ },
+ languages: {
+ register: vi.fn(),
+ typescript: {
+ typescriptDefaults: {
+ setDiagnosticsOptions: vi.fn(),
+ setCompilerOptions: vi.fn(),
+ },
+ javascriptDefaults: {
+ setDiagnosticsOptions: vi.fn(),
+ },
+ },
+ },
+}));
+
+vi.mock('../../themes/useEditorTheme', () => ({
+ useEditorTheme: () => ({ isDark: false }),
+}));
+
+vi.mock('../../themes/monaco', () => ({
+ buildMonacoTheme: () => ({ base: 'vs', inherit: true, rules: [], colors: {} }),
+ OV_MONACO_THEME: 'ov-theme',
+}));
+
+beforeEach(() => {
+ vi.clearAllMocks();
+ sharedModels.length = 0;
+});
+
+describe('DiffViewer', () => {
+ it('renders the diff container', () => {
+ render( );
+ expect(screen.getByTestId('diff-viewer')).toBeInTheDocument();
+ });
+
+ it('creates a diff editor on mount', () => {
+ render( );
+ expect(mockCreateDiffEditor).toHaveBeenCalled();
+ });
+
+ it('applies language to both models', () => {
+ render( );
+ // Language sync effect calls setModelLanguage for both models
+ expect(mockSetModelLanguage).toHaveBeenCalledWith(
+ expect.anything(),
+ 'yaml',
+ );
+ // Called twice — once for original, once for modified
+ expect(mockSetModelLanguage).toHaveBeenCalledTimes(2);
+ });
+
+ it('defaults to side-by-side mode', () => {
+ render( );
+ expect(mockCreateDiffEditor).toHaveBeenCalledWith(
+ expect.anything(),
+ expect.objectContaining({ renderSideBySide: true }),
+ );
+ });
+
+ it('switches to inline mode', () => {
+ render( );
+ expect(mockCreateDiffEditor).toHaveBeenCalledWith(
+ expect.anything(),
+ expect.objectContaining({ renderSideBySide: false }),
+ );
+ });
+
+ it('merges className', () => {
+ render( );
+ expect(screen.getByTestId('diff-viewer')).toHaveClass('custom');
+ });
+
+ it('defaults to readOnly true', () => {
+ render( );
+ expect(mockCreateDiffEditor).toHaveBeenCalledWith(
+ expect.anything(),
+ expect.objectContaining({ readOnly: true }),
+ );
+ });
+
+ it('supports readOnly false', () => {
+ render( );
+ expect(mockCreateDiffEditor).toHaveBeenCalledWith(
+ expect.anything(),
+ expect.objectContaining({ readOnly: false }),
+ );
+ });
+
+ it('applies height prop as style', () => {
+ render( );
+ const container = screen.getByTestId('diff-viewer');
+ expect(container).toHaveStyle({ height: '500px' });
+ });
+
+ it('forwards ref', () => {
+ const ref = { current: null } as unknown as React.RefObject;
+ render( );
+ expect(ref.current).toBeInstanceOf(HTMLDivElement);
+ });
+
+ it('disposes editor and models on unmount', () => {
+ const { unmount } = render( );
+ const models = (mockDiffEditorInstance.getModel as ReturnType)();
+ unmount();
+ expect(models.original.dispose).toHaveBeenCalled();
+ expect(models.modified.dispose).toHaveBeenCalled();
+ expect(mockDiffEditorInstance.dispose).toHaveBeenCalled();
+ });
+});
diff --git a/packages/editors/src/components/diff-viewer/DiffViewer.tsx b/packages/editors/src/components/diff-viewer/DiffViewer.tsx
new file mode 100644
index 0000000..197bcf8
--- /dev/null
+++ b/packages/editors/src/components/diff-viewer/DiffViewer.tsx
@@ -0,0 +1,129 @@
+import { forwardRef, useEffect, useRef, useState } from 'react';
+import * as monaco from 'monaco-editor';
+import { useEditorTheme } from '../../themes/useEditorTheme';
+import { buildMonacoTheme, OV_MONACO_THEME } from '../../themes/monaco';
+import styles from './DiffViewer.module.css';
+
+export type DiffMode = 'side-by-side' | 'inline';
+
+export interface DiffViewerProps {
+ original: string;
+ modified: string;
+ language?: string;
+ mode?: DiffMode;
+ readOnly?: boolean;
+ height?: string | number;
+ className?: string;
+}
+
+function cn(...parts: Array): string {
+ return parts.filter(Boolean).join(' ');
+}
+
+export const DiffViewer = forwardRef(function DiffViewer(
+ {
+ original,
+ modified,
+ language,
+ mode = 'side-by-side',
+ readOnly = true,
+ height = '100%',
+ className,
+ },
+ ref,
+) {
+ const theme = useEditorTheme();
+ const containerRef = useRef(null);
+ const editorRef = useRef(null);
+ const [isReady, setIsReady] = useState(false);
+
+ // Create editor on mount
+ useEffect(() => {
+ if (!containerRef.current) return;
+
+ monaco.editor.defineTheme(OV_MONACO_THEME, buildMonacoTheme(theme));
+ monaco.editor.setTheme(OV_MONACO_THEME);
+
+ const editor = monaco.editor.createDiffEditor(containerRef.current, {
+ readOnly,
+ renderSideBySide: mode === 'side-by-side',
+ renderValidationDecorations: 'off',
+ scrollBeyondLastLine: false,
+ automaticLayout: true,
+ fontSize: 13,
+ fontFamily: 'var(--ov-font-family-mono, monospace)',
+ });
+
+ const originalModel = monaco.editor.createModel(original, language);
+ const modifiedModel = monaco.editor.createModel(modified, language);
+ editor.setModel({ original: originalModel, modified: modifiedModel });
+
+ editorRef.current = editor;
+ setIsReady(true);
+
+ return () => {
+ editor.getModel()?.original?.dispose();
+ editor.getModel()?.modified?.dispose();
+ editor.dispose();
+ editorRef.current = null;
+ setIsReady(false);
+ };
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ // Update original content
+ useEffect(() => {
+ if (!editorRef.current || !isReady) return;
+ const origModel = editorRef.current.getModel()?.original;
+ if (origModel && original !== origModel.getValue()) {
+ origModel.setValue(original);
+ }
+ }, [original, isReady]);
+
+ // Update modified content
+ useEffect(() => {
+ if (!editorRef.current || !isReady) return;
+ const modifiedEditor = editorRef.current.getModifiedEditor();
+ if (modified !== modifiedEditor.getValue()) {
+ editorRef.current.getModel()?.modified?.setValue(modified);
+ }
+ }, [modified, isReady]);
+
+ // Update language
+ useEffect(() => {
+ if (!editorRef.current || !isReady) return;
+ const lang = language ?? 'plaintext';
+ const models = editorRef.current.getModel();
+ if (models?.original) monaco.editor.setModelLanguage(models.original, lang);
+ if (models?.modified) monaco.editor.setModelLanguage(models.modified, lang);
+ }, [language, isReady]);
+
+ // Update options
+ useEffect(() => {
+ if (!editorRef.current || !isReady) return;
+ editorRef.current.updateOptions({
+ readOnly,
+ renderSideBySide: mode === 'side-by-side',
+ });
+ }, [readOnly, mode, isReady]);
+
+ // Update theme
+ useEffect(() => {
+ if (!isReady) return;
+ monaco.editor.defineTheme(OV_MONACO_THEME, buildMonacoTheme(theme));
+ monaco.editor.setTheme(OV_MONACO_THEME);
+ }, [theme, isReady]);
+
+ return (
+
+ );
+});
+
+DiffViewer.displayName = 'DiffViewer';
diff --git a/packages/editors/src/components/diff-viewer/index.ts b/packages/editors/src/components/diff-viewer/index.ts
new file mode 100644
index 0000000..4e96330
--- /dev/null
+++ b/packages/editors/src/components/diff-viewer/index.ts
@@ -0,0 +1,2 @@
+export { DiffViewer } from './DiffViewer';
+export type { DiffViewerProps, DiffMode } from './DiffViewer';
diff --git a/packages/editors/src/components/index.ts b/packages/editors/src/components/index.ts
new file mode 100644
index 0000000..c843fac
--- /dev/null
+++ b/packages/editors/src/components/index.ts
@@ -0,0 +1,30 @@
+export { CodeEditor } from './code-editor';
+export type {
+ CodeEditorProps,
+ CodeEditorHandle,
+ EditorDiagnostic,
+ DiagnosticSeverity,
+ CursorPosition,
+ EditorDebugState,
+} from './code-editor';
+
+export { DiffViewer } from './diff-viewer';
+export type { DiffViewerProps, DiffMode } from './diff-viewer';
+
+export { Terminal } from './terminal';
+export type {
+ TerminalProps,
+ TerminalHandle,
+ TerminalSignal,
+ TerminalSearchOptions,
+ TerminalErrorInfo,
+} from './terminal';
+
+export { MarkdownPreview } from './markdown-preview';
+export type { MarkdownPreviewProps } from './markdown-preview';
+
+export { CommandPalette } from './command-palette';
+export type { CommandPaletteProps, CommandItem } from './command-palette';
+
+export { ObjectInspector } from './object-inspector';
+export type { ObjectInspectorProps, InspectorFormat } from './object-inspector';
diff --git a/packages/editors/src/components/markdown-preview/MarkdownPreview.module.css b/packages/editors/src/components/markdown-preview/MarkdownPreview.module.css
new file mode 100644
index 0000000..8479f43
--- /dev/null
+++ b/packages/editors/src/components/markdown-preview/MarkdownPreview.module.css
@@ -0,0 +1,99 @@
+.Root {
+ color: var(--ov-color-editor-fg, var(--ov-color-fg-default));
+ font-family: var(--ov-font-family-sans, sans-serif);
+ font-size: var(--ov-font-size-md, 14px);
+ line-height: var(--ov-line-height-md, 1.6);
+ padding: var(--ov-space-3, 12px);
+ overflow-y: auto;
+}
+
+.Root h1,
+.Root h2,
+.Root h3,
+.Root h4,
+.Root h5,
+.Root h6 {
+ color: var(--ov-color-fg-default);
+ font-weight: var(--ov-font-weight-bold, 600);
+ margin-top: var(--ov-space-4, 16px);
+ margin-bottom: var(--ov-space-2, 8px);
+}
+
+.Root h1 {
+ font-size: var(--ov-font-size-2xl, 24px);
+ border-bottom: 1px solid var(--ov-color-border-default);
+ padding-bottom: var(--ov-space-2, 8px);
+}
+
+.Root h2 {
+ font-size: var(--ov-font-size-xl, 20px);
+ border-bottom: 1px solid var(--ov-color-border-default);
+ padding-bottom: var(--ov-space-1, 4px);
+}
+
+.Root h3 {
+ font-size: var(--ov-font-size-lg, 16px);
+}
+
+.Root p {
+ margin-top: 0;
+ margin-bottom: var(--ov-space-3, 12px);
+}
+
+.Root ul,
+.Root ol {
+ margin-top: 0;
+ margin-bottom: var(--ov-space-3, 12px);
+ padding-left: var(--ov-space-5, 24px);
+}
+
+.Root li {
+ margin-bottom: var(--ov-space-1, 4px);
+}
+
+.Root figure {
+ margin: 0 0 var(--ov-space-3, 12px) 0;
+}
+
+/* Strip bottom margin from last paragraph inside blockquotes */
+.Root blockquote > p:last-child {
+ margin-bottom: 0;
+}
+
+/* Blockquote spacing between siblings */
+.Root blockquote {
+ margin-bottom: var(--ov-space-3, 12px);
+}
+
+/* Task list checkboxes — keep inline with text */
+.Root :global(.ov-md-checkbox) {
+ display: inline-grid;
+ width: auto;
+ vertical-align: middle;
+ margin-right: var(--ov-space-1, 4px);
+}
+
+/* Task list items should not show bullet */
+.Root li:has(> :global(.ov-md-checkbox)) {
+ list-style: none;
+}
+
+.Root img {
+ max-width: 100%;
+ height: auto;
+ border-radius: var(--ov-radius-sm, 4px);
+}
+
+/* Accordion spacing for details/summary blocks */
+.Root [data-ov-component='accordion'] {
+ margin-bottom: var(--ov-space-3, 12px);
+}
+
+.Loading {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: var(--ov-space-4, 16px);
+ color: var(--ov-color-fg-muted);
+ font-size: var(--ov-font-size-sm, 13px);
+}
diff --git a/packages/editors/src/components/markdown-preview/MarkdownPreview.stories.tsx b/packages/editors/src/components/markdown-preview/MarkdownPreview.stories.tsx
new file mode 100644
index 0000000..521ec7f
--- /dev/null
+++ b/packages/editors/src/components/markdown-preview/MarkdownPreview.stories.tsx
@@ -0,0 +1,469 @@
+import type { Meta, StoryObj } from '@storybook/react';
+import { MarkdownPreview } from './MarkdownPreview';
+
+/** Checked-in SVG assets for offline/deterministic stories. */
+import placeholderSvg from './assets/placeholder.svg';
+import badgeBuildSvg from './assets/badge-build.svg';
+import badgeCoverageSvg from './assets/badge-coverage.svg';
+import badgeLicenseSvg from './assets/badge-license.svg';
+import badgeVersionSvg from './assets/badge-version.svg';
+import badgePrsSvg from './assets/badge-prs.svg';
+
+const richMarkdown = `# Omniview Platform Guide
+
+Welcome to the **Omniview** platform. This document exercises every markdown surface supported by the preview component.
+
+## Inline Formatting
+
+Regular text with **bold**, *italic*, ~~strikethrough~~, and **_bold italic_** combined. Here is some \`inline code\` and a [link to the docs](https://docs.omniview.dev).
+
+## Headings
+
+### Third-level heading
+#### Fourth-level heading
+##### Fifth-level heading
+###### Sixth-level heading
+
+## Lists
+
+### Unordered
+
+- Cluster management
+- Resource monitoring
+ - CPU & memory
+ - Disk I/O
+ - Read throughput
+ - Write throughput
+- Alerting & notifications
+
+### Ordered
+
+1. Connect to the cluster
+2. Select a namespace
+3. Choose a resource type
+ 1. Pods
+ 2. Services
+ 3. Deployments
+
+### Task List
+
+- [x] Set up project structure
+- [x] Implement CodeEditor component
+- [x] Implement DiffViewer component
+- [ ] Add integration tests
+- [ ] Performance benchmarks
+- [ ] Release v0.1.0
+
+## Code Blocks
+
+A quick shell command:
+
+\`\`\`bash
+kubectl get pods -n production --sort-by='.status.startTime'
+\`\`\`
+
+TypeScript with syntax highlighting:
+
+\`\`\`typescript
+import { createClient } from './api/client';
+
+const client = createClient();
+
+interface PodListResponse {
+ items: Array<{
+ metadata: { name: string; namespace: string };
+ status: { phase: 'Running' | 'Pending' | 'Failed' };
+ }>;
+}
+
+export async function fetchPods(namespace: string): Promise {
+ const { data } = await client.get(
+ \`/api/v1/namespaces/\${namespace}/pods\`,
+ );
+ return data;
+}
+\`\`\`
+
+JSON configuration:
+
+\`\`\`json
+{
+ "cluster": "production",
+ "context": "omniview-prod",
+ "preferences": {
+ "theme": "dark",
+ "refreshInterval": 5000,
+ "maxLogLines": 1000
+ }
+}
+\`\`\`
+
+YAML manifest:
+
+\`\`\`yaml
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: omniview-api
+ labels:
+ app: omniview
+spec:
+ replicas: 3
+ selector:
+ matchLabels:
+ app: omniview
+ template:
+ spec:
+ containers:
+ - name: api
+ image: omniview/api:latest
+ ports:
+ - containerPort: 8080
+\`\`\`
+
+A plain code block with no language:
+
+\`\`\`
+no syntax highlighting here
+just plain preformatted text
+\`\`\`
+
+## Tables
+
+| Kind | API Version | Namespaced | Description |
+|------|:----------:|:----------:|-------------|
+| Pod | v1 | Yes | Smallest deployable unit |
+| Service | v1 | Yes | Network endpoint abstraction |
+| Deployment | apps/v1 | Yes | Declarative pod management |
+| ConfigMap | v1 | Yes | Non-confidential configuration |
+| Secret | v1 | Yes | Sensitive data storage |
+| Namespace | v1 | No | Cluster partitioning |
+| Node | v1 | No | Worker machine in cluster |
+
+## Blockquotes
+
+> **Note:** All API calls go through the centralized client at \`src/api/client.ts\`.
+
+> **Warning:** This API is experimental and may change in future releases.
+>
+> Proceed with caution when using the \`--force\` flag in production.
+
+> Nested blockquote with code:
+>
+> \`\`\`bash
+> kubectl port-forward svc/my-service 8080:80
+> \`\`\`
+
+## Links
+
+- [GitHub Repository](https://github.com/omniviewdev/omniview)
+- [Documentation](https://docs.omniview.dev)
+- [TypeScript Handbook](https://www.typescriptlang.org/docs/handbook)
+
+Inline link: Visit [GitHub](https://github.com) for source code.
+
+## Horizontal Rules
+
+Content above the rule.
+
+---
+
+Content below the rule.
+
+---
+
+## Images
+
+
+
+## Paragraphs & Emphasis
+
+This is a regular paragraph with enough text to demonstrate line wrapping behavior in the preview component. The markdown renderer should handle long paragraphs gracefully without breaking the layout.
+
+*This entire paragraph is italicized for emphasis. It demonstrates how the preview handles blocks of styled text rather than just inline fragments.*
+
+**This entire paragraph is bold. It is useful for call-outs or important notices that need to stand out from the surrounding content.**
+
+---
+
+*Last updated: 2025-03-09*`;
+
+const meta: Meta = {
+ title: 'Editors/MarkdownPreview',
+ component: MarkdownPreview,
+ tags: ['autodocs'],
+ args: {
+ content: richMarkdown,
+ },
+ argTypes: {
+ allowHtml: { control: 'boolean' },
+ },
+ decorators: [
+ (Story) => (
+
+
+
+ ),
+ ],
+};
+
+export default meta;
+type Story = StoryObj;
+
+export const Playground: Story = {};
+
+export const Simple: Story = {
+ args: {
+ content:
+ '# Hello World\n\nThis is a simple markdown preview.\n\nIt supports **bold**, *italic*, and `inline code`.',
+ },
+};
+
+export const WithCodeBlocks: Story = {
+ args: {
+ content: `## Code Examples
+
+### JavaScript
+\`\`\`javascript
+function fibonacci(n) {
+ if (n <= 1) return n;
+ return fibonacci(n - 1) + fibonacci(n - 2);
+}
+\`\`\`
+
+### Python
+\`\`\`python
+def fibonacci(n: int) -> int:
+ if n <= 1:
+ return n
+ return fibonacci(n - 1) + fibonacci(n - 2)
+\`\`\`
+
+### Go
+\`\`\`go
+func fibonacci(n int) int {
+ if n <= 1 {
+ return n
+ }
+ return fibonacci(n-1) + fibonacci(n-2)
+}
+\`\`\``,
+ },
+};
+
+export const Tables: Story = {
+ args: {
+ content: `## Kubernetes Resource Types
+
+| Kind | API Version | Namespaced | Description |
+|------|------------|------------|-------------|
+| Pod | v1 | Yes | Smallest deployable unit |
+| Service | v1 | Yes | Network endpoint abstraction |
+| Deployment | apps/v1 | Yes | Declarative pod management |
+| ConfigMap | v1 | Yes | Non-confidential configuration |
+| Secret | v1 | Yes | Sensitive data storage |
+| Namespace | v1 | No | Cluster partitioning |
+| Node | v1 | No | Worker machine in cluster |
+| PersistentVolume | v1 | No | Storage resource |`,
+ },
+};
+
+export const TaskLists: Story = {
+ args: {
+ content: `## Sprint Checklist
+
+- [x] Set up project structure
+- [x] Implement CodeEditor component
+- [x] Implement DiffViewer component
+- [x] Implement Terminal component
+- [ ] Add integration tests
+- [ ] Performance benchmarks
+- [ ] Documentation review
+- [ ] Release v0.1.0`,
+ },
+};
+
+export const NestedLists: Story = {
+ args: {
+ content: `## Project Structure
+
+- \`packages/\`
+ - \`base-ui/\` — Core UI primitives
+ - \`src/components/\` — Component library
+ - \`src/theme/\` — Theme system
+ - \`editors/\` — Editor components
+ - \`src/components/\`
+ - \`code-editor/\` — Monaco-based editor
+ - \`terminal/\` — xterm.js terminal
+ - \`diff-viewer/\` — Side-by-side diff
+ - \`src/themes/\` — Editor theme integration
+- \`docs/\` — Documentation
+- \`scripts/\` — Build tooling`,
+ },
+};
+
+export const Blockquotes: Story = {
+ args: {
+ content: `## Important Notes
+
+> **Warning:** This API is experimental and may change in future releases.
+
+> **Tip:** Use \`kubectl port-forward\` to access services locally:
+> \`\`\`bash
+> kubectl port-forward svc/my-service 8080:80
+> \`\`\``,
+ },
+};
+
+export const Links: Story = {
+ args: {
+ content: `## Resources
+
+Links open in new tabs with \`rel="noopener noreferrer"\`:
+
+- [React Documentation](https://react.dev)
+- [TypeScript Handbook](https://www.typescriptlang.org/docs/handbook)
+- [Kubernetes Documentation](https://kubernetes.io/docs)
+
+Inline link: Visit [GitHub](https://github.com) for source code.`,
+ },
+};
+
+export const EmptyContent: Story = {
+ args: {
+ content: '',
+ },
+};
+
+export const LongDocument: Story = {
+ args: {
+ content: Array.from(
+ { length: 20 },
+ (_, i) =>
+ `## Section ${i + 1}\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.\n\n\`\`\`\ncode block ${i + 1}\n\`\`\`\n`,
+ ).join('\n'),
+ },
+};
+
+export const WithHtml: Story = {
+ args: {
+ content: `## HTML Content
+
+This story has \`allowHtml\` enabled, so raw HTML is parsed and rendered.
+
+### Details / Summary
+
+
+Click to expand — deployment notes
+
+The v2.0 release requires a database migration. Run the following **before** deploying:
+
+\`\`\`bash
+pnpm db:migrate --env production
+\`\`\`
+
+After migration, verify with \`pnpm db:status\`.
+
+
+
+
+Architecture decision records
+
+- **ADR-001** — Use Monaco Editor for code editing
+- **ADR-002** — Use xterm.js for terminal emulation
+- **ADR-003** — Monorepo with pnpm workspaces
+
+
+
+### Badges & Shields
+
+
+
+
+
+
+### Inline HTML elements
+
+This paragraph has strong , emphasis , strikethrough, inline code, and a link .
+
+### Definition list (via HTML)
+
+
+ Kubernetes
+ An open-source container orchestration platform.
+ Helm
+ A package manager for Kubernetes.
+
+
+### Keyboard shortcuts
+
+Press Ctrl + Shift + P to open the command palette.`,
+ allowHtml: true,
+ },
+};
+
+export const GitHubReadme: Story = {
+ args: {
+ content: `
+ Omniview — A unified platform for Kubernetes management
+
+
+
+
+
+
+
+
+
+---
+
+## Features
+
+- Multi-cluster management with a unified dashboard
+- Real-time resource monitoring and alerting
+- Integrated code editor with **YAML** schema validation
+- Built-in terminal for \`kubectl\` access
+
+## Quick Start
+
+\`\`\`bash
+# Install
+pnpm add @omniview/app
+
+# Run
+pnpm dev
+\`\`\`
+
+## Configuration
+
+| Option | Type | Default | Description |
+|--------|------|---------|-------------|
+| \`theme\` | \`string\` | \`"dark"\` | UI theme |
+| \`refreshInterval\` | \`number\` | \`5000\` | Poll interval in ms |
+| \`maxLogLines\` | \`number\` | \`1000\` | Terminal log buffer |
+
+## Contributing
+
+- [x] Read the contributing guide
+- [ ] Fork the repository
+- [ ] Create a feature branch
+- [ ] Submit a pull request
+
+
+Development setup
+
+\`\`\`bash
+git clone https://github.com/omniviewdev/omniview.git
+cd omniview
+pnpm install
+pnpm dev
+\`\`\`
+
+
+
+## License
+
+MIT — see LICENSE for details.`,
+ allowHtml: true,
+ },
+};
diff --git a/packages/editors/src/components/markdown-preview/MarkdownPreview.test.tsx b/packages/editors/src/components/markdown-preview/MarkdownPreview.test.tsx
new file mode 100644
index 0000000..d41d2c6
--- /dev/null
+++ b/packages/editors/src/components/markdown-preview/MarkdownPreview.test.tsx
@@ -0,0 +1,199 @@
+import { render, screen } from '@testing-library/react';
+import { describe, expect, it, vi } from 'vitest';
+import { MarkdownPreview } from './MarkdownPreview';
+
+// Mock remark/rehype plugins
+vi.mock('remark-gfm', () => ({
+ __esModule: true,
+ default: () => {},
+}));
+
+vi.mock('rehype-raw', () => ({
+ __esModule: true,
+ default: () => {},
+}));
+
+vi.mock('rehype-sanitize', () => ({
+ __esModule: true,
+ default: () => {},
+ defaultSchema: { tagNames: [], attributes: {} },
+}));
+
+// Mock base-ui components as simple pass-throughs
+vi.mock('@omniview/base-ui', () => ({
+ CodeBlock: ({ children, language }: { children: string; language?: string }) => (
+
+ {children}
+
+ ),
+ Code: ({ children }: { children: React.ReactNode }) => (
+ {children}
+ ),
+ Table: {
+ Root: ({ children }: { children: React.ReactNode }) => (
+
+ ),
+ Head: ({ children }: { children: React.ReactNode }) => {children} ,
+ Body: ({ children }: { children: React.ReactNode }) => {children} ,
+ Row: ({ children }: { children: React.ReactNode }) => {children} ,
+ HeaderCell: ({ children }: { children: React.ReactNode }) => {children} ,
+ Cell: ({ children }: { children: React.ReactNode }) => {children} ,
+ },
+ Link: ({ children, ...props }: { children: React.ReactNode; [key: string]: unknown }) => (
+
+ {children}
+
+ ),
+ Blockquote: ({ children }: { children: React.ReactNode }) => (
+ {children}
+ ),
+ Separator: () => ,
+ Checkbox: Object.assign(
+ ({ checked }: { checked?: boolean }) => (
+
+ ),
+ {
+ Root: ({ children }: { children: React.ReactNode }) => {children} ,
+ Control: ({ children }: { children: React.ReactNode }) => {children} ,
+ Indicator: () => ,
+ },
+ ),
+ Accordion: Object.assign(
+ ({ children }: { children: React.ReactNode }) => (
+ {children}
+ ),
+ {
+ Item: ({ children, title }: { children: React.ReactNode; title: string }) => (
+
+ {children}
+
+ ),
+ },
+ ),
+}));
+
+// Mock react-markdown
+vi.mock('react-markdown', () => ({
+ __esModule: true,
+ default: ({
+ children,
+ skipHtml,
+ remarkPlugins,
+ rehypePlugins,
+ components,
+ }: {
+ children: string;
+ skipHtml?: boolean;
+ remarkPlugins?: unknown[];
+ rehypePlugins?: unknown[];
+ components?: Record;
+ }) => (
+ 0)}
+ data-has-rehype-plugins={String(Array.isArray(rehypePlugins) && rehypePlugins.length > 0)}
+ data-component-keys={components ? Object.keys(components).sort().join(',') : ''}
+ >
+ {children}
+
+ ),
+}));
+
+describe('MarkdownPreview', () => {
+ it('renders the preview container', () => {
+ render( );
+ expect(screen.getByTestId('markdown-preview')).toBeInTheDocument();
+ });
+
+ it('passes content to the markdown renderer', () => {
+ render( );
+ expect(screen.getByTestId('react-markdown-mock')).toHaveTextContent('# Title');
+ });
+
+ it('merges className', () => {
+ render( );
+ expect(screen.getByTestId('markdown-preview')).toHaveClass('custom-md');
+ });
+
+ it('forwards ref', () => {
+ const ref = { current: null } as unknown as React.RefObject;
+ render( );
+ expect(ref.current).toBeInstanceOf(HTMLDivElement);
+ });
+
+ it('passes additional HTML attributes', () => {
+ render( );
+ expect(screen.getByTestId('markdown-preview')).toHaveAttribute('aria-label', 'Preview');
+ });
+
+ it('sets skipHtml=true by default (allowHtml=false)', () => {
+ render( );
+ expect(screen.getByTestId('react-markdown-mock')).toHaveAttribute('data-skip-html', 'true');
+ });
+
+ it('sets skipHtml=false when allowHtml=true', () => {
+ render( );
+ expect(screen.getByTestId('react-markdown-mock')).toHaveAttribute('data-skip-html', 'false');
+ });
+
+ it('adds rehype plugins when allowHtml=true', () => {
+ render( );
+ expect(screen.getByTestId('react-markdown-mock')).toHaveAttribute(
+ 'data-has-rehype-plugins',
+ 'true',
+ );
+ });
+
+ it('does not add rehype plugins when allowHtml=false', () => {
+ render( );
+ expect(screen.getByTestId('react-markdown-mock')).toHaveAttribute(
+ 'data-has-rehype-plugins',
+ 'false',
+ );
+ });
+
+ it('passes remarkPlugins to the renderer', () => {
+ render( );
+ expect(screen.getByTestId('react-markdown-mock')).toHaveAttribute(
+ 'data-has-remark-plugins',
+ 'true',
+ );
+ });
+
+ it('provides component overrides for all mapped elements', () => {
+ render( );
+ const md = screen.getByTestId('react-markdown-mock');
+ const keys = md.getAttribute('data-component-keys');
+ expect(keys).toContain('code');
+ expect(keys).toContain('pre');
+ expect(keys).toContain('table');
+ expect(keys).toContain('thead');
+ expect(keys).toContain('tbody');
+ expect(keys).toContain('tr');
+ expect(keys).toContain('th');
+ expect(keys).toContain('td');
+ expect(keys).toContain('a');
+ expect(keys).toContain('blockquote');
+ expect(keys).toContain('hr');
+ expect(keys).toContain('details');
+ expect(keys).toContain('summary');
+ expect(keys).toContain('input');
+ });
+
+ it('renders with empty content', () => {
+ render( );
+ expect(screen.getByTestId('markdown-preview')).toBeInTheDocument();
+ expect(screen.getByTestId('react-markdown-mock')).toHaveTextContent('');
+ });
+
+ it('passes style prop through', () => {
+ render( );
+ expect(screen.getByTestId('markdown-preview')).toHaveStyle({ maxWidth: '500px' });
+ });
+
+ it('passes data attributes through', () => {
+ render( );
+ expect(screen.getByTestId('markdown-preview')).toHaveAttribute('data-section', 'readme');
+ });
+});
diff --git a/packages/editors/src/components/markdown-preview/MarkdownPreview.tsx b/packages/editors/src/components/markdown-preview/MarkdownPreview.tsx
new file mode 100644
index 0000000..ca147df
--- /dev/null
+++ b/packages/editors/src/components/markdown-preview/MarkdownPreview.tsx
@@ -0,0 +1,199 @@
+import { Children, forwardRef, isValidElement, lazy, Suspense, useId, useMemo, type ComponentType, type HTMLAttributes, type ReactNode } from 'react';
+import remarkGfm from 'remark-gfm';
+import rehypeRaw from 'rehype-raw';
+import rehypeSanitize, { defaultSchema } from 'rehype-sanitize';
+import {
+ Accordion,
+ Blockquote,
+ Checkbox,
+ Code,
+ CodeBlock,
+ Link,
+ Separator,
+ Table,
+} from '@omniview/base-ui';
+import styles from './MarkdownPreview.module.css';
+
+const ReactMarkdown = lazy(() => import('react-markdown'));
+
+export interface MarkdownPreviewProps extends HTMLAttributes {
+ content: string;
+ allowHtml?: boolean;
+}
+
+function cn(...parts: Array): string {
+ return parts.filter(Boolean).join(' ');
+}
+
+/** Recursively extract plain text from a hast node tree. */
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+function hastText(node: any): string {
+ if (!node) return '';
+ if (node.type === 'text') return node.value ?? '';
+ if (Array.isArray(node.children)) return node.children.map(hastText).join('');
+ return '';
+}
+
+/** Accordion wrapper for / that uses useId() for a stable, deterministic ID. */
+function DetailsAccordion({ summaryText, children }: { summaryText: string; children: ReactNode[] }) {
+ const id = useId();
+ const itemId = `md-details-${id}`;
+ return (
+
+
+ {children}
+
+
+ );
+}
+
+/** Stable component overrides for react-markdown */
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+type MdProps = Record;
+
+const markdownComponents: Record> = {
+ // Code blocks & inline code
+ pre: ({ children }: MdProps) => <>{children}>,
+ code: ({ className, children }: MdProps) => {
+ const match = /language-([^\s]+)/.exec(className ?? '');
+ const text = String(children).replace(/\n$/, '');
+ // Fenced blocks: has language class OR contains newlines (multiline = block)
+ if (match || text.includes('\n')) {
+ return (
+
+ {text}
+
+ );
+ }
+ return {children};
+ },
+
+ // Tables
+ table: ({ children }: MdProps) => (
+
+ {children}
+
+ ),
+ thead: ({ children }: MdProps) => {children} ,
+ tbody: ({ children }: MdProps) => {children} ,
+ tr: ({ children }: MdProps) => {children} ,
+ th: ({ children, style }: MdProps) => (
+ {children}
+ ),
+ td: ({ children, style }: MdProps) => (
+ {children}
+ ),
+
+ // Links
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ a: ({ children, href, node, ...rest }: MdProps) => (
+
+ {children}
+
+ ),
+
+ // Blockquotes
+ blockquote: ({ children }: MdProps) => {children} ,
+
+ // Horizontal rules
+ hr: () => ,
+
+ // Details/summary → Accordion — extract title from the hast node's summary child
+ details: ({ children, node }: MdProps) => {
+ // Extract summary text from the hast AST (before React renders it)
+ let summaryText = 'Details';
+ if (node?.children) {
+ const summaryNode = node.children.find(
+ (c: MdProps) => c.tagName === 'summary',
+ );
+ if (summaryNode) {
+ const text = hastText(summaryNode).trim();
+ if (text) summaryText = text;
+ }
+ }
+
+ // Filter out the rendered summary element from React children
+ const body: ReactNode[] = [];
+ Children.forEach(children, (child) => {
+ // Skip the hidden summary marker
+ if (isValidElement(child) && (child.props as MdProps)?.['data-md-summary']) {
+ return;
+ }
+ body.push(child);
+ });
+
+ return {body} ;
+ },
+ // Mark summary so details can filter it out; text is extracted from hast node
+ summary: ({ children }: MdProps) => (
+ {children}
+ ),
+
+ // Task list checkboxes — use compound parts for inline rendering
+ input: ({ type, checked, disabled }: MdProps) => {
+ if (type === 'checkbox') {
+ return (
+
+
+
+
+
+ );
+ }
+ return null;
+ },
+};
+
+export const MarkdownPreview = forwardRef(
+ function MarkdownPreview({ content, allowHtml = false, className, ...props }, ref) {
+ return (
+
+
+ Loading…
+
+ }
+ >
+
+
+
+ );
+ },
+);
+
+MarkdownPreview.displayName = 'MarkdownPreview';
+
+/** Sanitize schema that extends the default to allow class on any element (needed for alerts). */
+const sanitizeSchema = {
+ ...defaultSchema,
+ attributes: {
+ ...defaultSchema.attributes,
+ '*': [...(defaultSchema.attributes?.['*'] ?? []), 'className'],
+ },
+};
+
+/** Inner component that uses lazy-loaded deps */
+function MarkdownContent({ content, allowHtml }: { content: string; allowHtml: boolean }) {
+ const rehypePlugins = useMemo(
+ () => (allowHtml ? [rehypeRaw, [rehypeSanitize, sanitizeSchema]] : []),
+ [allowHtml],
+ );
+
+ return (
+
+ {content}
+
+ );
+}
diff --git a/packages/editors/src/components/markdown-preview/assets/badge-build.svg b/packages/editors/src/components/markdown-preview/assets/badge-build.svg
new file mode 100644
index 0000000..1680019
--- /dev/null
+++ b/packages/editors/src/components/markdown-preview/assets/badge-build.svg
@@ -0,0 +1 @@
+build pass
diff --git a/packages/editors/src/components/markdown-preview/assets/badge-coverage.svg b/packages/editors/src/components/markdown-preview/assets/badge-coverage.svg
new file mode 100644
index 0000000..47a23c5
--- /dev/null
+++ b/packages/editors/src/components/markdown-preview/assets/badge-coverage.svg
@@ -0,0 +1 @@
+coverage 94%
diff --git a/packages/editors/src/components/markdown-preview/assets/badge-license.svg b/packages/editors/src/components/markdown-preview/assets/badge-license.svg
new file mode 100644
index 0000000..d8c673f
--- /dev/null
+++ b/packages/editors/src/components/markdown-preview/assets/badge-license.svg
@@ -0,0 +1 @@
+license MIT
diff --git a/packages/editors/src/components/markdown-preview/assets/badge-prs.svg b/packages/editors/src/components/markdown-preview/assets/badge-prs.svg
new file mode 100644
index 0000000..0ff3316
--- /dev/null
+++ b/packages/editors/src/components/markdown-preview/assets/badge-prs.svg
@@ -0,0 +1 @@
+PRs welcome
diff --git a/packages/editors/src/components/markdown-preview/assets/badge-version.svg b/packages/editors/src/components/markdown-preview/assets/badge-version.svg
new file mode 100644
index 0000000..f1999cb
--- /dev/null
+++ b/packages/editors/src/components/markdown-preview/assets/badge-version.svg
@@ -0,0 +1 @@
+version 0.1.0
diff --git a/packages/editors/src/components/markdown-preview/assets/placeholder.svg b/packages/editors/src/components/markdown-preview/assets/placeholder.svg
new file mode 100644
index 0000000..73f0dda
--- /dev/null
+++ b/packages/editors/src/components/markdown-preview/assets/placeholder.svg
@@ -0,0 +1 @@
+Omniview Preview
diff --git a/packages/editors/src/components/markdown-preview/index.ts b/packages/editors/src/components/markdown-preview/index.ts
new file mode 100644
index 0000000..84bb173
--- /dev/null
+++ b/packages/editors/src/components/markdown-preview/index.ts
@@ -0,0 +1,2 @@
+export { MarkdownPreview } from './MarkdownPreview';
+export type { MarkdownPreviewProps } from './MarkdownPreview';
diff --git a/packages/editors/src/components/object-inspector/ObjectInspector.module.css b/packages/editors/src/components/object-inspector/ObjectInspector.module.css
new file mode 100644
index 0000000..4c4bd53
--- /dev/null
+++ b/packages/editors/src/components/object-inspector/ObjectInspector.module.css
@@ -0,0 +1,135 @@
+.Root {
+ font-family: var(--ov-font-family-mono, monospace);
+ font-size: var(--ov-font-size-sm, 13px);
+ line-height: 1.5;
+ color: var(--ov-color-editor-fg, var(--ov-color-fg-default));
+ background: var(--ov-color-editor-bg, var(--ov-color-bg-surface));
+ border: 1px solid var(--ov-color-editor-border, var(--ov-color-border-default));
+ border-radius: var(--ov-radius-sm, 4px);
+ overflow: auto;
+}
+
+.Toolbar {
+ display: flex;
+ align-items: center;
+ gap: var(--ov-space-2, 8px);
+ padding: var(--ov-space-2, 8px);
+ border-bottom: 1px solid var(--ov-color-border-default);
+}
+
+.SearchInput {
+ flex: 1;
+ padding: var(--ov-space-1, 4px) var(--ov-space-2, 8px);
+ background: var(--ov-color-input-bg, transparent);
+ color: var(--ov-color-input-fg, var(--ov-color-fg-default));
+ border: 1px solid var(--ov-color-input-border, var(--ov-color-border-default));
+ border-radius: var(--ov-radius-sm, 4px);
+ font-family: inherit;
+ font-size: inherit;
+ outline: none;
+}
+
+.SearchInput::placeholder {
+ color: var(--ov-color-input-placeholder, var(--ov-color-fg-muted));
+}
+
+.SearchInput:focus-visible {
+ outline: 2px solid var(--ov-color-border-focus, var(--ov-color-fg-accent, #007fd4));
+ outline-offset: -1px;
+}
+
+.CopyButton {
+ padding: var(--ov-space-1, 4px) var(--ov-space-2, 8px);
+ background: var(--ov-color-button-secondary-bg, var(--ov-color-bg-subtle));
+ color: var(--ov-color-button-secondary-fg, var(--ov-color-fg-default));
+ border: 1px solid var(--ov-color-border-default);
+ border-radius: var(--ov-radius-sm, 4px);
+ font-size: var(--ov-font-size-xs, 11px);
+ cursor: pointer;
+}
+
+.CopyButton:hover {
+ background: var(--ov-color-button-secondary-hover-bg, var(--ov-color-bg-subtle));
+}
+
+.Tree {
+ padding: var(--ov-space-2, 8px);
+}
+
+.Node {
+ /* container for tree node */
+}
+
+.Row {
+ display: flex;
+ align-items: center;
+ padding: 1px 0;
+ cursor: default;
+ white-space: nowrap;
+}
+
+.Row:hover {
+ background: var(--ov-color-list-hover-bg, var(--ov-color-bg-subtle));
+}
+
+.RowHighlight {
+ background: var(--ov-color-editor-find-match-bg, rgba(255, 255, 0, 0.15));
+}
+
+.Chevron {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 16px;
+ height: 16px;
+ flex-shrink: 0;
+ font-size: 10px;
+ color: var(--ov-color-fg-muted);
+ transition: transform var(--ov-duration-fast, 100ms) var(--ov-ease-standard, ease);
+ cursor: pointer;
+}
+
+.ChevronExpanded {
+ transform: rotate(90deg);
+}
+
+.ChevronSpacer {
+ display: inline-block;
+ width: 16px;
+ flex-shrink: 0;
+}
+
+.Key {
+ color: var(--ov-syntax-property, var(--ov-color-fg-default));
+}
+
+.Colon {
+ color: var(--ov-syntax-punctuation, var(--ov-color-fg-muted));
+}
+
+.Value[data-type='string'] {
+ color: var(--ov-syntax-string, var(--ov-color-fg-accent, currentColor));
+}
+
+.Value[data-type='number'] {
+ color: var(--ov-syntax-number, var(--ov-color-fg-success, currentColor));
+}
+
+.Value[data-type='boolean'] {
+ color: var(--ov-syntax-keyword, var(--ov-color-fg-info, currentColor));
+}
+
+.Value[data-type='null'],
+.Value[data-type='undefined'] {
+ color: var(--ov-syntax-keyword, var(--ov-color-fg-info, currentColor));
+ font-style: italic;
+}
+
+.Preview {
+ color: var(--ov-color-fg-muted);
+ font-style: italic;
+}
+
+.Comma {
+ color: var(--ov-syntax-punctuation, var(--ov-color-fg-muted));
+}
diff --git a/packages/editors/src/components/object-inspector/ObjectInspector.stories.tsx b/packages/editors/src/components/object-inspector/ObjectInspector.stories.tsx
new file mode 100644
index 0000000..94a1378
--- /dev/null
+++ b/packages/editors/src/components/object-inspector/ObjectInspector.stories.tsx
@@ -0,0 +1,266 @@
+import type { Meta, StoryObj } from '@storybook/react';
+import { ObjectInspector } from './ObjectInspector';
+
+const sampleData = {
+ apiVersion: 'v1',
+ kind: 'Pod',
+ metadata: {
+ name: 'my-pod',
+ namespace: 'default',
+ labels: {
+ app: 'web',
+ version: 'v1.2.3',
+ },
+ annotations: {
+ 'kubectl.kubernetes.io/last-applied-configuration': '...',
+ },
+ creationTimestamp: '2024-01-15T10:30:00Z',
+ uid: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
+ },
+ spec: {
+ containers: [
+ {
+ name: 'web',
+ image: 'nginx:latest',
+ ports: [{ containerPort: 80, protocol: 'TCP' }],
+ env: [
+ { name: 'NODE_ENV', value: 'production' },
+ { name: 'PORT', value: '8080' },
+ ],
+ resources: {
+ limits: { cpu: '500m', memory: '128Mi' },
+ requests: { cpu: '100m', memory: '64Mi' },
+ },
+ },
+ {
+ name: 'sidecar',
+ image: 'envoy:v1.28',
+ ports: [{ containerPort: 9901 }],
+ },
+ ],
+ restartPolicy: 'Always',
+ serviceAccountName: 'default',
+ nodeName: 'worker-node-1',
+ },
+ status: {
+ phase: 'Running',
+ conditions: [
+ { type: 'Ready', status: true, lastTransitionTime: '2024-01-15T10:31:00Z' },
+ { type: 'PodScheduled', status: true, lastTransitionTime: '2024-01-15T10:30:00Z' },
+ { type: 'ContainersReady', status: true, lastTransitionTime: '2024-01-15T10:31:00Z' },
+ { type: 'Initialized', status: true, lastTransitionTime: '2024-01-15T10:30:05Z' },
+ ],
+ containerStatuses: [
+ {
+ name: 'web',
+ ready: true,
+ restartCount: 0,
+ state: { running: { startedAt: '2024-01-15T10:30:30Z' } },
+ },
+ {
+ name: 'sidecar',
+ ready: true,
+ restartCount: 2,
+ state: { running: { startedAt: '2024-01-15T10:30:45Z' } },
+ },
+ ],
+ startTime: '2024-01-15T10:30:00Z',
+ podIP: '10.244.0.5',
+ hostIP: '192.168.1.100',
+ },
+};
+
+const meta: Meta = {
+ title: 'Editors/ObjectInspector',
+ component: ObjectInspector,
+ tags: ['autodocs'],
+ args: {
+ data: sampleData,
+ defaultExpanded: 2,
+ },
+ argTypes: {
+ format: { control: 'radio', options: ['json', 'yaml'] },
+ defaultExpanded: { control: { type: 'range', min: 0, max: 5, step: 1 } },
+ searchable: { control: 'boolean' },
+ copyable: { control: 'boolean' },
+ },
+ decorators: [
+ (Story) => (
+
+
+
+ ),
+ ],
+};
+
+export default meta;
+type Story = StoryObj;
+
+export const Playground: Story = {};
+
+export const FullyExpanded: Story = {
+ args: {
+ defaultExpanded: true,
+ },
+};
+
+export const Collapsed: Story = {
+ args: {
+ defaultExpanded: 0,
+ },
+};
+
+export const WithSearch: Story = {
+ args: {
+ searchable: true,
+ defaultExpanded: true,
+ },
+};
+
+export const WithCopy: Story = {
+ args: {
+ copyable: true,
+ },
+};
+
+export const WithSearchAndCopy: Story = {
+ args: {
+ searchable: true,
+ copyable: true,
+ defaultExpanded: true,
+ },
+};
+
+export const YamlFormat: Story = {
+ args: {
+ format: 'yaml',
+ copyable: true,
+ },
+};
+
+export const SimpleArray: Story = {
+ args: {
+ data: ['hello', 42, true, null, { nested: 'value' }],
+ defaultExpanded: true,
+ },
+};
+
+/** Demonstrates handling of all primitive types. */
+export const PrimitiveTypes: Story = {
+ args: {
+ data: {
+ string: 'hello world',
+ number: 42,
+ float: 3.14159,
+ booleanTrue: true,
+ booleanFalse: false,
+ nullValue: null,
+ emptyString: '',
+ zero: 0,
+ negativeNumber: -100,
+ largeNumber: 9007199254740991,
+ },
+ defaultExpanded: true,
+ },
+};
+
+/** Deeply nested data structure. */
+export const DeepNesting: Story = {
+ args: {
+ data: {
+ level1: {
+ level2: {
+ level3: {
+ level4: {
+ level5: {
+ level6: {
+ value: 'deeply nested',
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ defaultExpanded: 3,
+ },
+};
+
+/** Circular reference handling — should display [Circular] instead of crashing. */
+export const CircularReference: Story = {
+ args: (() => {
+ const obj: Record = {
+ name: 'root',
+ children: [
+ { name: 'child-1', type: 'leaf' },
+ { name: 'child-2', type: 'leaf' },
+ ],
+ };
+ obj.self = obj;
+ (obj.children as Record[])[0]!.parent = obj;
+ return { data: obj, defaultExpanded: true };
+ })(),
+};
+
+/** Empty object and array edge cases. */
+export const EmptyContainers: Story = {
+ args: {
+ data: {
+ emptyObject: {},
+ emptyArray: [],
+ nonEmpty: { key: 'value' },
+ nonEmptyArray: [1, 2],
+ },
+ defaultExpanded: true,
+ },
+};
+
+/** Large flat object with many properties. */
+export const ManyProperties: Story = {
+ args: {
+ data: Object.fromEntries(
+ Array.from({ length: 50 }, (_, i) => [`property_${i + 1}`, `value_${i + 1}`]),
+ ),
+ defaultExpanded: true,
+ },
+};
+
+/** Complex Kubernetes service list response. */
+export const KubernetesServiceList: Story = {
+ args: {
+ data: {
+ apiVersion: 'v1',
+ kind: 'ServiceList',
+ items: [
+ {
+ metadata: { name: 'kubernetes', namespace: 'default' },
+ spec: {
+ type: 'ClusterIP',
+ clusterIP: '10.96.0.1',
+ ports: [{ port: 443, targetPort: 6443, protocol: 'TCP' }],
+ },
+ },
+ {
+ metadata: { name: 'web-frontend', namespace: 'production' },
+ spec: {
+ type: 'LoadBalancer',
+ clusterIP: '10.96.1.50',
+ ports: [
+ { name: 'http', port: 80, targetPort: 8080, protocol: 'TCP' },
+ { name: 'https', port: 443, targetPort: 8443, protocol: 'TCP' },
+ ],
+ selector: { app: 'web', tier: 'frontend' },
+ },
+ status: {
+ loadBalancer: {
+ ingress: [{ ip: '203.0.113.50' }],
+ },
+ },
+ },
+ ],
+ },
+ defaultExpanded: 2,
+ searchable: true,
+ copyable: true,
+ },
+};
diff --git a/packages/editors/src/components/object-inspector/ObjectInspector.test.tsx b/packages/editors/src/components/object-inspector/ObjectInspector.test.tsx
new file mode 100644
index 0000000..873b9a4
--- /dev/null
+++ b/packages/editors/src/components/object-inspector/ObjectInspector.test.tsx
@@ -0,0 +1,128 @@
+import { render, screen, fireEvent } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { describe, expect, it, vi } from 'vitest';
+import { ObjectInspector } from './ObjectInspector';
+
+const testData = {
+ name: 'test',
+ count: 42,
+ active: true,
+ items: ['a', 'b'],
+ nested: {
+ key: 'value',
+ },
+ nothing: null,
+};
+
+describe('ObjectInspector', () => {
+ it('renders the inspector container', () => {
+ render( );
+ expect(screen.getByTestId('object-inspector')).toBeInTheDocument();
+ });
+
+ it('renders the tree', () => {
+ render( );
+ expect(screen.getByTestId('inspector-tree')).toBeInTheDocument();
+ });
+
+ it('renders root node', () => {
+ render( );
+ expect(screen.getByTestId('inspector-node-root')).toBeInTheDocument();
+ });
+
+ it('expands to default depth', () => {
+ render( );
+ // Root (depth 0) is expanded, children are visible
+ expect(screen.getByTestId('inspector-node-name')).toBeInTheDocument();
+ expect(screen.getByTestId('inspector-node-count')).toBeInTheDocument();
+ });
+
+ it('expands/collapses on click', () => {
+ render( );
+ const root = screen.getByTestId('inspector-node-root');
+ // Initially collapsed — children not visible
+ expect(screen.queryByTestId('inspector-node-name')).not.toBeInTheDocument();
+ fireEvent.click(root);
+ expect(screen.getByTestId('inspector-node-name')).toBeInTheDocument();
+ });
+
+ it('shows search input when searchable', () => {
+ render( );
+ expect(screen.getByTestId('inspector-search')).toBeInTheDocument();
+ });
+
+ it('filters by search query', async () => {
+ const user = userEvent.setup();
+ render( );
+ const search = screen.getByTestId('inspector-search');
+ await user.type(search, 'name');
+ // The "name" node row should have the highlight class
+ const nameNode = screen.getByTestId('inspector-node-name');
+ expect(nameNode.className).toContain('Highlight');
+ });
+
+ it('shows copy button when copyable', () => {
+ render( );
+ expect(screen.getByTestId('inspector-copy')).toBeInTheDocument();
+ });
+
+ it('copies JSON on copy click', async () => {
+ const writeText = vi.fn().mockResolvedValue(undefined);
+ Object.defineProperty(navigator, 'clipboard', {
+ value: { writeText },
+ writable: true,
+ configurable: true,
+ });
+ render( );
+ fireEvent.click(screen.getByTestId('inspector-copy'));
+ expect(writeText).toHaveBeenCalledWith(JSON.stringify(testData, null, 2));
+ });
+
+ it('sets data-format attribute', () => {
+ render( );
+ expect(screen.getByTestId('object-inspector')).toHaveAttribute('data-format', 'yaml');
+ });
+
+ it('merges className', () => {
+ render( );
+ expect(screen.getByTestId('object-inspector')).toHaveClass('custom');
+ });
+
+ it('forwards ref', () => {
+ const ref = { current: null } as unknown as React.RefObject;
+ render( );
+ expect(ref.current).toBeInstanceOf(HTMLDivElement);
+ });
+
+ it('handles circular references without crashing', () => {
+ const circular: Record = { name: 'test' };
+ circular.self = circular;
+ // Should not throw or infinite loop
+ expect(() => render( )).not.toThrow();
+ // The circular ref should show [Circular]
+ expect(screen.getByText('[Circular]')).toBeInTheDocument();
+ });
+
+ it('handles empty object', () => {
+ render( );
+ expect(screen.getByTestId('inspector-node-root')).toBeInTheDocument();
+ });
+
+ it('handles empty array', () => {
+ render( );
+ expect(screen.getByTestId('inspector-node-root')).toBeInTheDocument();
+ });
+
+ it('handles clipboard API failure gracefully', async () => {
+ Object.defineProperty(navigator, 'clipboard', {
+ value: {
+ writeText: vi.fn().mockRejectedValue(new Error('Not allowed')),
+ },
+ writable: true,
+ configurable: true,
+ });
+ render( );
+ // Should not throw
+ expect(() => fireEvent.click(screen.getByTestId('inspector-copy'))).not.toThrow();
+ });
+});
diff --git a/packages/editors/src/components/object-inspector/ObjectInspector.tsx b/packages/editors/src/components/object-inspector/ObjectInspector.tsx
new file mode 100644
index 0000000..1974f99
--- /dev/null
+++ b/packages/editors/src/components/object-inspector/ObjectInspector.tsx
@@ -0,0 +1,420 @@
+import { forwardRef, useCallback, useEffect, useMemo, useRef, useState, type HTMLAttributes } from 'react';
+import styles from './ObjectInspector.module.css';
+
+export type InspectorFormat = 'json' | 'yaml';
+
+export interface ObjectInspectorProps extends HTMLAttributes {
+ data: unknown;
+ format?: InspectorFormat;
+ defaultExpanded?: boolean | number;
+ searchable?: boolean;
+ copyable?: boolean;
+}
+
+function cn(...parts: Array): string {
+ return parts.filter(Boolean).join(' ');
+}
+
+function getType(value: unknown): string {
+ if (value === null) return 'null';
+ if (value === undefined) return 'undefined';
+ if (Array.isArray(value)) return 'array';
+ return typeof value;
+}
+
+function formatValue(value: unknown): string {
+ if (value === null) return 'null';
+ if (value === undefined) return 'undefined';
+ if (typeof value === 'string') return JSON.stringify(value);
+ if (typeof value === 'boolean') return String(value);
+ if (typeof value === 'number') return String(value);
+ return String(value);
+}
+
+function shouldExpand(defaultExpanded: boolean | number, depth: number): boolean {
+ if (typeof defaultExpanded === 'boolean') return defaultExpanded;
+ return depth < defaultExpanded;
+}
+
+function matchesSearch(key: string, value: unknown, query: string): boolean {
+ const q = query.toLowerCase();
+ if (key.toLowerCase().includes(q)) return true;
+ if (typeof value === 'string' && value.toLowerCase().includes(q)) return true;
+ if (typeof value === 'number' && String(value).includes(q)) return true;
+ if (typeof value === 'boolean' && String(value).includes(q)) return true;
+ return false;
+}
+
+/** Check if a value is a non-null object that can be tracked by WeakSet. */
+function isObjectRef(value: unknown): value is object {
+ return value !== null && (typeof value === 'object' || typeof value === 'function');
+}
+
+/** Subset of WeakSet used for ancestor tracking (read-only checks). */
+interface AncestorSet {
+ has(value: object): boolean;
+}
+
+/**
+ * Check if any descendant of `value` matches `query`. Results are cached
+ * per object+query in `cache` to avoid O(n²) re-scanning from each TreeNode.
+ */
+function subtreeMatches(
+ value: unknown,
+ query: string,
+ ancestors: AncestorSet,
+ cache: WeakMap,
+): boolean {
+ if (!query) return false;
+ const type = getType(value);
+ if (type !== 'object' && type !== 'array') return false;
+ if (value === null || value === undefined) return false;
+ if (isObjectRef(value) && ancestors.has(value)) return false;
+
+ // Return cached result if available
+ if (isObjectRef(value) && cache.has(value)) return cache.get(value)!;
+
+ const childAncestors: AncestorSet = {
+ has: (v: object) => v === value || ancestors.has(v),
+ };
+ let result = false;
+ for (const [k, v] of Object.entries(value as Record)) {
+ if (matchesSearch(k, v, query) || subtreeMatches(v, query, childAncestors, cache)) {
+ result = true;
+ break;
+ }
+ }
+
+ if (isObjectRef(value)) cache.set(value, result);
+ return result;
+}
+
+interface TreeNodeProps {
+ nodeKey: string;
+ value: unknown;
+ depth: number;
+ defaultExpanded: boolean | number;
+ searchQuery: string;
+ isLast: boolean;
+ /** Ancestor object references for circular reference detection. */
+ ancestors: AncestorSet;
+ /** Per-query cache for subtreeMatches results (shared across all nodes). */
+ searchCache: WeakMap;
+}
+
+function TreeNode({
+ nodeKey,
+ value,
+ depth,
+ defaultExpanded,
+ searchQuery,
+ isLast,
+ ancestors,
+ searchCache,
+}: TreeNodeProps) {
+ const type = getType(value);
+ const isExpandable = type === 'object' || type === 'array';
+
+ // Detect circular references by checking if this value is already an ancestor
+ const isCircular = isExpandable && isObjectRef(value) && ancestors.has(value);
+
+ const [expanded, setExpanded] = useState(
+ () => isExpandable && !isCircular && shouldExpand(defaultExpanded, depth),
+ );
+
+ // Track whether the node was force-expanded by search so we can restore
+ // the exact pre-search state when the search is cleared.
+ const searchExpandedRef = useRef(false);
+ const preSearchExpandedRef = useRef(false);
+
+ // Auto-expand when search matches a descendant
+ const hasDescendantMatch = useMemo(
+ () => searchQuery ? subtreeMatches(value, searchQuery, ancestors, searchCache) : false,
+ [searchQuery, value, ancestors, searchCache],
+ );
+
+ // Force expand when search finds matches in descendants
+ useEffect(() => {
+ if (searchQuery && hasDescendantMatch && isExpandable && !isCircular && !expanded) {
+ // Only snapshot pre-search state the first time we force-open
+ if (!searchExpandedRef.current) {
+ preSearchExpandedRef.current = expanded;
+ }
+ searchExpandedRef.current = true;
+ setExpanded(true);
+ }
+ // Restore pre-search state when search clears OR node stops matching
+ if (searchExpandedRef.current && (!searchQuery || !hasDescendantMatch)) {
+ searchExpandedRef.current = false;
+ setExpanded(preSearchExpandedRef.current);
+ }
+ }, [searchQuery, hasDescendantMatch, isExpandable, isCircular, expanded]);
+
+ // Create a new ancestors set that includes this value for children.
+ // WeakSet doesn't support iteration, so we use a duck-typed wrapper
+ // that checks both the parent set and the current value.
+ const childAncestors = useMemo(() => {
+ if (!isExpandable || !isObjectRef(value) || isCircular) return ancestors;
+ return {
+ has: (v: object) => v === value || ancestors.has(v),
+ };
+ }, [isExpandable, value, isCircular, ancestors]);
+
+ const entries = useMemo(() => {
+ if (!isExpandable || value === null || value === undefined || isCircular) return [];
+ return Object.entries(value as Record);
+ }, [isExpandable, value, isCircular]);
+
+ const toggle = useCallback(() => {
+ if (isExpandable && !isCircular) setExpanded((prev) => !prev);
+ }, [isExpandable, isCircular]);
+
+ const isHighlighted = searchQuery && matchesSearch(nodeKey, value, searchQuery);
+
+ const preview = isCircular
+ ? '[Circular]'
+ : isExpandable
+ ? type === 'array'
+ ? `Array(${(value as unknown[]).length})`
+ : `{${entries.length}}`
+ : null;
+
+ return (
+
+
{
+ if (e.key === 'Enter' || e.key === ' ') {
+ e.preventDefault();
+ toggle();
+ }
+ }}
+ tabIndex={0}
+ role="treeitem"
+ aria-expanded={isExpandable && !isCircular ? expanded : undefined}
+ data-testid={`inspector-node-${nodeKey}`}
+ >
+ {isExpandable && !isCircular ? (
+ ▸
+ ) : (
+
+ )}
+ {nodeKey}
+ :
+ {isExpandable ? (
+ {preview}
+ ) : (
+
+ {formatValue(value)}
+
+ )}
+ {!isLast && !expanded && , }
+
+ {expanded &&
+ entries.map(([k, v], i) => (
+
+ ))}
+
+ );
+}
+
+/** Shared empty WeakSet used as the initial ancestors set. */
+const EMPTY_ANCESTORS: AncestorSet = new WeakSet();
+
+export const ObjectInspector = forwardRef(
+ function ObjectInspector(
+ {
+ data,
+ format = 'json',
+ defaultExpanded = 1,
+ searchable = false,
+ copyable = false,
+ className,
+ ...props
+ },
+ ref,
+ ) {
+ const [searchQuery, setSearchQuery] = useState('');
+
+ const handleCopy = useCallback(async () => {
+ try {
+ let text: string;
+ if (format === 'yaml') {
+ text = toYaml(data);
+ } else {
+ text = safeJsonStringify(data);
+ }
+ await navigator.clipboard.writeText(text);
+ } catch {
+ // Clipboard API may not be available (e.g., insecure context)
+ }
+ }, [data, format]);
+
+ // Root starts with an empty ancestors set — the root node itself
+ // is not its own ancestor, but its children will see it as one
+ const rootAncestors = EMPTY_ANCESTORS;
+
+ // Per-query cache for subtreeMatches — invalidated when query changes
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ const searchCache = useMemo(() => new WeakMap(), [searchQuery]);
+
+ return (
+
+ {(searchable || copyable) && (
+
+ {searchable && (
+ setSearchQuery(e.target.value)}
+ placeholder="Search keys/values…"
+ aria-label="Search object"
+ data-testid="inspector-search"
+ />
+ )}
+ {copyable && (
+
+ Copy
+
+ )}
+
+ )}
+
+
+
+
+ );
+ },
+);
+
+ObjectInspector.displayName = 'ObjectInspector';
+
+/** JSON.stringify with circular reference safety (ancestor-stack approach). */
+function safeJsonStringify(value: unknown): string {
+ // Use an ancestor stack so shared (but non-cyclic) references serialize fully.
+ // JSON.stringify calls the replacer with `this` bound to the object that
+ // contains the current key. We track ancestors by pushing on enter and
+ // popping when we leave.
+ const stack: object[] = [];
+ return JSON.stringify(
+ value,
+ function (this: unknown, _key: string, val: unknown) {
+ if (typeof val !== 'object' || val === null) return val;
+
+ // Trim the stack: `this` is the parent container. Pop until the
+ // top of the stack IS the parent (or the stack is empty).
+ while (stack.length > 0 && stack[stack.length - 1] !== this) {
+ stack.pop();
+ }
+
+ if (stack.includes(val as object)) return '[Circular]';
+ stack.push(val as object);
+ return val;
+ },
+ 2,
+ );
+}
+
+/** Check if a YAML scalar needs quoting. */
+function needsYamlQuoting(s: string): boolean {
+ if (s === '') return true;
+ if (/^[\s]/.test(s) || /[\s]$/.test(s)) return true;
+ // Starts with indicator characters or reserved values
+ if (/^[-?:,[\]{}#&*!|>'"%@`]/.test(s)) return true;
+ // Contains colon+space, hash+space, or other flow indicators
+ if (/[:\s]#|:\s|[{}[\],]/.test(s)) return true;
+ // YAML boolean/null literals (case-insensitive)
+ if (/^(true|false|yes|no|on|off|null|~)$/i.test(s)) return true;
+ // Looks like a number
+ if (/^[+-]?(\d+\.?\d*|\.inf|\.nan)$/i.test(s)) return true;
+ return false;
+}
+
+/** Quote a YAML scalar with double quotes, escaping special chars. */
+function quoteYaml(s: string): string {
+ return '"' + s.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n').replace(/\t/g, '\\t').replace(/\r/g, '\\r') + '"';
+}
+
+/** Safely serialize a string as a YAML scalar. */
+function yamlScalar(s: string, indent: number): string {
+ const prefix = ' '.repeat(indent);
+ // Multi-line strings use block scalar
+ if (s.includes('\n')) {
+ return `|\n${prefix} ${s.split('\n').join(`\n${prefix} `)}`;
+ }
+ return needsYamlQuoting(s) ? quoteYaml(s) : s;
+}
+
+/** Simple YAML serializer (no dependency, ancestor-stack approach) */
+function toYaml(value: unknown, indent: number = 0, ancestors?: object[]): string {
+ const prefix = ' '.repeat(indent);
+ if (value === null) return 'null';
+ if (value === undefined) return 'null';
+ if (typeof value === 'string') return yamlScalar(value, indent);
+ if (typeof value === 'number' || typeof value === 'boolean') return String(value);
+
+ if (typeof value === 'object') {
+ const stack = ancestors ?? [];
+ if (stack.includes(value)) return '"[Circular]"';
+ const childStack = [...stack, value];
+
+ if (Array.isArray(value)) {
+ if (value.length === 0) return '[]';
+ return value
+ .map((item) => `${prefix}- ${toYaml(item, indent + 1, childStack).trimStart()}`)
+ .join('\n');
+ }
+
+ const entries = Object.entries(value as Record);
+ if (entries.length === 0) return '{}';
+ return entries
+ .map(([k, v]) => {
+ const safeKey = needsYamlQuoting(k) ? quoteYaml(k) : k;
+ const serialized = toYaml(v, indent + 1, childStack);
+ if (
+ typeof v === 'object' &&
+ v !== null &&
+ (Array.isArray(v) ? v.length > 0 : Object.keys(v).length > 0)
+ ) {
+ return `${prefix}${safeKey}:\n${serialized}`;
+ }
+ return `${prefix}${safeKey}: ${serialized}`;
+ })
+ .join('\n');
+ }
+
+ return String(value);
+}
diff --git a/packages/editors/src/components/object-inspector/index.ts b/packages/editors/src/components/object-inspector/index.ts
new file mode 100644
index 0000000..0bcb698
--- /dev/null
+++ b/packages/editors/src/components/object-inspector/index.ts
@@ -0,0 +1,2 @@
+export { ObjectInspector } from './ObjectInspector';
+export type { ObjectInspectorProps, InspectorFormat } from './ObjectInspector';
diff --git a/packages/editors/src/components/terminal/Terminal.module.css b/packages/editors/src/components/terminal/Terminal.module.css
new file mode 100644
index 0000000..d4f2fbf
--- /dev/null
+++ b/packages/editors/src/components/terminal/Terminal.module.css
@@ -0,0 +1,11 @@
+.Root {
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+ background: var(--ov-color-terminal-bg, var(--ov-color-bg-surface));
+ border: 1px solid var(--ov-color-terminal-border, var(--ov-color-border-default));
+ border-radius: var(--ov-radius-sm, 4px);
+ padding: var(--ov-space-1, 4px);
+ /* Prevent parent sans-serif font from leaking into xterm's rendering */
+ font-family: var(--ov-font-family-mono, monospace);
+}
diff --git a/packages/editors/src/components/terminal/Terminal.stories.tsx b/packages/editors/src/components/terminal/Terminal.stories.tsx
new file mode 100644
index 0000000..0ab4c7d
--- /dev/null
+++ b/packages/editors/src/components/terminal/Terminal.stories.tsx
@@ -0,0 +1,517 @@
+import type { Meta, StoryObj } from '@storybook/react';
+import { useRef, useEffect, useState } from 'react';
+import { Terminal, type TerminalHandle, type TerminalProps } from './Terminal';
+
+const meta: Meta = {
+ title: 'Editors/Terminal',
+ component: Terminal,
+ tags: ['autodocs'],
+ args: {
+ fontSize: 13,
+ },
+ argTypes: {
+ fontSize: { control: { type: 'range', min: 10, max: 24, step: 1 } },
+ scrollback: { control: { type: 'number', min: 0, max: 50000, step: 500 } },
+ cursorBlink: { control: 'boolean' },
+ cursorStyle: { control: 'select', options: ['block', 'underline', 'bar'] },
+ convertEol: { control: 'boolean' },
+ macOptionIsMeta: { control: 'boolean' },
+ macOptionClickForcesSelection: { control: 'boolean' },
+ linkHandling: { control: 'boolean' },
+ allowTransparency: { control: 'boolean' },
+ disableStdin: { control: 'boolean' },
+ renderer: { control: 'select', options: ['auto', 'webgl', 'dom'] },
+ lineHeight: { control: { type: 'range', min: 1.0, max: 2.0, step: 0.1 } },
+ letterSpacing: { control: { type: 'range', min: -2, max: 5, step: 0.5 } },
+ screenReaderMode: { control: 'boolean' },
+ minimumContrastRatio: { control: { type: 'range', min: 1, max: 21, step: 0.5 } },
+ },
+ decorators: [
+ (Story) => (
+
+
+
+ ),
+ ],
+};
+
+export default meta;
+type Story = StoryObj;
+
+export const Playground: Story = {};
+
+/** Writes simulated `ls -la` output to demonstrate basic text rendering. */
+function WithOutputStory(args: TerminalProps) {
+ const ref = useRef(null);
+
+ useEffect(() => {
+ const timeout = setTimeout(() => {
+ if (ref.current) {
+ ref.current.writeln('$ ls -la');
+ ref.current.writeln('total 48');
+ ref.current.writeln('drwxr-xr-x 8 user staff 256 Jan 1 12:00 .');
+ ref.current.writeln('drwxr-xr-x 10 user staff 320 Jan 1 11:00 ..');
+ ref.current.writeln('-rw-r--r-- 1 user staff 1234 Jan 1 12:00 package.json');
+ ref.current.writeln('-rw-r--r-- 1 user staff 567 Jan 1 12:00 tsconfig.json');
+ ref.current.writeln('drwxr-xr-x 5 user staff 160 Jan 1 12:00 src');
+ ref.current.writeln('');
+ ref.current.write('$ ');
+ }
+ }, 500);
+ return () => clearTimeout(timeout);
+ }, []);
+
+ return ;
+}
+
+export const WithOutput: Story = {
+ render: (args) => ,
+};
+
+/** Demonstrates ANSI color codes in terminal output. */
+function AnsiColorsStory(args: TerminalProps) {
+ const ref = useRef(null);
+
+ useEffect(() => {
+ const timeout = setTimeout(() => {
+ if (!ref.current) return;
+ const t = ref.current;
+ t.writeln('\x1b[1mBold text\x1b[0m');
+ t.writeln('\x1b[3mItalic text\x1b[0m');
+ t.writeln('\x1b[4mUnderlined text\x1b[0m');
+ t.writeln('');
+ t.writeln('Standard colors:');
+ t.writeln(
+ ' \x1b[30mBlack\x1b[0m \x1b[31mRed\x1b[0m \x1b[32mGreen\x1b[0m \x1b[33mYellow\x1b[0m \x1b[34mBlue\x1b[0m \x1b[35mMagenta\x1b[0m \x1b[36mCyan\x1b[0m \x1b[37mWhite\x1b[0m',
+ );
+ t.writeln('');
+ t.writeln('Bright colors:');
+ t.writeln(
+ ' \x1b[90mBlack\x1b[0m \x1b[91mRed\x1b[0m \x1b[92mGreen\x1b[0m \x1b[93mYellow\x1b[0m \x1b[94mBlue\x1b[0m \x1b[95mMagenta\x1b[0m \x1b[96mCyan\x1b[0m \x1b[97mWhite\x1b[0m',
+ );
+ t.writeln('');
+ t.writeln('Background colors:');
+ t.writeln(
+ ' \x1b[41m Red \x1b[0m \x1b[42m Green \x1b[0m \x1b[43m Yellow \x1b[0m \x1b[44m Blue \x1b[0m \x1b[45m Magenta \x1b[0m \x1b[46m Cyan \x1b[0m',
+ );
+ t.writeln('');
+ t.write('$ ');
+ }, 300);
+ return () => clearTimeout(timeout);
+ }, []);
+
+ return ;
+}
+
+export const AnsiColors: Story = {
+ render: (args) => ,
+};
+
+/** Demonstrates high-throughput output with rapid line writes. */
+function HighThroughputStory(args: TerminalProps) {
+ const ref = useRef(null);
+
+ useEffect(() => {
+ const timeout = setTimeout(() => {
+ if (!ref.current) return;
+ ref.current.writeln('$ cat /var/log/system.log | head -200');
+ for (let i = 0; i < 200; i++) {
+ const level =
+ i % 10 === 0
+ ? '\x1b[31mERROR\x1b[0m'
+ : i % 5 === 0
+ ? '\x1b[33mWARN\x1b[0m'
+ : '\x1b[32mINFO\x1b[0m';
+ ref.current.writeln(
+ `2024-01-15T10:${String(i % 60).padStart(2, '0')}:00Z ${level} service.handler Request processed in ${Math.floor(Math.random() * 500)}ms`,
+ );
+ }
+ ref.current.writeln('');
+ ref.current.write('$ ');
+ }, 300);
+ return () => clearTimeout(timeout);
+ }, []);
+
+ return ;
+}
+
+export const HighThroughput: Story = {
+ render: (args) => ,
+};
+
+/** Interactive terminal that echoes typed input. */
+function InteractiveStory(args: TerminalProps) {
+ const ref = useRef(null);
+ const [line, setLine] = useState('');
+
+ const handleData = (data: string) => {
+ if (!ref.current) return;
+ if (data === '\r') {
+ ref.current.writeln('');
+ if (line.trim()) {
+ ref.current.writeln(`\x1b[36m> ${line}\x1b[0m`);
+ }
+ setLine('');
+ ref.current.write('$ ');
+ } else if (data === '\x7f') {
+ // Backspace
+ if (line.length > 0) {
+ setLine((prev) => prev.slice(0, -1));
+ ref.current.write('\b \b');
+ }
+ } else {
+ setLine((prev) => prev + data);
+ ref.current.write(data);
+ }
+ };
+
+ useEffect(() => {
+ const timeout = setTimeout(() => {
+ ref.current?.writeln('Interactive echo terminal. Type something and press Enter.');
+ ref.current?.write('$ ');
+ }, 300);
+ return () => clearTimeout(timeout);
+ }, []);
+
+ return ;
+}
+
+export const Interactive: Story = {
+ render: (args) => ,
+};
+
+/** Demonstrates the onResize callback by displaying current dimensions. */
+function ResizeDemoStory(args: TerminalProps) {
+ const ref = useRef(null);
+
+ const handleResize = (cols: number, rows: number) => {
+ ref.current?.writeln(`\x1b[33m[resize] ${cols} cols x ${rows} rows\x1b[0m`);
+ };
+
+ useEffect(() => {
+ const timeout = setTimeout(() => {
+ const dims = ref.current?.getDimensions();
+ ref.current?.writeln('Resize this panel to see dimension changes.');
+ if (dims) {
+ ref.current?.writeln(`Current: ${dims.cols} cols x ${dims.rows} rows`);
+ }
+ ref.current?.writeln('');
+ }, 500);
+ return () => clearTimeout(timeout);
+ }, []);
+
+ return ;
+}
+
+export const ResizeDemo: Story = {
+ render: (args) => ,
+ decorators: [
+ (Story) => (
+
+
+
+ ),
+ ],
+};
+
+/** Shows different font sizes side by side. */
+function FontSizesStory() {
+ return (
+
+ {[10, 13, 16, 20].map((size) => (
+
+ ))}
+
+ );
+}
+
+function FontSizePanel({ fontSize }: { fontSize: number }) {
+ const ref = useRef(null);
+ useEffect(() => {
+ const t = setTimeout(() => {
+ ref.current?.writeln(`fontSize: ${fontSize}`);
+ ref.current?.writeln('$ echo "Hello"');
+ ref.current?.writeln('Hello');
+ }, 300);
+ return () => clearTimeout(t);
+ }, [fontSize]);
+ return (
+
+
+
+ );
+}
+
+export const FontSizes: Story = {
+ render: () => ,
+ decorators: [],
+};
+
+/** Large scrollback buffer demonstration. */
+function LargeScrollbackStory(args: TerminalProps) {
+ const ref = useRef(null);
+
+ useEffect(() => {
+ const timeout = setTimeout(() => {
+ if (!ref.current) return;
+ for (let i = 1; i <= 1000; i++) {
+ ref.current.writeln(`Line ${i}: ${'x'.repeat(40)}`);
+ }
+ ref.current.writeln('');
+ ref.current.writeln('\x1b[32mScroll up to see 1000 lines of output.\x1b[0m');
+ }, 300);
+ return () => clearTimeout(timeout);
+ }, []);
+
+ return ;
+}
+
+export const LargeScrollback: Story = {
+ render: (args) => ,
+};
+
+/** Demonstrates clickable URL links in terminal output. */
+function ClickableLinksStory(args: TerminalProps) {
+ const ref = useRef(null);
+
+ useEffect(() => {
+ const timeout = setTimeout(() => {
+ if (!ref.current) return;
+ ref.current.writeln('Terminal with clickable links (hover to see):');
+ ref.current.writeln('');
+ ref.current.writeln(' https://github.com/omniviewdev/omniview');
+ ref.current.writeln(' https://example.com/api/v1/resources');
+ ref.current.writeln(' http://localhost:3000/dashboard');
+ ref.current.writeln('');
+ ref.current.write('$ ');
+ }, 300);
+ return () => clearTimeout(timeout);
+ }, []);
+
+ return ;
+}
+
+export const ClickableLinks: Story = {
+ render: (args) => ,
+};
+
+/** Search story — 100 lines of log output with search bar UI. */
+function SearchStory(args: TerminalProps) {
+ const ref = useRef(null);
+ const [searchTerm, setSearchTerm] = useState('ERROR');
+
+ useEffect(() => {
+ const timeout = setTimeout(() => {
+ if (!ref.current) return;
+ for (let i = 0; i < 100; i++) {
+ const level =
+ i % 10 === 0
+ ? '\x1b[31mERROR\x1b[0m'
+ : i % 5 === 0
+ ? '\x1b[33mWARN\x1b[0m'
+ : '\x1b[32mINFO\x1b[0m';
+ ref.current.writeln(
+ `[${String(i).padStart(3, '0')}] ${level} Processing request #${i + 1000}`,
+ );
+ }
+ }, 300);
+ return () => clearTimeout(timeout);
+ }, []);
+
+ return (
+
+
+ setSearchTerm(e.target.value)}
+ placeholder="Search..."
+ style={{ padding: '4px 8px', background: '#2d2d2d', color: '#fff', border: '1px solid #555', borderRadius: 4, fontSize: 13 }}
+ />
+ ref.current?.findNext(searchTerm)}
+ style={{ padding: '4px 12px', background: '#0e639c', color: '#fff', border: 'none', borderRadius: 4, cursor: 'pointer', fontSize: 13 }}
+ >
+ Find Next
+
+ ref.current?.findPrevious(searchTerm)}
+ style={{ padding: '4px 12px', background: '#0e639c', color: '#fff', border: 'none', borderRadius: 4, cursor: 'pointer', fontSize: 13 }}
+ >
+ Find Previous
+
+ ref.current?.clearSearch()}
+ style={{ padding: '4px 12px', background: '#333', color: '#fff', border: 'none', borderRadius: 4, cursor: 'pointer', fontSize: 13 }}
+ >
+ Clear
+
+
+
+
+
+
+ );
+}
+
+export const Search: Story = {
+ render: (args) => ,
+};
+
+/** ReadOnly terminal that streams simulated log output. */
+function ReadOnlyStory(args: TerminalProps) {
+ const ref = useRef(null);
+
+ useEffect(() => {
+ let i = 0;
+ const interval = setInterval(() => {
+ if (!ref.current) return;
+ const level = i % 7 === 0 ? '\x1b[31mERROR\x1b[0m' : i % 3 === 0 ? '\x1b[33mWARN\x1b[0m' : '\x1b[32mINFO\x1b[0m';
+ const ts = new Date().toISOString();
+ ref.current.writeln(`${ts} ${level} app.server Event #${i + 1}`);
+ i++;
+ }, 500);
+
+ return () => clearInterval(interval);
+ }, []);
+
+ return ;
+}
+
+export const ReadOnly: Story = {
+ render: (args) => ,
+};
+
+/** Three terminals side-by-side comparing renderers. */
+function RendererComparisonStory() {
+ return (
+
+ {(['webgl', 'dom'] as const).map((r) => (
+
+ ))}
+
+ );
+}
+
+function RendererPanel({ renderer }: { renderer: 'webgl' | 'dom' }) {
+ const ref = useRef(null);
+ useEffect(() => {
+ const t = setTimeout(() => {
+ if (!ref.current) return;
+ ref.current.writeln(`\x1b[1mRenderer: ${renderer}\x1b[0m`);
+ ref.current.writeln('');
+ ref.current.writeln('The quick brown fox jumps');
+ ref.current.writeln('over the lazy dog.');
+ ref.current.writeln('');
+ ref.current.writeln('\x1b[31mRed\x1b[0m \x1b[32mGreen\x1b[0m \x1b[34mBlue\x1b[0m');
+ }, 400);
+ return () => clearTimeout(t);
+ }, [renderer]);
+
+ return (
+
+
+ {renderer.toUpperCase()}
+
+
+
+
+
+ );
+}
+
+export const RendererComparison: Story = {
+ render: () => ,
+ decorators: [],
+};
+
+/** Serialization — save the buffer content. */
+function SerializationStory(args: TerminalProps) {
+ const ref = useRef(null);
+ const [buffer, setBuffer] = useState('');
+
+ useEffect(() => {
+ const timeout = setTimeout(() => {
+ if (!ref.current) return;
+ ref.current.writeln('$ echo "Hello from the terminal"');
+ ref.current.writeln('Hello from the terminal');
+ ref.current.writeln('$ date');
+ ref.current.writeln('Mon Jan 15 10:30:00 UTC 2024');
+ ref.current.writeln('$ whoami');
+ ref.current.writeln('developer');
+ ref.current.write('$ ');
+ }, 300);
+ return () => clearTimeout(timeout);
+ }, []);
+
+ return (
+
+
+ setBuffer(ref.current?.getBufferContent() ?? '')}
+ style={{ padding: '4px 12px', background: '#0e639c', color: '#fff', border: 'none', borderRadius: 4, cursor: 'pointer', fontSize: 13 }}
+ >
+ Save Buffer
+
+
+
+
+
+ {buffer && (
+
+ {buffer}
+
+ )}
+
+ );
+}
+
+export const Serialization: Story = {
+ render: (args) => ,
+};
+
+/** Custom key handler — intercepts Ctrl+C to show custom behavior. */
+function CustomKeyHandlerStory(args: TerminalProps) {
+ const ref = useRef(null);
+ const [intercepted, setIntercepted] = useState(false);
+
+ const handleKey = (event: KeyboardEvent) => {
+ if (event.ctrlKey && event.key === 'c') {
+ setIntercepted(true);
+ ref.current?.writeln('\r\n\x1b[33m[Ctrl+C intercepted by custom handler]\x1b[0m');
+ ref.current?.write('$ ');
+ setTimeout(() => setIntercepted(false), 1500);
+ return false; // Prevent default xterm handling
+ }
+ return true;
+ };
+
+ useEffect(() => {
+ const timeout = setTimeout(() => {
+ ref.current?.writeln('Type Ctrl+C to see the custom key handler intercept it.');
+ ref.current?.write('$ ');
+ }, 300);
+ return () => clearTimeout(timeout);
+ }, []);
+
+ return (
+
+ {intercepted && (
+
+ Ctrl+C was intercepted!
+
+ )}
+
+
+
+
+ );
+}
+
+export const CustomKeyHandler: Story = {
+ render: (args) => ,
+};
diff --git a/packages/editors/src/components/terminal/Terminal.test.tsx b/packages/editors/src/components/terminal/Terminal.test.tsx
new file mode 100644
index 0000000..f5ffd79
--- /dev/null
+++ b/packages/editors/src/components/terminal/Terminal.test.tsx
@@ -0,0 +1,936 @@
+import { render, screen, act } from '@testing-library/react';
+import { createRef } from 'react';
+import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest';
+import { Terminal, type TerminalHandle } from './Terminal';
+
+// Mock xterm modules
+const mockOnData = vi.fn();
+const mockOnBinary = vi.fn();
+const mockOnResize = vi.fn();
+const mockOnBell = vi.fn();
+const mockOnTitleChange = vi.fn();
+const mockOnKey = vi.fn();
+const mockOnSelectionChange = vi.fn();
+const mockOnLineFeed = vi.fn();
+const mockOnScroll = vi.fn();
+const mockOnWriteParsed = vi.fn();
+
+const mockTerminal = {
+ open: vi.fn(),
+ dispose: vi.fn(),
+ loadAddon: vi.fn(),
+ attachCustomKeyEventHandler: vi.fn(),
+ onData: vi.fn((cb) => {
+ mockOnData.mockImplementation(cb);
+ }),
+ onBinary: vi.fn((cb) => {
+ mockOnBinary.mockImplementation(cb);
+ }),
+ onResize: vi.fn((cb) => {
+ mockOnResize.mockImplementation(cb);
+ }),
+ onBell: vi.fn((cb) => {
+ mockOnBell.mockImplementation(cb);
+ }),
+ onTitleChange: vi.fn((cb) => {
+ mockOnTitleChange.mockImplementation(cb);
+ }),
+ onKey: vi.fn((cb) => {
+ mockOnKey.mockImplementation(cb);
+ }),
+ onSelectionChange: vi.fn((cb) => {
+ mockOnSelectionChange.mockImplementation(cb);
+ }),
+ onLineFeed: vi.fn((cb) => {
+ mockOnLineFeed.mockImplementation(cb);
+ }),
+ onScroll: vi.fn((cb) => {
+ mockOnScroll.mockImplementation(cb);
+ }),
+ onWriteParsed: vi.fn((cb) => {
+ mockOnWriteParsed.mockImplementation(cb);
+ }),
+ write: vi.fn(),
+ writeln: vi.fn(),
+ clear: vi.fn(),
+ focus: vi.fn(),
+ blur: vi.fn(),
+ reset: vi.fn(),
+ scrollToBottom: vi.fn(),
+ getSelection: vi.fn(() => 'selected-text'),
+ hasSelection: vi.fn(() => true),
+ selectAll: vi.fn(),
+ clearSelection: vi.fn(),
+ scrollToLine: vi.fn(),
+ scrollLines: vi.fn(),
+ paste: vi.fn(),
+ cols: 80,
+ rows: 24,
+ options: {} as Record,
+ unicode: { activeVersion: '6' },
+};
+
+const mockFitAddon = {
+ fit: vi.fn(),
+ dispose: vi.fn(),
+};
+
+const mockSearchAddon = {
+ findNext: vi.fn(() => true),
+ findPrevious: vi.fn(() => true),
+ clearDecorations: vi.fn(),
+ dispose: vi.fn(),
+};
+
+const mockSerializeAddon = {
+ serialize: vi.fn(() => 'buffer-content'),
+ dispose: vi.fn(),
+};
+
+const mockUnicode11Addon = {
+ dispose: vi.fn(),
+};
+
+const mockWebglAddon = {
+ onContextLoss: vi.fn(),
+ dispose: vi.fn(),
+};
+
+const mockWebLinksAddon = {
+ dispose: vi.fn(),
+};
+
+vi.mock('@xterm/xterm', () => ({
+ Terminal: vi.fn(() => mockTerminal),
+}));
+
+vi.mock('@xterm/addon-fit', () => ({
+ FitAddon: vi.fn(() => mockFitAddon),
+}));
+
+vi.mock('@xterm/addon-search', () => ({
+ SearchAddon: vi.fn(() => mockSearchAddon),
+}));
+
+vi.mock('@xterm/addon-serialize', () => ({
+ SerializeAddon: vi.fn(() => mockSerializeAddon),
+}));
+
+vi.mock('@xterm/addon-unicode11', () => ({
+ Unicode11Addon: vi.fn(() => mockUnicode11Addon),
+}));
+
+vi.mock('@xterm/addon-webgl', () => ({
+ WebglAddon: vi.fn(() => mockWebglAddon),
+}));
+
+vi.mock('@xterm/addon-web-links', () => ({
+ WebLinksAddon: vi.fn(() => mockWebLinksAddon),
+}));
+
+// Mock document.fonts.ready
+Object.defineProperty(document, 'fonts', {
+ value: { ready: Promise.resolve() },
+ writable: true,
+});
+
+// Mock ResizeObserver
+let resizeCallback: (() => void) | null = null;
+const mockObserve = vi.fn();
+const mockDisconnect = vi.fn();
+
+beforeEach(() => {
+ vi.clearAllMocks();
+ resizeCallback = null;
+ mockTerminal.unicode.activeVersion = '6';
+ global.ResizeObserver = vi.fn().mockImplementation((cb) => {
+ resizeCallback = cb;
+ return {
+ observe: mockObserve,
+ unobserve: vi.fn(),
+ disconnect: mockDisconnect,
+ };
+ });
+});
+
+afterEach(() => {
+ vi.restoreAllMocks();
+});
+
+async function waitForInit() {
+ await act(async () => {
+ await new Promise((r) => setTimeout(r, 10));
+ });
+}
+
+describe('Terminal', () => {
+ it('renders terminal container', () => {
+ render( );
+ expect(screen.getByTestId('terminal')).toBeInTheDocument();
+ });
+
+ it('initializes xterm on mount', async () => {
+ render( );
+ await waitForInit();
+ expect(mockTerminal.open).toHaveBeenCalled();
+ });
+
+ it('loads fit, search, serialize, and unicode11 addons', async () => {
+ render( );
+ await waitForInit();
+ expect(mockTerminal.loadAddon).toHaveBeenCalledWith(mockFitAddon);
+ expect(mockTerminal.loadAddon).toHaveBeenCalledWith(mockSearchAddon);
+ expect(mockTerminal.loadAddon).toHaveBeenCalledWith(mockSerializeAddon);
+ expect(mockTerminal.loadAddon).toHaveBeenCalledWith(mockUnicode11Addon);
+ });
+
+ it('activates unicode 11', async () => {
+ render( );
+ await waitForInit();
+ expect(mockTerminal.unicode.activeVersion).toBe('11');
+ });
+
+ it('loads web links addon by default', async () => {
+ render( );
+ await waitForInit();
+ expect(mockTerminal.loadAddon).toHaveBeenCalledWith(mockWebLinksAddon);
+ });
+
+ it('skips web links addon when linkHandling is false', async () => {
+ render( );
+ await waitForInit();
+ expect(mockTerminal.loadAddon).not.toHaveBeenCalledWith(mockWebLinksAddon);
+ });
+
+ it('calls initial fit after opening', async () => {
+ render( );
+ await waitForInit();
+ expect(mockFitAddon.fit).toHaveBeenCalled();
+ });
+
+ it('sets up ResizeObserver on container', async () => {
+ render( );
+ await waitForInit();
+ expect(mockObserve).toHaveBeenCalled();
+ });
+
+ it('calls onData through stable ref', async () => {
+ const onData = vi.fn();
+ render( );
+ await waitForInit();
+ expect(mockTerminal.onData).toHaveBeenCalled();
+ mockOnData('test-input');
+ expect(onData).toHaveBeenCalledWith('test-input');
+ });
+
+ it('calls onBinaryData through stable ref', async () => {
+ const onBinaryData = vi.fn();
+ render( );
+ await waitForInit();
+ expect(mockTerminal.onBinary).toHaveBeenCalled();
+ mockOnBinary('binary-data');
+ expect(onBinaryData).toHaveBeenCalledWith('binary-data');
+ });
+
+ it('calls onResize through stable ref', async () => {
+ const onResize = vi.fn();
+ render( );
+ await waitForInit();
+ expect(mockTerminal.onResize).toHaveBeenCalled();
+ mockOnResize({ cols: 120, rows: 40 });
+ expect(onResize).toHaveBeenCalledWith(120, 40);
+ });
+
+ it('calls onBell through stable ref', async () => {
+ const onBell = vi.fn();
+ render( );
+ await waitForInit();
+ expect(mockTerminal.onBell).toHaveBeenCalled();
+ mockOnBell();
+ expect(onBell).toHaveBeenCalled();
+ });
+
+ it('calls onTitleChange through stable ref', async () => {
+ const onTitleChange = vi.fn();
+ render( );
+ await waitForInit();
+ expect(mockTerminal.onTitleChange).toHaveBeenCalled();
+ mockOnTitleChange('new-title');
+ expect(onTitleChange).toHaveBeenCalledWith('new-title');
+ });
+
+ it('calls onKey through stable ref', async () => {
+ const onKey = vi.fn();
+ render( );
+ await waitForInit();
+ expect(mockTerminal.onKey).toHaveBeenCalled();
+ const event = { key: 'a', domEvent: new KeyboardEvent('keydown') };
+ mockOnKey(event);
+ expect(onKey).toHaveBeenCalledWith(event);
+ });
+
+ it('calls onSelectionChange through stable ref', async () => {
+ const onSelectionChange = vi.fn();
+ render( );
+ await waitForInit();
+ expect(mockTerminal.onSelectionChange).toHaveBeenCalled();
+ mockOnSelectionChange();
+ expect(onSelectionChange).toHaveBeenCalled();
+ });
+
+ it('calls onLineFeed through stable ref', async () => {
+ const onLineFeed = vi.fn();
+ render( );
+ await waitForInit();
+ expect(mockTerminal.onLineFeed).toHaveBeenCalled();
+ mockOnLineFeed();
+ expect(onLineFeed).toHaveBeenCalled();
+ });
+
+ it('calls onScroll through stable ref', async () => {
+ const onScroll = vi.fn();
+ render( );
+ await waitForInit();
+ expect(mockTerminal.onScroll).toHaveBeenCalled();
+ mockOnScroll(42);
+ expect(onScroll).toHaveBeenCalledWith(42);
+ });
+
+ it('handles WebGL context loss gracefully', async () => {
+ render( );
+ await waitForInit();
+ expect(mockWebglAddon.onContextLoss).toHaveBeenCalled();
+ const contextLossHandler = mockWebglAddon.onContextLoss.mock.calls[0]?.[0] as () => void;
+ contextLossHandler();
+ expect(mockWebglAddon.dispose).toHaveBeenCalled();
+ });
+
+ it('passes disableStdin to xterm constructor', async () => {
+ const { Terminal: MockTerminal } = await import('@xterm/xterm');
+ render( );
+ await waitForInit();
+ expect(MockTerminal).toHaveBeenCalledWith(
+ expect.objectContaining({ disableStdin: true }),
+ );
+ });
+
+ it('passes cursorStyle to xterm constructor', async () => {
+ const { Terminal: MockTerminal } = await import('@xterm/xterm');
+ render( );
+ await waitForInit();
+ expect(MockTerminal).toHaveBeenCalledWith(
+ expect.objectContaining({ cursorStyle: 'underline' }),
+ );
+ });
+
+ it('attaches customKeyEventHandler', async () => {
+ const handler = vi.fn(() => true);
+ render( );
+ await waitForInit();
+ expect(mockTerminal.attachCustomKeyEventHandler).toHaveBeenCalledWith(handler);
+ });
+
+ describe('renderer prop', () => {
+ it('loads WebGL by default (auto)', async () => {
+ render( );
+ await waitForInit();
+ expect(mockTerminal.loadAddon).toHaveBeenCalledWith(mockWebglAddon);
+ });
+
+ it('renderer="dom" skips WebGL', async () => {
+ const { WebglAddon } = await import('@xterm/addon-webgl');
+ (WebglAddon as unknown as ReturnType).mockClear();
+ render( );
+ await waitForInit();
+ expect(WebglAddon).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('imperative handle', () => {
+ it('write accepts string data', async () => {
+ const ref = createRef();
+ render( );
+ await waitForInit();
+ ref.current?.write('hello');
+ expect(mockTerminal.write).toHaveBeenCalledWith('hello');
+ });
+
+ it('write accepts Uint8Array data', async () => {
+ const ref = createRef();
+ render( );
+ await waitForInit();
+ const data = new Uint8Array([72, 101, 108, 108, 111]);
+ ref.current?.write(data);
+ expect(mockTerminal.write).toHaveBeenCalledWith(data);
+ });
+
+ it('writeln writes a line', async () => {
+ const ref = createRef();
+ render( );
+ await waitForInit();
+ ref.current?.writeln('line');
+ expect(mockTerminal.writeln).toHaveBeenCalledWith('line');
+ });
+
+ it('clear clears the terminal', async () => {
+ const ref = createRef();
+ render( );
+ await waitForInit();
+ ref.current?.clear();
+ expect(mockTerminal.clear).toHaveBeenCalled();
+ });
+
+ it('focus focuses the terminal', async () => {
+ const ref = createRef();
+ render( );
+ await waitForInit();
+ ref.current?.focus();
+ expect(mockTerminal.focus).toHaveBeenCalled();
+ });
+
+ it('fit re-fits the terminal', async () => {
+ const ref = createRef();
+ render( );
+ await waitForInit();
+ ref.current?.fit();
+ expect(mockFitAddon.fit).toHaveBeenCalled();
+ });
+
+ it('getDimensions returns current cols/rows', async () => {
+ const ref = createRef();
+ render( );
+ await waitForInit();
+ const dims = ref.current?.getDimensions();
+ expect(dims).toEqual({ cols: 80, rows: 24 });
+ });
+
+ it('reset resets the terminal', async () => {
+ const ref = createRef();
+ render( );
+ await waitForInit();
+ ref.current?.reset();
+ expect(mockTerminal.reset).toHaveBeenCalled();
+ });
+
+ it('scrollToBottom scrolls to bottom', async () => {
+ const ref = createRef();
+ render( );
+ await waitForInit();
+ ref.current?.scrollToBottom();
+ expect(mockTerminal.scrollToBottom).toHaveBeenCalled();
+ });
+
+ it('findNext proxies to SearchAddon', async () => {
+ const ref = createRef();
+ render( );
+ await waitForInit();
+ const result = ref.current?.findNext('test', { caseSensitive: true });
+ expect(mockSearchAddon.findNext).toHaveBeenCalledWith('test', { caseSensitive: true });
+ expect(result).toBe(true);
+ });
+
+ it('findPrevious proxies to SearchAddon', async () => {
+ const ref = createRef();
+ render( );
+ await waitForInit();
+ const result = ref.current?.findPrevious('test');
+ expect(mockSearchAddon.findPrevious).toHaveBeenCalledWith('test', undefined);
+ expect(result).toBe(true);
+ });
+
+ it('clearSearch proxies to SearchAddon.clearDecorations', async () => {
+ const ref = createRef();
+ render( );
+ await waitForInit();
+ ref.current?.clearSearch();
+ expect(mockSearchAddon.clearDecorations).toHaveBeenCalled();
+ });
+
+ it('getSelection returns selected text', async () => {
+ const ref = createRef();
+ render( );
+ await waitForInit();
+ expect(ref.current?.getSelection()).toBe('selected-text');
+ });
+
+ it('hasSelection returns true when selection exists', async () => {
+ const ref = createRef();
+ render( );
+ await waitForInit();
+ expect(ref.current?.hasSelection()).toBe(true);
+ });
+
+ it('selectAll selects all content', async () => {
+ const ref = createRef();
+ render( );
+ await waitForInit();
+ ref.current?.selectAll();
+ expect(mockTerminal.selectAll).toHaveBeenCalled();
+ });
+
+ it('clearSelection clears selection', async () => {
+ const ref = createRef();
+ render( );
+ await waitForInit();
+ ref.current?.clearSelection();
+ expect(mockTerminal.clearSelection).toHaveBeenCalled();
+ });
+
+ it('scrollToLine scrolls to specific line', async () => {
+ const ref = createRef();
+ render( );
+ await waitForInit();
+ ref.current?.scrollToLine(50);
+ expect(mockTerminal.scrollToLine).toHaveBeenCalledWith(50);
+ });
+
+ it('scrollUp scrolls up by N lines', async () => {
+ const ref = createRef();
+ render( );
+ await waitForInit();
+ ref.current?.scrollUp(5);
+ expect(mockTerminal.scrollLines).toHaveBeenCalledWith(-5);
+ });
+
+ it('scrollDown scrolls down by N lines', async () => {
+ const ref = createRef();
+ render( );
+ await waitForInit();
+ ref.current?.scrollDown(3);
+ expect(mockTerminal.scrollLines).toHaveBeenCalledWith(3);
+ });
+
+ it('paste pastes data into terminal', async () => {
+ const ref = createRef();
+ render( );
+ await waitForInit();
+ ref.current?.paste('pasted text');
+ expect(mockTerminal.paste).toHaveBeenCalledWith('pasted text');
+ });
+
+ it('getBufferContent returns serialized buffer', async () => {
+ const ref = createRef();
+ render( );
+ await waitForInit();
+ expect(ref.current?.getBufferContent()).toBe('buffer-content');
+ expect(mockSerializeAddon.serialize).toHaveBeenCalled();
+ });
+
+ it('handles methods called before init gracefully', () => {
+ const ref = createRef();
+ render( );
+ // Call before async init completes — should not throw
+ expect(() => ref.current?.write('test')).not.toThrow();
+ expect(() => ref.current?.clear()).not.toThrow();
+ expect(() => ref.current?.focus()).not.toThrow();
+ expect(() => ref.current?.blur()).not.toThrow();
+ expect(ref.current?.getDimensions()).toBeNull();
+ expect(ref.current?.findNext('test')).toBe(false);
+ expect(ref.current?.findPrevious('test')).toBe(false);
+ expect(() => ref.current?.clearSearch()).not.toThrow();
+ expect(ref.current?.getSelection()).toBe('');
+ expect(ref.current?.hasSelection()).toBe(false);
+ expect(() => ref.current?.selectAll()).not.toThrow();
+ expect(() => ref.current?.clearSelection()).not.toThrow();
+ expect(() => ref.current?.scrollToLine(0)).not.toThrow();
+ expect(() => ref.current?.scrollUp(1)).not.toThrow();
+ expect(() => ref.current?.scrollDown(1)).not.toThrow();
+ expect(() => ref.current?.paste('text')).not.toThrow();
+ expect(ref.current?.getBufferContent()).toBe('');
+ });
+ });
+
+ it('merges className', () => {
+ render( );
+ expect(screen.getByTestId('terminal')).toHaveClass('custom-terminal');
+ });
+
+ it('disposes terminal and addons on unmount', async () => {
+ const { unmount } = render( );
+ await waitForInit();
+ unmount();
+ expect(mockDisconnect).toHaveBeenCalled();
+ expect(mockTerminal.dispose).toHaveBeenCalled();
+ });
+
+ it('handles debounced fit on resize', async () => {
+ vi.useFakeTimers();
+ render( );
+
+ // Wait for init with real timers briefly
+ vi.useRealTimers();
+ await waitForInit();
+ vi.useFakeTimers();
+
+ // Clear fit calls from init
+ mockFitAddon.fit.mockClear();
+
+ // Trigger resize
+ resizeCallback?.();
+ resizeCallback?.();
+ resizeCallback?.();
+
+ // Should not have called fit yet (debounced)
+ expect(mockFitAddon.fit).not.toHaveBeenCalled();
+
+ // Advance past debounce period
+ vi.advanceTimersByTime(15);
+ expect(mockFitAddon.fit).toHaveBeenCalledTimes(1);
+
+ vi.useRealTimers();
+ });
+
+ it('passes mac-specific options', async () => {
+ const { Terminal: MockTerminal } = await import('@xterm/xterm');
+ render( );
+ await waitForInit();
+ expect(MockTerminal).toHaveBeenCalledWith(
+ expect.objectContaining({
+ macOptionIsMeta: true,
+ macOptionClickForcesSelection: true,
+ }),
+ );
+ });
+
+ it('passes scrollback option', async () => {
+ const { Terminal: MockTerminal } = await import('@xterm/xterm');
+ render( );
+ await waitForInit();
+ expect(MockTerminal).toHaveBeenCalledWith(
+ expect.objectContaining({
+ scrollback: 10000,
+ }),
+ );
+ });
+
+ it('calls onReady with dimensions after init', async () => {
+ const onReady = vi.fn();
+ render( );
+ await waitForInit();
+ expect(onReady).toHaveBeenCalledWith({ cols: 80, rows: 24 });
+ });
+
+ it('auto-focuses when autoFocus is true', async () => {
+ mockTerminal.focus.mockClear();
+ render( );
+ await waitForInit();
+ expect(mockTerminal.focus).toHaveBeenCalled();
+ });
+
+ it('does not auto-focus by default', async () => {
+ mockTerminal.focus.mockClear();
+ render( );
+ await waitForInit();
+ expect(mockTerminal.focus).not.toHaveBeenCalled();
+ });
+
+ it('passes fontWeight to xterm constructor', async () => {
+ const { Terminal: MockTerminal } = await import('@xterm/xterm');
+ render( );
+ await waitForInit();
+ expect(MockTerminal).toHaveBeenCalledWith(
+ expect.objectContaining({ fontWeight: 'normal' }),
+ );
+ });
+
+ it('passes fontWeightBold to xterm constructor', async () => {
+ const { Terminal: MockTerminal } = await import('@xterm/xterm');
+ render( );
+ await waitForInit();
+ expect(MockTerminal).toHaveBeenCalledWith(
+ expect.objectContaining({ fontWeightBold: 'bold' }),
+ );
+ });
+
+ it('calls onWriteParsed through stable ref', async () => {
+ const onWriteParsed = vi.fn();
+ render( );
+ await waitForInit();
+ expect(mockTerminal.onWriteParsed).toHaveBeenCalled();
+ mockOnWriteParsed();
+ expect(onWriteParsed).toHaveBeenCalled();
+ });
+
+ it('blur handle method blurs the terminal', async () => {
+ const ref = createRef();
+ render( );
+ await waitForInit();
+ ref.current?.blur();
+ expect(mockTerminal.blur).toHaveBeenCalled();
+ });
+
+ describe('dynamic option updates', () => {
+ it('updates fontSize dynamically and triggers fit', async () => {
+ vi.useFakeTimers();
+ const { rerender } = render( );
+ vi.useRealTimers();
+ await waitForInit();
+ vi.useFakeTimers();
+
+ mockTerminal.options = {} as Record;
+ mockFitAddon.fit.mockClear();
+
+ rerender( );
+
+ expect(mockTerminal.options.fontSize).toBe(16);
+ // Fit is debounced — advance timer
+ vi.advanceTimersByTime(15);
+ expect(mockFitAddon.fit).toHaveBeenCalled();
+ vi.useRealTimers();
+ });
+
+ it('updates cursorBlink dynamically', async () => {
+ const { rerender } = render( );
+ await waitForInit();
+ mockTerminal.options = {} as Record;
+
+ rerender( );
+ expect(mockTerminal.options.cursorBlink).toBe(false);
+ });
+
+ it('updates cursorStyle dynamically', async () => {
+ const { rerender } = render( );
+ await waitForInit();
+ mockTerminal.options = {} as Record;
+
+ rerender( );
+ expect(mockTerminal.options.cursorStyle).toBe('bar');
+ });
+
+ it('updates scrollback dynamically', async () => {
+ const { rerender } = render( );
+ await waitForInit();
+ mockTerminal.options = {} as Record;
+
+ rerender( );
+ expect(mockTerminal.options.scrollback).toBe(10000);
+ });
+
+ it('updates lineHeight dynamically and triggers fit', async () => {
+ vi.useFakeTimers();
+ const { rerender } = render( );
+ vi.useRealTimers();
+ await waitForInit();
+ vi.useFakeTimers();
+
+ mockTerminal.options = {} as Record;
+ mockFitAddon.fit.mockClear();
+
+ rerender( );
+ expect(mockTerminal.options.lineHeight).toBe(1.5);
+ vi.advanceTimersByTime(15);
+ expect(mockFitAddon.fit).toHaveBeenCalled();
+ vi.useRealTimers();
+ });
+
+ it('updates letterSpacing dynamically and triggers fit', async () => {
+ vi.useFakeTimers();
+ const { rerender } = render( );
+ vi.useRealTimers();
+ await waitForInit();
+ vi.useFakeTimers();
+
+ mockTerminal.options = {} as Record;
+ mockFitAddon.fit.mockClear();
+
+ rerender( );
+ expect(mockTerminal.options.letterSpacing).toBe(2);
+ vi.advanceTimersByTime(15);
+ expect(mockFitAddon.fit).toHaveBeenCalled();
+ vi.useRealTimers();
+ });
+ });
+
+ describe('constructor options passthrough', () => {
+ it('passes lineHeight to constructor', async () => {
+ const { Terminal: MockTerminal } = await import('@xterm/xterm');
+ render( );
+ await waitForInit();
+ expect(MockTerminal).toHaveBeenCalledWith(
+ expect.objectContaining({ lineHeight: 1.2 }),
+ );
+ });
+
+ it('passes letterSpacing to constructor', async () => {
+ const { Terminal: MockTerminal } = await import('@xterm/xterm');
+ render( );
+ await waitForInit();
+ expect(MockTerminal).toHaveBeenCalledWith(
+ expect.objectContaining({ letterSpacing: 1 }),
+ );
+ });
+
+ it('passes rows and cols to constructor', async () => {
+ const { Terminal: MockTerminal } = await import('@xterm/xterm');
+ render( );
+ await waitForInit();
+ expect(MockTerminal).toHaveBeenCalledWith(
+ expect.objectContaining({ rows: 30, cols: 120 }),
+ );
+ });
+
+ it('passes screenReaderMode to constructor', async () => {
+ const { Terminal: MockTerminal } = await import('@xterm/xterm');
+ render( );
+ await waitForInit();
+ expect(MockTerminal).toHaveBeenCalledWith(
+ expect.objectContaining({ screenReaderMode: true }),
+ );
+ });
+
+ it('passes minimumContrastRatio to constructor', async () => {
+ const { Terminal: MockTerminal } = await import('@xterm/xterm');
+ render( );
+ await waitForInit();
+ expect(MockTerminal).toHaveBeenCalledWith(
+ expect.objectContaining({ minimumContrastRatio: 4.5 }),
+ );
+ });
+
+ it('passes drawBoldTextInBrightColors to constructor', async () => {
+ const { Terminal: MockTerminal } = await import('@xterm/xterm');
+ render( );
+ await waitForInit();
+ expect(MockTerminal).toHaveBeenCalledWith(
+ expect.objectContaining({ drawBoldTextInBrightColors: false }),
+ );
+ });
+ });
+
+ describe('renderer prop (extended)', () => {
+ it('renderer="webgl" explicitly loads WebGL', async () => {
+ render( );
+ await waitForInit();
+ expect(mockTerminal.loadAddon).toHaveBeenCalledWith(mockWebglAddon);
+ });
+ });
+
+ describe('cleanup and lifecycle', () => {
+ it('cancels debounced fit on unmount', async () => {
+ vi.useFakeTimers();
+ const { unmount } = render( );
+ vi.useRealTimers();
+ await waitForInit();
+ vi.useFakeTimers();
+
+ mockFitAddon.fit.mockClear();
+
+ // Trigger a resize but unmount before debounce fires
+ resizeCallback?.();
+ unmount();
+
+ // Advance timer — fit should NOT fire because debounce was cancelled
+ vi.advanceTimersByTime(50);
+ expect(mockFitAddon.fit).not.toHaveBeenCalled();
+ vi.useRealTimers();
+ });
+
+ it('disposes addons before terminal', async () => {
+ const disposeOrder: string[] = [];
+ mockFitAddon.dispose.mockImplementation(() => disposeOrder.push('fitAddon'));
+ mockSearchAddon.dispose.mockImplementation(() => disposeOrder.push('searchAddon'));
+ mockSerializeAddon.dispose.mockImplementation(() => disposeOrder.push('serializeAddon'));
+ mockUnicode11Addon.dispose.mockImplementation(() => disposeOrder.push('unicode11Addon'));
+ mockWebLinksAddon.dispose.mockImplementation(() => disposeOrder.push('webLinksAddon'));
+ mockWebglAddon.dispose.mockImplementation(() => disposeOrder.push('webglAddon'));
+ mockTerminal.dispose.mockImplementation(() => disposeOrder.push('terminal'));
+
+ const { unmount } = render( );
+ await waitForInit();
+ unmount();
+
+ // Terminal should be last
+ expect(disposeOrder[disposeOrder.length - 1]).toBe('terminal');
+ // All addons should come before terminal
+ expect(disposeOrder.indexOf('terminal')).toBeGreaterThan(disposeOrder.indexOf('fitAddon'));
+ expect(disposeOrder.indexOf('terminal')).toBeGreaterThan(disposeOrder.indexOf('searchAddon'));
+ });
+
+ it('disconnects ResizeObserver on unmount', async () => {
+ const { unmount } = render( );
+ await waitForInit();
+ unmount();
+ expect(mockDisconnect).toHaveBeenCalledTimes(1);
+ });
+
+ it('does not call onReady if unmounted during init', async () => {
+ const onReady = vi.fn();
+ const { unmount } = render( );
+ // Unmount immediately before init completes
+ unmount();
+ await waitForInit();
+ expect(onReady).not.toHaveBeenCalled();
+ });
+
+ it('calls onError when init fails', async () => {
+ // Make the xterm import fail
+ const { Terminal: OrigTerminal } = await import('@xterm/xterm');
+ (OrigTerminal as unknown as ReturnType).mockImplementationOnce(() => {
+ throw new Error('WebGL context unavailable');
+ });
+
+ const onError = vi.fn();
+ render( );
+ await waitForInit();
+ expect(onError).toHaveBeenCalledWith(expect.any(Error));
+ });
+ });
+
+ describe('resize handling', () => {
+ it('handles window resize events', async () => {
+ render( );
+ await waitForInit();
+ mockFitAddon.fit.mockClear();
+
+ // Simulate window resize
+ vi.useFakeTimers();
+ window.dispatchEvent(new Event('resize'));
+ vi.advanceTimersByTime(15);
+ expect(mockFitAddon.fit).toHaveBeenCalled();
+ vi.useRealTimers();
+ });
+
+ it('fit handle method does not throw when addon is not loaded', () => {
+ const ref = createRef();
+ render( );
+ // fit before init — should not throw
+ expect(() => ref.current?.fit()).not.toThrow();
+ });
+
+ it('debounce coalesces rapid resize events into one fit call', async () => {
+ vi.useFakeTimers();
+ render( );
+ vi.useRealTimers();
+ await waitForInit();
+ vi.useFakeTimers();
+
+ mockFitAddon.fit.mockClear();
+
+ // Fire many rapid resizes
+ for (let i = 0; i < 20; i++) {
+ resizeCallback?.();
+ }
+
+ vi.advanceTimersByTime(15);
+ // Should coalesce to exactly 1 call
+ expect(mockFitAddon.fit).toHaveBeenCalledTimes(1);
+ vi.useRealTimers();
+ });
+ });
+
+ describe('onReady ordering', () => {
+ it('onReady fires after fitAddon.fit()', async () => {
+ const callOrder: string[] = [];
+ mockFitAddon.fit.mockImplementation(() => callOrder.push('fit'));
+ const onReady = vi.fn(() => callOrder.push('onReady'));
+
+ render( );
+ await waitForInit();
+
+ const fitIndex = callOrder.indexOf('fit');
+ const readyIndex = callOrder.indexOf('onReady');
+ expect(fitIndex).toBeGreaterThanOrEqual(0);
+ expect(readyIndex).toBeGreaterThan(fitIndex);
+ });
+ });
+});
diff --git a/packages/editors/src/components/terminal/Terminal.tsx b/packages/editors/src/components/terminal/Terminal.tsx
new file mode 100644
index 0000000..575c37c
--- /dev/null
+++ b/packages/editors/src/components/terminal/Terminal.tsx
@@ -0,0 +1,770 @@
+import '@xterm/xterm/css/xterm.css';
+
+import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef } from 'react';
+import { useEditorTheme } from '../../themes/useEditorTheme';
+import { buildXtermTheme } from '../../themes/xterm';
+import styles from './Terminal.module.css';
+
+/**
+ * Signal types matching the Omniview plugin SDK's StreamSignal enum.
+ * These map 1:1 to `core/exec/signal/{SIGNAL}/{sessionId}` Wails events.
+ */
+export type TerminalSignal =
+ | 'ERROR'
+ | 'CLOSE'
+ | 'SIGINT'
+ | 'SIGQUIT'
+ | 'SIGTERM'
+ | 'SIGKILL'
+ | 'SIGHUP'
+ | 'SIGUSR1'
+ | 'SIGUSR2'
+ | 'SIGWINCH';
+
+/**
+ * Structured error payload from the plugin SDK's StreamError.
+ * Received with the 'ERROR' signal when a session encounters an error.
+ */
+export interface TerminalErrorInfo {
+ /** Short error title for display. */
+ title: string;
+ /** Suggested resolution or next step. */
+ suggestion: string;
+ /** Raw error message for debugging. */
+ raw: string;
+ /** Whether the session can be retried. */
+ retryable?: boolean;
+ /** Alternative commands to try (e.g., different shells). */
+ retryCommands?: string[];
+}
+
+/** Options for terminal search operations. */
+export interface TerminalSearchOptions {
+ /** Whether the search should be case-sensitive. */
+ caseSensitive?: boolean;
+ /** Whether the search should match whole words only. */
+ wholeWord?: boolean;
+ /** Whether the search term is a regex. */
+ regex?: boolean;
+ /** Whether to search incrementally (highlight as you type). */
+ incremental?: boolean;
+}
+
+export interface TerminalProps {
+ // ── Session / data callbacks ──────────────────────────────────────────
+
+ /** Called when user types input. Wire to `ExecClient.WriteSession(sessionId, data)`. */
+ onData?: (data: string) => void;
+ /** Called when user pastes binary data. */
+ onBinaryData?: (data: string) => void;
+ /** Called on terminal resize with new dimensions. Wire to `ExecClient.ResizeSession(sessionId, rows, cols)`. */
+ onResize?: (cols: number, rows: number) => void;
+ /**
+ * Called when a signal is received (for exec plugin integration).
+ * Not invoked internally — the consumer's session layer should call
+ * `callbacksRef.current.onSignal?.(signal, payload)` when a signal
+ * arrives over the session transport (e.g., Wails event bus).
+ */
+ onSignal?: (signal: TerminalSignal, payload?: unknown) => void;
+ /** Called when the terminal init encounters an error (e.g., failed to load xterm). */
+ onError?: (error: Error) => void;
+ /**
+ * Called when the terminal session closes.
+ * Not invoked internally — wire this from your session transport's
+ * close/disconnect handler.
+ */
+ onClose?: (code?: number) => void;
+ /**
+ * Called once when the terminal is fully initialized and ready to accept writes.
+ * This is the right time to attach to a session and start writing data.
+ * Receives the current dimensions so the consumer can send an initial resize.
+ */
+ onReady?: (dimensions: { cols: number; rows: number }) => void;
+
+ // ── xterm event callbacks ─────────────────────────────────────────────
+
+ /** Called when the terminal bell is triggered. */
+ onBell?: () => void;
+ /** Called when the terminal title changes (via escape sequence). */
+ onTitleChange?: (title: string) => void;
+ /** Called on each keypress with the key and DOM event. */
+ onKey?: (event: { key: string; domEvent: KeyboardEvent }) => void;
+ /** Called when the text selection changes. */
+ onSelectionChange?: () => void;
+ /** Called on each line feed. */
+ onLineFeed?: () => void;
+ /** Called when the viewport scrolls. */
+ onScroll?: (newPosition: number) => void;
+ /**
+ * Called after write data has been parsed (at most once per animation frame).
+ * Useful as a "render complete" signal for high-throughput output.
+ */
+ onWriteParsed?: () => void;
+
+ // ── Appearance ────────────────────────────────────────────────────────
+
+ /** Font size in pixels. */
+ fontSize?: number;
+ /** Font family override. */
+ fontFamily?: string;
+ /** Font weight ('normal', 'bold', '100'-'900'). */
+ fontWeight?: 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900';
+ /** Font weight for bold text. */
+ fontWeightBold?: 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900';
+ /** Enable cursor blink. */
+ cursorBlink?: boolean;
+ /** Cursor style. */
+ cursorStyle?: 'block' | 'underline' | 'bar';
+ /** Width of the cursor in CSS pixels (only applies to bar cursor). */
+ cursorWidth?: number;
+ /** Line height multiplier. */
+ lineHeight?: number;
+ /** Letter spacing in pixels. */
+ letterSpacing?: number;
+ /** Whether to draw bold text in bright colors. */
+ drawBoldTextInBrightColors?: boolean;
+
+ // ── Behavior ──────────────────────────────────────────────────────────
+
+ /** Maximum scrollback buffer lines. 0 = unlimited. */
+ scrollback?: number;
+ /** Convert \n to \r\n for proper line endings. */
+ convertEol?: boolean;
+ /** Allow transparent background. */
+ allowTransparency?: boolean;
+ /** Treat macOS Option key as Meta (for proper Alt-key sequences). */
+ macOptionIsMeta?: boolean;
+ /** Allow macOS Option+Click to force selection. */
+ macOptionClickForcesSelection?: boolean;
+ /** Enable clickable URLs in terminal output. */
+ linkHandling?: boolean;
+ /** Disable user input (read-only / log-viewer mode). */
+ disableStdin?: boolean;
+ /** Intercept key events before xterm processes them. Return false to prevent default handling. */
+ customKeyEventHandler?: (event: KeyboardEvent) => boolean;
+ /** Auto-focus the terminal after initialization. */
+ autoFocus?: boolean;
+
+ // ── Rendering ─────────────────────────────────────────────────────────
+
+ /** Renderer to use. 'auto' tries WebGL then falls back to DOM. */
+ renderer?: 'auto' | 'webgl' | 'dom';
+ /** Initial number of rows before fit. */
+ rows?: number;
+ /** Initial number of columns before fit. */
+ cols?: number;
+
+ // ── Accessibility ─────────────────────────────────────────────────────
+
+ /** Enable screen reader mode for accessibility. */
+ screenReaderMode?: boolean;
+ /** Minimum contrast ratio for text (1-21). */
+ minimumContrastRatio?: number;
+
+ // ── Layout ────────────────────────────────────────────────────────────
+
+ /** Custom CSS class. */
+ className?: string;
+}
+
+export interface TerminalHandle {
+ // ── Write ─────────────────────────────────────────────────────────────
+
+ /** Write a string or Uint8Array to the terminal. */
+ write: (data: string | Uint8Array) => void;
+ /** Write a string followed by a newline. */
+ writeln: (data: string) => void;
+
+ // ── Terminal state ────────────────────────────────────────────────────
+
+ /** Clear the terminal viewport and scrollback. */
+ clear: () => void;
+ /** Focus the terminal. */
+ focus: () => void;
+ /** Blur (unfocus) the terminal. */
+ blur: () => void;
+ /** Re-fit the terminal to its container. */
+ fit: () => void;
+ /** Get the current terminal dimensions, or null if not initialized. */
+ getDimensions: () => { cols: number; rows: number } | null;
+ /** Reset the terminal (clear + reset state). */
+ reset: () => void;
+
+ // ── Search ────────────────────────────────────────────────────────────
+
+ /** Find the next match for a search term. */
+ findNext: (term: string, options?: TerminalSearchOptions) => boolean;
+ /** Find the previous match for a search term. */
+ findPrevious: (term: string, options?: TerminalSearchOptions) => boolean;
+ /** Clear search highlights. */
+ clearSearch: () => void;
+
+ // ── Selection ─────────────────────────────────────────────────────────
+
+ /** Get the current text selection. */
+ getSelection: () => string;
+ /** Whether text is currently selected. */
+ hasSelection: () => boolean;
+ /** Select all terminal content. */
+ selectAll: () => void;
+ /** Clear the current selection. */
+ clearSelection: () => void;
+
+ // ── Scrolling ─────────────────────────────────────────────────────────
+
+ /** Scroll to the bottom of the terminal. */
+ scrollToBottom: () => void;
+ /** Scroll to a specific line. */
+ scrollToLine: (line: number) => void;
+ /** Scroll up by N lines. */
+ scrollUp: (lines: number) => void;
+ /** Scroll down by N lines. */
+ scrollDown: (lines: number) => void;
+
+ // ── Clipboard ─────────────────────────────────────────────────────────
+
+ /** Paste data into the terminal. */
+ paste: (data: string) => void;
+
+ // ── Buffer ────────────────────────────────────────────────────────────
+
+ /** Get the full buffer content as a string (via SerializeAddon). */
+ getBufferContent: () => string;
+}
+
+function cn(...parts: Array): string {
+ return parts.filter(Boolean).join(' ');
+}
+
+/**
+ * Simple debounce for resize handlers. Returns a debounced function
+ * and a cancel function for cleanup.
+ */
+function debounce void>(
+ fn: T,
+ ms: number,
+): { run: (...args: Parameters) => void; cancel: () => void } {
+ let timer: ReturnType | null = null;
+ return {
+ run: (...args: Parameters) => {
+ if (timer !== null) clearTimeout(timer);
+ timer = setTimeout(() => {
+ timer = null;
+ fn(...args);
+ }, ms);
+ },
+ cancel: () => {
+ if (timer !== null) {
+ clearTimeout(timer);
+ timer = null;
+ }
+ },
+ };
+}
+
+/**
+ * Production-grade terminal component built on xterm.js 6.
+ *
+ * Designed for the Omniview IDE bottom-drawer terminal, but usable anywhere.
+ * Wire `onData` → `ExecClient.WriteSession` and `onResize` → `ExecClient.ResizeSession`,
+ * then write session stdout/stderr to the handle's `write()` method.
+ *
+ * @example
+ * ```tsx
+ * const ref = useRef(null);
+ *
+ * {
+ * attachSession(sessionId);
+ * resizeSession(sessionId, rows, cols);
+ * }}
+ * onData={(data) => writeSession(sessionId, data)}
+ * onResize={(cols, rows) => resizeSession(sessionId, rows, cols)}
+ * autoFocus
+ * />
+ * ```
+ */
+export const Terminal = forwardRef(function Terminal(
+ {
+ onData,
+ onBinaryData,
+ onResize,
+ onSignal,
+ onError,
+ onClose,
+ onReady,
+ onBell,
+ onTitleChange,
+ onKey,
+ onSelectionChange,
+ onLineFeed,
+ onScroll,
+ onWriteParsed,
+ fontSize = 13,
+ fontFamily,
+ fontWeight,
+ fontWeightBold,
+ scrollback = 5000,
+ cursorBlink = true,
+ cursorStyle,
+ cursorWidth,
+ lineHeight,
+ letterSpacing,
+ drawBoldTextInBrightColors = true,
+ convertEol = true,
+ allowTransparency = true,
+ macOptionIsMeta = true,
+ macOptionClickForcesSelection = true,
+ linkHandling = true,
+ disableStdin,
+ customKeyEventHandler,
+ autoFocus = false,
+ renderer = 'auto',
+ rows: initialRows,
+ cols: initialCols,
+ screenReaderMode,
+ minimumContrastRatio,
+ className,
+ },
+ ref,
+) {
+ const containerRef = useRef(null);
+ const termRef = useRef(null);
+ const fitAddonRef = useRef(null);
+ const searchAddonRef = useRef(null);
+ const serializeAddonRef = useRef(null);
+ const addonsRef = useRef void }>>([]);
+ const observerRef = useRef(null);
+ const debouncedFitRef = useRef<{ run: () => void; cancel: () => void } | null>(null);
+ const windowResizeCleanupRef = useRef<(() => void) | null>(null);
+ const theme = useEditorTheme();
+
+ // Stable refs for callbacks to avoid re-initializing terminal
+ const callbacksRef = useRef({
+ onData, onBinaryData, onResize, onSignal, onError, onClose, onReady,
+ onBell, onTitleChange, onKey, onSelectionChange, onLineFeed, onScroll, onWriteParsed,
+ });
+ callbacksRef.current = {
+ onData, onBinaryData, onResize, onSignal, onError, onClose, onReady,
+ onBell, onTitleChange, onKey, onSelectionChange, onLineFeed, onScroll, onWriteParsed,
+ };
+
+ const fit = useCallback(() => {
+ try {
+ const addon = fitAddonRef.current as { fit?: () => void } | null;
+ addon?.fit?.();
+ } catch {
+ // fit() can throw if container has zero dimensions (e.g., hidden tab)
+ }
+ }, []);
+
+ useImperativeHandle(ref, () => ({
+ write: (data: string | Uint8Array) => {
+ const t = termRef.current as { write?: (d: string | Uint8Array) => void } | null;
+ t?.write?.(data);
+ },
+ writeln: (data: string) => {
+ const t = termRef.current as { writeln?: (d: string) => void } | null;
+ t?.writeln?.(data);
+ },
+ clear: () => {
+ const t = termRef.current as { clear?: () => void } | null;
+ t?.clear?.();
+ },
+ focus: () => {
+ const t = termRef.current as { focus?: () => void } | null;
+ t?.focus?.();
+ },
+ blur: () => {
+ const t = termRef.current as { blur?: () => void } | null;
+ t?.blur?.();
+ },
+ fit,
+ getDimensions: () => {
+ const t = termRef.current as { cols?: number; rows?: number } | null;
+ if (t && typeof t.cols === 'number' && typeof t.rows === 'number') {
+ return { cols: t.cols, rows: t.rows };
+ }
+ return null;
+ },
+ reset: () => {
+ const t = termRef.current as { reset?: () => void } | null;
+ t?.reset?.();
+ },
+ scrollToBottom: () => {
+ const t = termRef.current as { scrollToBottom?: () => void } | null;
+ t?.scrollToBottom?.();
+ },
+ findNext: (term: string, options?: TerminalSearchOptions): boolean => {
+ const s = searchAddonRef.current as { findNext?: (t: string, o?: TerminalSearchOptions) => boolean } | null;
+ return s?.findNext?.(term, options) ?? false;
+ },
+ findPrevious: (term: string, options?: TerminalSearchOptions): boolean => {
+ const s = searchAddonRef.current as { findPrevious?: (t: string, o?: TerminalSearchOptions) => boolean } | null;
+ return s?.findPrevious?.(term, options) ?? false;
+ },
+ clearSearch: () => {
+ const s = searchAddonRef.current as { clearDecorations?: () => void } | null;
+ s?.clearDecorations?.();
+ },
+ getSelection: (): string => {
+ const t = termRef.current as { getSelection?: () => string } | null;
+ return t?.getSelection?.() ?? '';
+ },
+ hasSelection: (): boolean => {
+ const t = termRef.current as { hasSelection?: () => boolean } | null;
+ return t?.hasSelection?.() ?? false;
+ },
+ selectAll: () => {
+ const t = termRef.current as { selectAll?: () => void } | null;
+ t?.selectAll?.();
+ },
+ clearSelection: () => {
+ const t = termRef.current as { clearSelection?: () => void } | null;
+ t?.clearSelection?.();
+ },
+ scrollToLine: (line: number) => {
+ const t = termRef.current as { scrollToLine?: (l: number) => void } | null;
+ t?.scrollToLine?.(line);
+ },
+ scrollUp: (lines: number) => {
+ const t = termRef.current as { scrollLines?: (l: number) => void } | null;
+ t?.scrollLines?.(-lines);
+ },
+ scrollDown: (lines: number) => {
+ const t = termRef.current as { scrollLines?: (l: number) => void } | null;
+ t?.scrollLines?.(lines);
+ },
+ paste: (data: string) => {
+ const t = termRef.current as { paste?: (d: string) => void } | null;
+ t?.paste?.(data);
+ },
+ getBufferContent: (): string => {
+ const s = serializeAddonRef.current as { serialize?: () => string } | null;
+ return s?.serialize?.() ?? '';
+ },
+ }));
+
+ // Initialize xterm lazily.
+ // @note Mount-only props (read once during init, not reactive): fontFamily, fontWeight,
+ // fontWeightBold, allowTransparency, convertEol, macOptionIsMeta,
+ // macOptionClickForcesSelection, drawBoldTextInBrightColors, disableStdin, cursorWidth,
+ // initialRows, initialCols, screenReaderMode, minimumContrastRatio, customKeyEventHandler,
+ // autoFocus, renderer, linkHandling. Changing these after mount requires remounting.
+ useEffect(() => {
+ const container = containerRef.current;
+ if (!container) return;
+
+ let disposed = false;
+
+ async function init() {
+ // Wait for fonts to be ready to prevent wrong char width measurements
+ await document.fonts.ready;
+
+ const [{ Terminal: XTerminal }, { FitAddon }, { SearchAddon }, { SerializeAddon }, { Unicode11Addon }] =
+ await Promise.all([
+ import('@xterm/xterm'),
+ import('@xterm/addon-fit'),
+ import('@xterm/addon-search'),
+ import('@xterm/addon-serialize'),
+ import('@xterm/addon-unicode11'),
+ ]);
+
+ if (disposed) return;
+
+ const xtermTheme = buildXtermTheme();
+ const termOptions: Record = {
+ fontSize,
+ fontFamily: fontFamily || "'Menlo', 'Monaco', 'Consolas', 'Liberation Mono', 'Courier New', monospace",
+ theme: xtermTheme,
+ allowTransparency,
+ cursorBlink,
+ convertEol,
+ scrollback,
+ macOptionIsMeta,
+ macOptionClickForcesSelection,
+ allowProposedApi: true,
+ drawBoldTextInBrightColors,
+ };
+
+ if (fontWeight !== undefined) termOptions.fontWeight = fontWeight;
+ if (fontWeightBold !== undefined) termOptions.fontWeightBold = fontWeightBold;
+ if (disableStdin !== undefined) termOptions.disableStdin = disableStdin;
+ if (cursorStyle !== undefined) termOptions.cursorStyle = cursorStyle;
+ if (cursorWidth !== undefined) termOptions.cursorWidth = cursorWidth;
+ if (lineHeight !== undefined) termOptions.lineHeight = lineHeight;
+ if (letterSpacing !== undefined) termOptions.letterSpacing = letterSpacing;
+ if (initialRows !== undefined) termOptions.rows = initialRows;
+ if (initialCols !== undefined) termOptions.cols = initialCols;
+ if (screenReaderMode !== undefined) termOptions.screenReaderMode = screenReaderMode;
+ if (minimumContrastRatio !== undefined) termOptions.minimumContrastRatio = minimumContrastRatio;
+
+ const term = new XTerminal(termOptions);
+
+ if (customKeyEventHandler) {
+ (term as unknown as { attachCustomKeyEventHandler: (h: (e: KeyboardEvent) => boolean) => void })
+ .attachCustomKeyEventHandler(customKeyEventHandler);
+ }
+
+ const loadedAddons: Array<{ dispose: () => void }> = [];
+
+ const fitAddon = new FitAddon();
+ const searchAddon = new SearchAddon();
+ const serializeAddon = new SerializeAddon();
+ const unicode11Addon = new Unicode11Addon();
+ term.loadAddon(fitAddon);
+ term.loadAddon(searchAddon);
+ term.loadAddon(serializeAddon);
+ term.loadAddon(unicode11Addon);
+ loadedAddons.push(fitAddon, searchAddon, serializeAddon, unicode11Addon);
+
+ // Activate unicode11 for proper emoji/CJK width
+ (term as unknown as { unicode: { activeVersion: string } }).unicode.activeVersion = '11';
+
+ // Load web links addon for clickable URLs
+ if (linkHandling) {
+ try {
+ const { WebLinksAddon } = await import('@xterm/addon-web-links');
+ if (!disposed) {
+ const webLinksAddon = new WebLinksAddon();
+ term.loadAddon(webLinksAddon);
+ loadedAddons.push(webLinksAddon);
+ }
+ } catch {
+ // Web links addon not available, non-critical
+ }
+ }
+
+ if (disposed) {
+ term.dispose();
+ return;
+ }
+
+ // Open terminal in container
+ term.open(container!);
+
+ // Load renderer based on prop
+ if (renderer === 'auto' || renderer === 'webgl') {
+ try {
+ const { WebglAddon } = await import('@xterm/addon-webgl');
+ if (!disposed) {
+ const webglAddon = new WebglAddon();
+ webglAddon.onContextLoss(() => {
+ webglAddon.dispose();
+ });
+ term.loadAddon(webglAddon);
+ loadedAddons.push(webglAddon);
+ }
+ } catch {
+ // WebGL not available, DOM renderer is used as fallback
+ }
+ }
+ // renderer === 'dom' — no extra addon needed
+
+ if (disposed) {
+ for (const addon of loadedAddons) {
+ try {
+ addon.dispose();
+ } catch {
+ // ignore
+ }
+ }
+ term.dispose();
+ return;
+ }
+
+ // Initial fit
+ fitAddon.fit();
+
+ // Store refs
+ termRef.current = term;
+ fitAddonRef.current = fitAddon;
+ searchAddonRef.current = searchAddon;
+ serializeAddonRef.current = serializeAddon;
+ addonsRef.current = loadedAddons;
+
+ // Wire up callbacks via stable refs
+ term.onData((data: string) => {
+ callbacksRef.current.onData?.(data);
+ });
+
+ term.onBinary((data: string) => {
+ callbacksRef.current.onBinaryData?.(data);
+ });
+
+ term.onResize(({ cols, rows }: { cols: number; rows: number }) => {
+ callbacksRef.current.onResize?.(cols, rows);
+ });
+
+ (term as unknown as { onBell: (cb: () => void) => void }).onBell(() => {
+ callbacksRef.current.onBell?.();
+ });
+
+ (term as unknown as { onTitleChange: (cb: (t: string) => void) => void }).onTitleChange(
+ (title: string) => {
+ callbacksRef.current.onTitleChange?.(title);
+ },
+ );
+
+ (term as unknown as { onKey: (cb: (e: { key: string; domEvent: KeyboardEvent }) => void) => void }).onKey(
+ (event: { key: string; domEvent: KeyboardEvent }) => {
+ callbacksRef.current.onKey?.(event);
+ },
+ );
+
+ (term as unknown as { onSelectionChange: (cb: () => void) => void }).onSelectionChange(() => {
+ callbacksRef.current.onSelectionChange?.();
+ });
+
+ (term as unknown as { onLineFeed: (cb: () => void) => void }).onLineFeed(() => {
+ callbacksRef.current.onLineFeed?.();
+ });
+
+ (term as unknown as { onScroll: (cb: (p: number) => void) => void }).onScroll((pos: number) => {
+ callbacksRef.current.onScroll?.(pos);
+ });
+
+ (term as unknown as { onWriteParsed: (cb: () => void) => void }).onWriteParsed(() => {
+ callbacksRef.current.onWriteParsed?.();
+ });
+
+ // Debounced fit for resize handling (10ms debounce like legacy impl)
+ const debouncedFit = debounce(() => {
+ try {
+ fitAddon.fit();
+ } catch {
+ // Ignore fit errors (zero-size container, etc.)
+ }
+ }, 10);
+ debouncedFitRef.current = debouncedFit;
+
+ // ResizeObserver for container resize
+ const resizeObserver = new ResizeObserver(() => {
+ debouncedFit.run();
+ });
+ resizeObserver.observe(container!);
+ observerRef.current = resizeObserver;
+
+ // Window resize handler as additional safety
+ const handleWindowResize = () => {
+ debouncedFit.run();
+ };
+ window.addEventListener('resize', handleWindowResize);
+
+ // Store window resize cleanup in a ref (not on the DOM node)
+ windowResizeCleanupRef.current = () => {
+ window.removeEventListener('resize', handleWindowResize);
+ };
+
+ // Auto-focus if requested
+ if (autoFocus) {
+ term.focus();
+ }
+
+ // Notify consumer that the terminal is ready
+ callbacksRef.current.onReady?.({ cols: term.cols, rows: term.rows });
+ }
+
+ init().catch((err) => {
+ callbacksRef.current.onError?.(err instanceof Error ? err : new Error(String(err)));
+ });
+
+ return () => {
+ disposed = true;
+
+ // Cancel pending debounced fit
+ debouncedFitRef.current?.cancel();
+ debouncedFitRef.current = null;
+
+ // Disconnect resize observer
+ observerRef.current?.disconnect();
+ observerRef.current = null;
+
+ // Remove window resize listener
+ windowResizeCleanupRef.current?.();
+ windowResizeCleanupRef.current = null;
+
+ // Dispose addons first, then terminal (proper order)
+ for (const addon of addonsRef.current) {
+ try {
+ addon.dispose();
+ } catch {
+ // ignore addon disposal errors
+ }
+ }
+ addonsRef.current = [];
+
+ // Dispose terminal
+ const term = termRef.current as { dispose?: () => void } | null;
+ try {
+ term?.dispose?.();
+ } catch {
+ // ignore terminal disposal errors
+ }
+
+ termRef.current = null;
+ fitAddonRef.current = null;
+ searchAddonRef.current = null;
+ serializeAddonRef.current = null;
+ };
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
+
+ // Update theme dynamically without recreating terminal
+ useEffect(() => {
+ const term = termRef.current as { options?: { theme: unknown } } | null;
+ if (term?.options) {
+ term.options.theme = buildXtermTheme();
+ }
+ }, [theme]);
+
+ // Update font size dynamically
+ useEffect(() => {
+ const term = termRef.current as { options?: { fontSize: number } } | null;
+ if (term?.options) {
+ term.options.fontSize = fontSize;
+ debouncedFitRef.current?.run();
+ }
+ }, [fontSize]);
+
+ // Update dynamically updatable options
+ useEffect(() => {
+ const term = termRef.current as { options?: Record } | null;
+ if (!term?.options) return;
+ if (cursorBlink !== undefined) term.options.cursorBlink = cursorBlink;
+ }, [cursorBlink]);
+
+ useEffect(() => {
+ const term = termRef.current as { options?: Record } | null;
+ if (!term?.options) return;
+ if (cursorStyle !== undefined) term.options.cursorStyle = cursorStyle;
+ }, [cursorStyle]);
+
+ useEffect(() => {
+ const term = termRef.current as { options?: Record } | null;
+ if (!term?.options) return;
+ if (scrollback !== undefined) term.options.scrollback = scrollback;
+ }, [scrollback]);
+
+ useEffect(() => {
+ const term = termRef.current as { options?: Record } | null;
+ if (!term?.options) return;
+ if (lineHeight !== undefined) {
+ term.options.lineHeight = lineHeight;
+ debouncedFitRef.current?.run();
+ }
+ }, [lineHeight]);
+
+ useEffect(() => {
+ const term = termRef.current as { options?: Record } | null;
+ if (!term?.options) return;
+ if (letterSpacing !== undefined) {
+ term.options.letterSpacing = letterSpacing;
+ debouncedFitRef.current?.run();
+ }
+ }, [letterSpacing]);
+
+ return
;
+});
+
+Terminal.displayName = 'Terminal';
diff --git a/packages/editors/src/components/terminal/index.ts b/packages/editors/src/components/terminal/index.ts
new file mode 100644
index 0000000..e89e652
--- /dev/null
+++ b/packages/editors/src/components/terminal/index.ts
@@ -0,0 +1,8 @@
+export { Terminal } from './Terminal';
+export type {
+ TerminalProps,
+ TerminalHandle,
+ TerminalSignal,
+ TerminalSearchOptions,
+ TerminalErrorInfo,
+} from './Terminal';
diff --git a/packages/editors/src/css.d.ts b/packages/editors/src/css.d.ts
new file mode 100644
index 0000000..06d8d96
--- /dev/null
+++ b/packages/editors/src/css.d.ts
@@ -0,0 +1,9 @@
+declare module '*.module.css' {
+ const classes: Record;
+ export default classes;
+}
+
+declare module '*.svg' {
+ const src: string;
+ export default src;
+}
diff --git a/packages/editors/src/index.ts b/packages/editors/src/index.ts
new file mode 100644
index 0000000..4195284
--- /dev/null
+++ b/packages/editors/src/index.ts
@@ -0,0 +1,8 @@
+export * from './components';
+export { useEditorTheme, getComputedToken } from './themes';
+export { buildMonacoTheme, OV_MONACO_THEME } from './themes';
+export { buildXtermTheme } from './themes';
+export type { XtermThemeData } from './themes';
+export { editorSchemas } from './schemas';
+export type { EditorSchema, EditorSchemaRegistry } from './schemas';
+export { setupMonacoWorkers } from './setup';
diff --git a/packages/editors/src/schemas/EditorSchemaRegistry.ts b/packages/editors/src/schemas/EditorSchemaRegistry.ts
new file mode 100644
index 0000000..092c1e2
--- /dev/null
+++ b/packages/editors/src/schemas/EditorSchemaRegistry.ts
@@ -0,0 +1,286 @@
+/**
+ * EditorSchemaRegistry — runtime registry for adding language schemas
+ * (primarily YAML/JSON) to Monaco editors.
+ *
+ * Plugins register schemas at runtime via `register()`. The CodeEditor
+ * subscribes via `onChange()` and calls `applyYamlSchemas()` / `applyJsonSchemas()`
+ * whenever schemas change.
+ *
+ * @example
+ * ```ts
+ * import { editorSchemas } from '@omniview/editors';
+ *
+ * // Plugin registers a Kubernetes Deployment schema
+ * editorSchemas.register({
+ * uri: 'https://kubernetesjsonschema.dev/v1.25.0/deployment-apps-v1.json',
+ * fileMatch: ['*apps%3A%3Av1%3A%3ADeployment.yaml'],
+ * schema: deploymentJsonSchema,
+ * });
+ * ```
+ */
+
+const DEBUG = false;
+function log(...args: unknown[]) {
+ if (DEBUG) console.log('[schema-registry]', ...args);
+}
+function warn(...args: unknown[]) {
+ if (DEBUG) console.warn('[schema-registry]', ...args);
+}
+
+export interface EditorSchema {
+ /** Unique URI identifying this schema. Must be a valid https:// URL. */
+ uri: string;
+ /**
+ * Glob patterns for filenames this schema applies to.
+ * Tested against the full Monaco model URI string — use URL-encoded
+ * colons (`%3A`) for structured GVR filenames.
+ *
+ * @example ['*core%3A%3Av1%3A%3APod.yaml']
+ */
+ fileMatch: string[];
+ /** The JSON Schema object. */
+ schema: Record;
+ /**
+ * Human-readable name shown in hover tooltips as the "Source:" link text.
+ * If omitted, the URI basename is used.
+ * @example "Pod (v1)"
+ */
+ name?: string;
+ /**
+ * Description shown in hover tooltips below the schema title.
+ * Supports plain text (markdown special chars will be escaped).
+ */
+ description?: string;
+}
+
+type SchemaChangeListener = () => void;
+
+/**
+ * Simple glob matcher — supports `*` (matches any sequence of chars).
+ * Used to replicate the fileMatch behavior of monaco-yaml.
+ */
+function globMatch(pattern: string, str: string): boolean {
+ // Simple iterative glob matcher — avoids RegExp to prevent ReDoS.
+ let pi = 0;
+ let si = 0;
+ let starPi = -1;
+ let starSi = -1;
+
+ while (si < str.length) {
+ if (pi < pattern.length && (pattern[pi] === str[si] || pattern[pi] === '?')) {
+ pi++;
+ si++;
+ } else if (pi < pattern.length && pattern[pi] === '*') {
+ starPi = pi;
+ starSi = si;
+ pi++;
+ } else if (starPi >= 0) {
+ pi = starPi + 1;
+ starSi++;
+ si = starSi;
+ } else {
+ return false;
+ }
+ }
+
+ while (pi < pattern.length && pattern[pi] === '*') {
+ pi++;
+ }
+
+ return pi === pattern.length;
+}
+
+type YamlHandle = {
+ update: (options: Record) => void;
+ dispose: () => void;
+};
+
+class EditorSchemaRegistryImpl {
+ private _schemas = new Map();
+ private _listeners = new Set();
+ private _yamlHandle: YamlHandle | null = null;
+
+ /** All currently registered schemas. */
+ get schemas(): EditorSchema[] {
+ return Array.from(this._schemas.values());
+ }
+
+ /**
+ * Inject the yaml handle created by `setupMonacoWorkers`.
+ * Called once at startup before any editor mounts.
+ * @internal
+ */
+ _setYamlHandle(handle: YamlHandle): void {
+ log('_setYamlHandle called — handle keys:', Object.keys(handle));
+ this._yamlHandle = handle;
+ }
+
+ /**
+ * Register a schema. If a schema with the same URI exists, it is replaced.
+ * Notifies all listeners (active editors) to reconfigure.
+ */
+ register(schema: EditorSchema): void {
+ log('register() — uri:', schema.uri, 'fileMatch:', schema.fileMatch,
+ 'schema keys:', Object.keys(schema.schema).slice(0, 10));
+ this._schemas.set(schema.uri, schema);
+ this._notify();
+ }
+
+ /** Register multiple schemas at once. */
+ registerAll(schemas: EditorSchema[]): void {
+ log('registerAll() —', schemas.length, 'schemas:',
+ schemas.map(s => s.uri));
+ for (const s of schemas) {
+ this._schemas.set(s.uri, s);
+ }
+ this._notify();
+ }
+
+ /** Unregister a schema by URI. */
+ unregister(uri: string): boolean {
+ log('unregister() — uri:', uri);
+ const deleted = this._schemas.delete(uri);
+ if (deleted) this._notify();
+ else log(' schema not found, nothing to unregister');
+ return deleted;
+ }
+
+ /** Clear all registered schemas. */
+ clear(): void {
+ log('clear() — removing', this._schemas.size, 'schemas');
+ this._schemas.clear();
+ this._notify();
+ }
+
+ /**
+ * Find all registered schemas whose `fileMatch` patterns match the given
+ * model URI string. Uses the same glob-like matching that monaco-yaml uses.
+ */
+ getSchemasForUri(modelUri: string): EditorSchema[] {
+ return this.schemas.filter((s) =>
+ s.fileMatch.some((pattern) => globMatch(pattern, modelUri)),
+ );
+ }
+
+ /** Whether the YAML handle has been injected (workers initialized). */
+ get isYamlReady(): boolean {
+ return this._yamlHandle !== null;
+ }
+
+ /** Subscribe to schema changes. Returns an unsubscribe function. */
+ onChange(listener: SchemaChangeListener): () => void {
+ this._listeners.add(listener);
+ log('onChange() — listener added, total listeners:', this._listeners.size);
+ return () => {
+ this._listeners.delete(listener);
+ log('onChange() — listener removed, total listeners:', this._listeners.size);
+ };
+ }
+
+ /**
+ * Push all registered YAML schemas to the monaco-yaml worker via `update()`.
+ * The yaml handle must have been injected by `setupMonacoWorkers` at startup.
+ */
+ applyYamlSchemas(): void {
+ if (!this._yamlHandle) {
+ warn('applyYamlSchemas() — NO yaml handle! Was setupMonacoWorkers imported?');
+ return;
+ }
+ const schemas = this.schemas;
+ const mapped = schemas.map((s) => ({
+ uri: s.uri,
+ fileMatch: s.fileMatch,
+ schema: s.schema as Record,
+ // name/description are passed through to yaml-language-server's
+ // registerExternalSchema — they appear in hover tooltips.
+ ...(s.name && { name: s.name }),
+ ...(s.description && { description: s.description }),
+ }));
+ log('applyYamlSchemas() — pushing', schemas.length, 'schemas');
+ this._yamlHandle.update({
+ enableSchemaRequest: false,
+ schemas: mapped,
+ });
+ log('applyYamlSchemas() — update() called successfully');
+ }
+
+ /**
+ * Apply all JSON schemas to Monaco's built-in JSON language service.
+ * Accepts the `monaco` namespace so the registry itself doesn't import
+ * `monaco-editor` — keeping it usable before Monaco loads.
+ */
+ applyJsonSchemas(monacoInstance: unknown): void {
+ const m = monacoInstance as {
+ json?: {
+ jsonDefaults?: {
+ setDiagnosticsOptions: (opts: {
+ validate: boolean;
+ schemas: Array<{
+ uri: string;
+ fileMatch: string[];
+ schema: Record;
+ }>;
+ }) => void;
+ };
+ };
+ // Fallback for pre-0.55 (languages.json.jsonDefaults)
+ languages?: {
+ json?: {
+ jsonDefaults?: {
+ setDiagnosticsOptions: (opts: {
+ validate: boolean;
+ schemas: Array<{
+ uri: string;
+ fileMatch: string[];
+ schema: Record;
+ }>;
+ }) => void;
+ };
+ };
+ };
+ };
+
+ const diagnosticsOptions = {
+ validate: true,
+ schemas: this.schemas.map((s) => ({
+ uri: s.uri,
+ fileMatch: s.fileMatch,
+ schema: s.schema,
+ })),
+ };
+
+ // 0.55+ top-level namespace
+ const jsonDefaults = m.json?.jsonDefaults ?? m.languages?.json?.jsonDefaults;
+ log('applyJsonSchemas() —', this.schemas.length, 'schemas, jsonDefaults available:',
+ !!jsonDefaults);
+ jsonDefaults?.setDiagnosticsOptions(diagnosticsOptions);
+ }
+
+ /** Log the current state of the registry for debugging. */
+ debugState(): void {
+ log('=== Registry State ===');
+ log(' Schemas:', this._schemas.size);
+ for (const [uri, schema] of this._schemas) {
+ log(` ${uri} → fileMatch: ${JSON.stringify(schema.fileMatch)}`);
+ }
+ log(' Listeners:', this._listeners.size);
+ log(' YAML handle:', this._yamlHandle ? 'SET' : 'NOT SET');
+ log('======================');
+ }
+
+ private _notify(): void {
+ log('_notify() — notifying', this._listeners.size, 'listeners');
+ for (const fn of this._listeners) {
+ try {
+ fn();
+ } catch (err) {
+ console.error('[schema-registry] listener threw during _notify():', err);
+ }
+ }
+ }
+}
+
+/** Singleton schema registry instance. */
+export const editorSchemas = new EditorSchemaRegistryImpl();
+
+export type { EditorSchemaRegistryImpl as EditorSchemaRegistry };
diff --git a/packages/editors/src/schemas/index.ts b/packages/editors/src/schemas/index.ts
new file mode 100644
index 0000000..b910e01
--- /dev/null
+++ b/packages/editors/src/schemas/index.ts
@@ -0,0 +1,2 @@
+export { editorSchemas } from './EditorSchemaRegistry';
+export type { EditorSchema, EditorSchemaRegistry } from './EditorSchemaRegistry';
diff --git a/packages/editors/src/schemas/kubernetes/configmap-v1.json b/packages/editors/src/schemas/kubernetes/configmap-v1.json
new file mode 100644
index 0000000..5634403
--- /dev/null
+++ b/packages/editors/src/schemas/kubernetes/configmap-v1.json
@@ -0,0 +1,316 @@
+{
+ "description": "ConfigMap holds configuration data for pods to consume.",
+ "properties": {
+ "apiVersion": {
+ "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
+ "type": [
+ "string",
+ "null"
+ ],
+ "enum": [
+ "v1"
+ ]
+ },
+ "binaryData": {
+ "additionalProperties": {
+ "format": "byte",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "BinaryData contains the binary data. Each key must consist of alphanumeric characters, '-', '_' or '.'. BinaryData can contain byte sequences that are not in the UTF-8 range. The keys stored in BinaryData must not overlap with the ones in the Data field, this is enforced during validation process. Using this field will require 1.10+ apiserver and kubelet.",
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "data": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "Data contains the configuration data. Each key must consist of alphanumeric characters, '-', '_' or '.'. Values with non-UTF-8 byte sequences must use the BinaryData field. The keys stored in Data must not overlap with the keys in the BinaryData field, this is enforced during validation process.",
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "immutable": {
+ "description": "Immutable, if set to true, ensures that data stored in the ConfigMap cannot be updated (only object metadata can be modified). If not set to true, the field can be modified at any time. Defaulted to nil.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "kind": {
+ "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
+ "type": [
+ "string",
+ "null"
+ ],
+ "enum": [
+ "ConfigMap"
+ ]
+ },
+ "metadata": {
+ "description": "ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.",
+ "properties": {
+ "annotations": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations",
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "creationTimestamp": {
+ "description": "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.",
+ "format": "date-time",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "deletionGracePeriodSeconds": {
+ "description": "Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "deletionTimestamp": {
+ "description": "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.",
+ "format": "date-time",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "finalizers": {
+ "description": "Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed. Finalizers may be processed and removed in any order. Order is NOT enforced because it introduces significant risk of stuck finalizers. finalizers is a shared field, any actor with permission can reorder it. If the finalizer list is processed in order, then this can lead to a situation in which the component responsible for the first finalizer in the list is waiting for a signal (field value, external system, or other) produced by a component responsible for a finalizer later in the list, resulting in a deadlock. Without enforced ordering finalizers are free to order amongst themselves and are not vulnerable to ordering changes in the list.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "generateName": {
+ "description": "GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\n\nIf this field is specified and the generated name exists, the server will return a 409.\n\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "generation": {
+ "description": "A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "labels": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels",
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "managedFields": {
+ "description": "ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like \"ci-cd\". The set of fields is always in the version that the workflow used when modifying the object.",
+ "items": {
+ "description": "ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource that the fieldset applies to.",
+ "properties": {
+ "apiVersion": {
+ "description": "APIVersion defines the version of this resource that this field set applies to. The format is \"group/version\" just like the top-level APIVersion field. It is necessary to track the version of a field set because it cannot be automatically converted.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "fieldsType": {
+ "description": "FieldsType is the discriminator for the different fields format and version. There is currently only one possible value: \"FieldsV1\"",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "fieldsV1": {
+ "description": "FieldsV1 stores a set of fields in a data structure like a Trie, in JSON format.\n\nEach key is either a '.' representing the field itself, and will always map to an empty set, or a string representing a sub-field or item. The string will follow one of these four formats: 'f:', where is the name of a field in a struct, or key in a map 'v:', where is the exact json formatted value of a list item 'i:', where is position of a item in a list 'k:', where is a map of a list item's key fields to their unique values If a key maps to an empty Fields value, the field that key represents is part of the set.\n\nThe exact format is defined in sigs.k8s.io/structured-merge-diff",
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "manager": {
+ "description": "Manager is an identifier of the workflow managing these fields.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "operation": {
+ "description": "Operation is the type of operation which lead to this ManagedFieldsEntry being created. The only valid values for this field are 'Apply' and 'Update'.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "subresource": {
+ "description": "Subresource is the name of the subresource used to update that object, or empty string if the object was updated through the main resource. The value of this field is used to distinguish between managers, even if they share the same name. For example, a status update will be distinct from a regular update using the same manager name. Note that the APIVersion field is not related to the Subresource field and it always corresponds to the version of the main resource.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "time": {
+ "description": "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.",
+ "format": "date-time",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "name": {
+ "description": "Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "namespace": {
+ "description": "Namespace defines the space within which each name must be unique. An empty namespace is equivalent to the \"default\" namespace, but \"default\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\n\nMust be a DNS_LABEL. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "ownerReferences": {
+ "description": "List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.",
+ "items": {
+ "description": "OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.",
+ "properties": {
+ "apiVersion": {
+ "description": "API version of the referent.",
+ "type": "string"
+ },
+ "blockOwnerDeletion": {
+ "description": "If true, AND if the owner has the \"foregroundDeletion\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. See https://kubernetes.io/docs/concepts/architecture/garbage-collection/#foreground-deletion for how the garbage collector interacts with this field and enforces the foreground deletion. Defaults to false. To set this field, a user needs \"delete\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "controller": {
+ "description": "If true, this reference points to the managing controller.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "kind": {
+ "description": "Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
+ "type": "string"
+ },
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names",
+ "type": "string"
+ },
+ "uid": {
+ "description": "UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids",
+ "type": "string"
+ }
+ },
+ "required": [
+ "apiVersion",
+ "kind",
+ "name",
+ "uid"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-merge-key": "uid",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "resourceVersion": {
+ "description": "An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\n\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "selfLink": {
+ "description": "Deprecated: selfLink is a legacy read-only field that is no longer populated by the system.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "uid": {
+ "description": "UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\n\nPopulated by the system. Read-only. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": "object",
+ "x-kubernetes-group-version-kind": [
+ {
+ "group": "",
+ "kind": "ConfigMap",
+ "version": "v1"
+ }
+ ],
+ "additionalProperties": false,
+ "$schema": "http://json-schema.org/schema#"
+}
\ No newline at end of file
diff --git a/packages/editors/src/schemas/kubernetes/deployment-apps-v1.json b/packages/editors/src/schemas/kubernetes/deployment-apps-v1.json
new file mode 100644
index 0000000..b530c96
--- /dev/null
+++ b/packages/editors/src/schemas/kubernetes/deployment-apps-v1.json
@@ -0,0 +1,10291 @@
+{
+ "description": "Deployment enables declarative updates for Pods and ReplicaSets.",
+ "properties": {
+ "apiVersion": {
+ "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
+ "type": [
+ "string",
+ "null"
+ ],
+ "enum": [
+ "apps/v1"
+ ]
+ },
+ "kind": {
+ "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
+ "type": [
+ "string",
+ "null"
+ ],
+ "enum": [
+ "Deployment"
+ ]
+ },
+ "metadata": {
+ "description": "ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.",
+ "properties": {
+ "annotations": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations",
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "creationTimestamp": {
+ "description": "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.",
+ "format": "date-time",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "deletionGracePeriodSeconds": {
+ "description": "Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "deletionTimestamp": {
+ "description": "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.",
+ "format": "date-time",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "finalizers": {
+ "description": "Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed. Finalizers may be processed and removed in any order. Order is NOT enforced because it introduces significant risk of stuck finalizers. finalizers is a shared field, any actor with permission can reorder it. If the finalizer list is processed in order, then this can lead to a situation in which the component responsible for the first finalizer in the list is waiting for a signal (field value, external system, or other) produced by a component responsible for a finalizer later in the list, resulting in a deadlock. Without enforced ordering finalizers are free to order amongst themselves and are not vulnerable to ordering changes in the list.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "generateName": {
+ "description": "GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\n\nIf this field is specified and the generated name exists, the server will return a 409.\n\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "generation": {
+ "description": "A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "labels": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels",
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "managedFields": {
+ "description": "ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like \"ci-cd\". The set of fields is always in the version that the workflow used when modifying the object.",
+ "items": {
+ "description": "ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource that the fieldset applies to.",
+ "properties": {
+ "apiVersion": {
+ "description": "APIVersion defines the version of this resource that this field set applies to. The format is \"group/version\" just like the top-level APIVersion field. It is necessary to track the version of a field set because it cannot be automatically converted.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "fieldsType": {
+ "description": "FieldsType is the discriminator for the different fields format and version. There is currently only one possible value: \"FieldsV1\"",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "fieldsV1": {
+ "description": "FieldsV1 stores a set of fields in a data structure like a Trie, in JSON format.\n\nEach key is either a '.' representing the field itself, and will always map to an empty set, or a string representing a sub-field or item. The string will follow one of these four formats: 'f:', where is the name of a field in a struct, or key in a map 'v:', where is the exact json formatted value of a list item 'i:', where is position of a item in a list 'k:', where is a map of a list item's key fields to their unique values If a key maps to an empty Fields value, the field that key represents is part of the set.\n\nThe exact format is defined in sigs.k8s.io/structured-merge-diff",
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "manager": {
+ "description": "Manager is an identifier of the workflow managing these fields.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "operation": {
+ "description": "Operation is the type of operation which lead to this ManagedFieldsEntry being created. The only valid values for this field are 'Apply' and 'Update'.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "subresource": {
+ "description": "Subresource is the name of the subresource used to update that object, or empty string if the object was updated through the main resource. The value of this field is used to distinguish between managers, even if they share the same name. For example, a status update will be distinct from a regular update using the same manager name. Note that the APIVersion field is not related to the Subresource field and it always corresponds to the version of the main resource.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "time": {
+ "description": "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.",
+ "format": "date-time",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "name": {
+ "description": "Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "namespace": {
+ "description": "Namespace defines the space within which each name must be unique. An empty namespace is equivalent to the \"default\" namespace, but \"default\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\n\nMust be a DNS_LABEL. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "ownerReferences": {
+ "description": "List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.",
+ "items": {
+ "description": "OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.",
+ "properties": {
+ "apiVersion": {
+ "description": "API version of the referent.",
+ "type": "string"
+ },
+ "blockOwnerDeletion": {
+ "description": "If true, AND if the owner has the \"foregroundDeletion\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. See https://kubernetes.io/docs/concepts/architecture/garbage-collection/#foreground-deletion for how the garbage collector interacts with this field and enforces the foreground deletion. Defaults to false. To set this field, a user needs \"delete\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "controller": {
+ "description": "If true, this reference points to the managing controller.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "kind": {
+ "description": "Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
+ "type": "string"
+ },
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names",
+ "type": "string"
+ },
+ "uid": {
+ "description": "UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids",
+ "type": "string"
+ }
+ },
+ "required": [
+ "apiVersion",
+ "kind",
+ "name",
+ "uid"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-merge-key": "uid",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "resourceVersion": {
+ "description": "An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\n\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "selfLink": {
+ "description": "Deprecated: selfLink is a legacy read-only field that is no longer populated by the system.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "uid": {
+ "description": "UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\n\nPopulated by the system. Read-only. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "spec": {
+ "description": "DeploymentSpec is the specification of the desired behavior of the Deployment.",
+ "properties": {
+ "minReadySeconds": {
+ "description": "Minimum number of seconds for which a newly created pod should be ready without any of its container crashing, for it to be considered available. Defaults to 0 (pod will be considered available as soon as it is ready)",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "paused": {
+ "description": "Indicates that the deployment is paused.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "progressDeadlineSeconds": {
+ "description": "The maximum time in seconds for a deployment to make progress before it is considered to be failed. The deployment controller will continue to process failed deployments and a condition with a ProgressDeadlineExceeded reason will be surfaced in the deployment status. Note that progress will not be estimated during the time a deployment is paused. Defaults to 600s.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "replicas": {
+ "description": "Number of desired pods. This is a pointer to distinguish between explicit zero and not specified. Defaults to 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "revisionHistoryLimit": {
+ "description": "The number of old ReplicaSets to retain to allow rollback. This is a pointer to distinguish between explicit zero and not specified. Defaults to 10.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "selector": {
+ "description": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.",
+ "properties": {
+ "matchExpressions": {
+ "description": "matchExpressions is a list of label selector requirements. The requirements are ANDed.",
+ "items": {
+ "description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
+ "properties": {
+ "key": {
+ "description": "key is the label key that the selector applies to.",
+ "type": "string"
+ },
+ "operator": {
+ "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.",
+ "type": "string"
+ },
+ "values": {
+ "description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key",
+ "operator"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "matchLabels": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.",
+ "type": [
+ "object",
+ "null"
+ ]
+ }
+ },
+ "type": "object",
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "strategy": {
+ "description": "DeploymentStrategy describes how to replace existing pods with new ones.",
+ "properties": {
+ "rollingUpdate": {
+ "description": "Spec to control the desired behavior of rolling update.",
+ "properties": {
+ "maxSurge": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ },
+ "maxUnavailable": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": {
+ "description": "Type of deployment. Can be \"Recreate\" or \"RollingUpdate\". Default is RollingUpdate.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "template": {
+ "description": "PodTemplateSpec describes the data a pod should have when created from a template",
+ "properties": {
+ "metadata": {
+ "description": "ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.",
+ "properties": {
+ "annotations": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations",
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "creationTimestamp": {
+ "description": "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.",
+ "format": "date-time",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "deletionGracePeriodSeconds": {
+ "description": "Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "deletionTimestamp": {
+ "description": "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.",
+ "format": "date-time",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "finalizers": {
+ "description": "Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed. Finalizers may be processed and removed in any order. Order is NOT enforced because it introduces significant risk of stuck finalizers. finalizers is a shared field, any actor with permission can reorder it. If the finalizer list is processed in order, then this can lead to a situation in which the component responsible for the first finalizer in the list is waiting for a signal (field value, external system, or other) produced by a component responsible for a finalizer later in the list, resulting in a deadlock. Without enforced ordering finalizers are free to order amongst themselves and are not vulnerable to ordering changes in the list.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "generateName": {
+ "description": "GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\n\nIf this field is specified and the generated name exists, the server will return a 409.\n\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "generation": {
+ "description": "A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "labels": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels",
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "managedFields": {
+ "description": "ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like \"ci-cd\". The set of fields is always in the version that the workflow used when modifying the object.",
+ "items": {
+ "description": "ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource that the fieldset applies to.",
+ "properties": {
+ "apiVersion": {
+ "description": "APIVersion defines the version of this resource that this field set applies to. The format is \"group/version\" just like the top-level APIVersion field. It is necessary to track the version of a field set because it cannot be automatically converted.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "fieldsType": {
+ "description": "FieldsType is the discriminator for the different fields format and version. There is currently only one possible value: \"FieldsV1\"",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "fieldsV1": {
+ "description": "FieldsV1 stores a set of fields in a data structure like a Trie, in JSON format.\n\nEach key is either a '.' representing the field itself, and will always map to an empty set, or a string representing a sub-field or item. The string will follow one of these four formats: 'f:', where is the name of a field in a struct, or key in a map 'v:', where is the exact json formatted value of a list item 'i:', where is position of a item in a list 'k:', where is a map of a list item's key fields to their unique values If a key maps to an empty Fields value, the field that key represents is part of the set.\n\nThe exact format is defined in sigs.k8s.io/structured-merge-diff",
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "manager": {
+ "description": "Manager is an identifier of the workflow managing these fields.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "operation": {
+ "description": "Operation is the type of operation which lead to this ManagedFieldsEntry being created. The only valid values for this field are 'Apply' and 'Update'.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "subresource": {
+ "description": "Subresource is the name of the subresource used to update that object, or empty string if the object was updated through the main resource. The value of this field is used to distinguish between managers, even if they share the same name. For example, a status update will be distinct from a regular update using the same manager name. Note that the APIVersion field is not related to the Subresource field and it always corresponds to the version of the main resource.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "time": {
+ "description": "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.",
+ "format": "date-time",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "name": {
+ "description": "Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "namespace": {
+ "description": "Namespace defines the space within which each name must be unique. An empty namespace is equivalent to the \"default\" namespace, but \"default\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\n\nMust be a DNS_LABEL. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "ownerReferences": {
+ "description": "List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.",
+ "items": {
+ "description": "OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.",
+ "properties": {
+ "apiVersion": {
+ "description": "API version of the referent.",
+ "type": "string"
+ },
+ "blockOwnerDeletion": {
+ "description": "If true, AND if the owner has the \"foregroundDeletion\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. See https://kubernetes.io/docs/concepts/architecture/garbage-collection/#foreground-deletion for how the garbage collector interacts with this field and enforces the foreground deletion. Defaults to false. To set this field, a user needs \"delete\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "controller": {
+ "description": "If true, this reference points to the managing controller.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "kind": {
+ "description": "Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
+ "type": "string"
+ },
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names",
+ "type": "string"
+ },
+ "uid": {
+ "description": "UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids",
+ "type": "string"
+ }
+ },
+ "required": [
+ "apiVersion",
+ "kind",
+ "name",
+ "uid"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-merge-key": "uid",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "resourceVersion": {
+ "description": "An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\n\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "selfLink": {
+ "description": "Deprecated: selfLink is a legacy read-only field that is no longer populated by the system.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "uid": {
+ "description": "UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\n\nPopulated by the system. Read-only. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "spec": {
+ "description": "PodSpec is a description of a pod.",
+ "properties": {
+ "activeDeadlineSeconds": {
+ "description": "Optional duration in seconds the pod may be active on the node relative to StartTime before the system will actively try to mark it failed and kill associated containers. Value must be a positive integer.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "affinity": {
+ "description": "Affinity is a group of affinity scheduling rules.",
+ "properties": {
+ "nodeAffinity": {
+ "description": "Node affinity is a group of node affinity scheduling rules.",
+ "properties": {
+ "preferredDuringSchedulingIgnoredDuringExecution": {
+ "description": "The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \"weight\" to the sum if the node matches the corresponding matchExpressions; the node(s) with the highest sum are the most preferred.",
+ "items": {
+ "description": "An empty preferred scheduling term matches all objects with implicit weight 0 (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op).",
+ "properties": {
+ "preference": {
+ "description": "A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.",
+ "properties": {
+ "matchExpressions": {
+ "description": "A list of node selector requirements by node's labels.",
+ "items": {
+ "description": "A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
+ "properties": {
+ "key": {
+ "description": "The label key that the selector applies to.",
+ "type": "string"
+ },
+ "operator": {
+ "description": "Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.",
+ "type": "string"
+ },
+ "values": {
+ "description": "An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key",
+ "operator"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "matchFields": {
+ "description": "A list of node selector requirements by node's fields.",
+ "items": {
+ "description": "A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
+ "properties": {
+ "key": {
+ "description": "The label key that the selector applies to.",
+ "type": "string"
+ },
+ "operator": {
+ "description": "Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.",
+ "type": "string"
+ },
+ "values": {
+ "description": "An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key",
+ "operator"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": "object",
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "weight": {
+ "description": "Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100.",
+ "format": "int32",
+ "type": "integer"
+ }
+ },
+ "required": [
+ "weight",
+ "preference"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "requiredDuringSchedulingIgnoredDuringExecution": {
+ "description": "A node selector represents the union of the results of one or more label queries over a set of nodes; that is, it represents the OR of the selectors represented by the node selector terms.",
+ "properties": {
+ "nodeSelectorTerms": {
+ "description": "Required. A list of node selector terms. The terms are ORed.",
+ "items": {
+ "description": "A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.",
+ "properties": {
+ "matchExpressions": {
+ "description": "A list of node selector requirements by node's labels.",
+ "items": {
+ "description": "A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
+ "properties": {
+ "key": {
+ "description": "The label key that the selector applies to.",
+ "type": "string"
+ },
+ "operator": {
+ "description": "Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.",
+ "type": "string"
+ },
+ "values": {
+ "description": "An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key",
+ "operator"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "matchFields": {
+ "description": "A list of node selector requirements by node's fields.",
+ "items": {
+ "description": "A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
+ "properties": {
+ "key": {
+ "description": "The label key that the selector applies to.",
+ "type": "string"
+ },
+ "operator": {
+ "description": "Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.",
+ "type": "string"
+ },
+ "values": {
+ "description": "An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key",
+ "operator"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "type": "array"
+ }
+ },
+ "required": [
+ "nodeSelectorTerms"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "podAffinity": {
+ "description": "Pod affinity is a group of inter pod affinity scheduling rules.",
+ "properties": {
+ "preferredDuringSchedulingIgnoredDuringExecution": {
+ "description": "The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \"weight\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.",
+ "items": {
+ "description": "The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s)",
+ "properties": {
+ "podAffinityTerm": {
+ "description": "Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key matches that of any node on which a pod of the set of pods is running",
+ "properties": {
+ "labelSelector": {
+ "description": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.",
+ "properties": {
+ "matchExpressions": {
+ "description": "matchExpressions is a list of label selector requirements. The requirements are ANDed.",
+ "items": {
+ "description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
+ "properties": {
+ "key": {
+ "description": "key is the label key that the selector applies to.",
+ "type": "string"
+ },
+ "operator": {
+ "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.",
+ "type": "string"
+ },
+ "values": {
+ "description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key",
+ "operator"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "matchLabels": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.",
+ "type": [
+ "object",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "namespaceSelector": {
+ "description": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.",
+ "properties": {
+ "matchExpressions": {
+ "description": "matchExpressions is a list of label selector requirements. The requirements are ANDed.",
+ "items": {
+ "description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
+ "properties": {
+ "key": {
+ "description": "key is the label key that the selector applies to.",
+ "type": "string"
+ },
+ "operator": {
+ "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.",
+ "type": "string"
+ },
+ "values": {
+ "description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key",
+ "operator"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "matchLabels": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.",
+ "type": [
+ "object",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "namespaces": {
+ "description": "namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector means \"this pod's namespace\".",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "topologyKey": {
+ "description": "This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "topologyKey"
+ ],
+ "type": "object",
+ "additionalProperties": false
+ },
+ "weight": {
+ "description": "weight associated with matching the corresponding podAffinityTerm, in the range 1-100.",
+ "format": "int32",
+ "type": "integer"
+ }
+ },
+ "required": [
+ "weight",
+ "podAffinityTerm"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "requiredDuringSchedulingIgnoredDuringExecution": {
+ "description": "If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied.",
+ "items": {
+ "description": "Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key matches that of any node on which a pod of the set of pods is running",
+ "properties": {
+ "labelSelector": {
+ "description": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.",
+ "properties": {
+ "matchExpressions": {
+ "description": "matchExpressions is a list of label selector requirements. The requirements are ANDed.",
+ "items": {
+ "description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
+ "properties": {
+ "key": {
+ "description": "key is the label key that the selector applies to.",
+ "type": "string"
+ },
+ "operator": {
+ "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.",
+ "type": "string"
+ },
+ "values": {
+ "description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key",
+ "operator"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "matchLabels": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.",
+ "type": [
+ "object",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "namespaceSelector": {
+ "description": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.",
+ "properties": {
+ "matchExpressions": {
+ "description": "matchExpressions is a list of label selector requirements. The requirements are ANDed.",
+ "items": {
+ "description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
+ "properties": {
+ "key": {
+ "description": "key is the label key that the selector applies to.",
+ "type": "string"
+ },
+ "operator": {
+ "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.",
+ "type": "string"
+ },
+ "values": {
+ "description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key",
+ "operator"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "matchLabels": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.",
+ "type": [
+ "object",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "namespaces": {
+ "description": "namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector means \"this pod's namespace\".",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "topologyKey": {
+ "description": "This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "topologyKey"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "podAntiAffinity": {
+ "description": "Pod anti affinity is a group of inter pod anti affinity scheduling rules.",
+ "properties": {
+ "preferredDuringSchedulingIgnoredDuringExecution": {
+ "description": "The scheduler will prefer to schedule pods to nodes that satisfy the anti-affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling anti-affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \"weight\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.",
+ "items": {
+ "description": "The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s)",
+ "properties": {
+ "podAffinityTerm": {
+ "description": "Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key matches that of any node on which a pod of the set of pods is running",
+ "properties": {
+ "labelSelector": {
+ "description": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.",
+ "properties": {
+ "matchExpressions": {
+ "description": "matchExpressions is a list of label selector requirements. The requirements are ANDed.",
+ "items": {
+ "description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
+ "properties": {
+ "key": {
+ "description": "key is the label key that the selector applies to.",
+ "type": "string"
+ },
+ "operator": {
+ "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.",
+ "type": "string"
+ },
+ "values": {
+ "description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key",
+ "operator"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "matchLabels": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.",
+ "type": [
+ "object",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "namespaceSelector": {
+ "description": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.",
+ "properties": {
+ "matchExpressions": {
+ "description": "matchExpressions is a list of label selector requirements. The requirements are ANDed.",
+ "items": {
+ "description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
+ "properties": {
+ "key": {
+ "description": "key is the label key that the selector applies to.",
+ "type": "string"
+ },
+ "operator": {
+ "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.",
+ "type": "string"
+ },
+ "values": {
+ "description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key",
+ "operator"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "matchLabels": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.",
+ "type": [
+ "object",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "namespaces": {
+ "description": "namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector means \"this pod's namespace\".",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "topologyKey": {
+ "description": "This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "topologyKey"
+ ],
+ "type": "object",
+ "additionalProperties": false
+ },
+ "weight": {
+ "description": "weight associated with matching the corresponding podAffinityTerm, in the range 1-100.",
+ "format": "int32",
+ "type": "integer"
+ }
+ },
+ "required": [
+ "weight",
+ "podAffinityTerm"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "requiredDuringSchedulingIgnoredDuringExecution": {
+ "description": "If the anti-affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the anti-affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied.",
+ "items": {
+ "description": "Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key matches that of any node on which a pod of the set of pods is running",
+ "properties": {
+ "labelSelector": {
+ "description": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.",
+ "properties": {
+ "matchExpressions": {
+ "description": "matchExpressions is a list of label selector requirements. The requirements are ANDed.",
+ "items": {
+ "description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
+ "properties": {
+ "key": {
+ "description": "key is the label key that the selector applies to.",
+ "type": "string"
+ },
+ "operator": {
+ "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.",
+ "type": "string"
+ },
+ "values": {
+ "description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key",
+ "operator"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "matchLabels": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.",
+ "type": [
+ "object",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "namespaceSelector": {
+ "description": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.",
+ "properties": {
+ "matchExpressions": {
+ "description": "matchExpressions is a list of label selector requirements. The requirements are ANDed.",
+ "items": {
+ "description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
+ "properties": {
+ "key": {
+ "description": "key is the label key that the selector applies to.",
+ "type": "string"
+ },
+ "operator": {
+ "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.",
+ "type": "string"
+ },
+ "values": {
+ "description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key",
+ "operator"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "matchLabels": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.",
+ "type": [
+ "object",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "namespaces": {
+ "description": "namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector means \"this pod's namespace\".",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "topologyKey": {
+ "description": "This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "topologyKey"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "automountServiceAccountToken": {
+ "description": "AutomountServiceAccountToken indicates whether a service account token should be automatically mounted.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "containers": {
+ "description": "List of containers belonging to the pod. Containers cannot currently be added or removed. There must be at least one container in a Pod. Cannot be updated.",
+ "items": {
+ "description": "A single application container that you want to run within a pod.",
+ "properties": {
+ "args": {
+ "description": "Arguments to the entrypoint. The container image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "command": {
+ "description": "Entrypoint array. Not executed within a shell. The container image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "env": {
+ "description": "List of environment variables to set in the container. Cannot be updated.",
+ "items": {
+ "description": "EnvVar represents an environment variable present in a Container.",
+ "properties": {
+ "name": {
+ "description": "Name of the environment variable. Must be a C_IDENTIFIER.",
+ "type": "string"
+ },
+ "value": {
+ "description": "Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to \"\".",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "valueFrom": {
+ "description": "EnvVarSource represents a source for the value of an EnvVar.",
+ "properties": {
+ "configMapKeyRef": {
+ "description": "Selects a key from a ConfigMap.",
+ "properties": {
+ "key": {
+ "description": "The key to select.",
+ "type": "string"
+ },
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "optional": {
+ "description": "Specify whether the ConfigMap or its key must be defined",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "fieldRef": {
+ "description": "ObjectFieldSelector selects an APIVersioned field of an object.",
+ "properties": {
+ "apiVersion": {
+ "description": "Version of the schema the FieldPath is written in terms of, defaults to \"v1\".",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "fieldPath": {
+ "description": "Path of the field to select in the specified API version.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "fieldPath"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "resourceFieldRef": {
+ "description": "ResourceFieldSelector represents container resources (cpu, memory) and their output format",
+ "properties": {
+ "containerName": {
+ "description": "Container name: required for volumes, optional for env vars",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "divisor": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ ]
+ },
+ "resource": {
+ "description": "Required: resource to select",
+ "type": "string"
+ }
+ },
+ "required": [
+ "resource"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "secretKeyRef": {
+ "description": "SecretKeySelector selects a key of a Secret.",
+ "properties": {
+ "key": {
+ "description": "The key of the secret to select from. Must be a valid secret key.",
+ "type": "string"
+ },
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "optional": {
+ "description": "Specify whether the Secret or its key must be defined",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "required": [
+ "name"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "envFrom": {
+ "description": "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.",
+ "items": {
+ "description": "EnvFromSource represents the source of a set of ConfigMaps",
+ "properties": {
+ "configMapRef": {
+ "description": "ConfigMapEnvSource selects a ConfigMap to populate the environment variables with.\n\nThe contents of the target ConfigMap's Data field will represent the key-value pairs as environment variables.",
+ "properties": {
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "optional": {
+ "description": "Specify whether the ConfigMap must be defined",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "prefix": {
+ "description": "An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "secretRef": {
+ "description": "SecretEnvSource selects a Secret to populate the environment variables with.\n\nThe contents of the target Secret's Data field will represent the key-value pairs as environment variables.",
+ "properties": {
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "optional": {
+ "description": "Specify whether the Secret must be defined",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "image": {
+ "description": "Container image name. More info: https://kubernetes.io/docs/concepts/containers/images This field is optional to allow higher level config management to default or override container images in workload controllers like Deployments and StatefulSets.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "imagePullPolicy": {
+ "description": "Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "lifecycle": {
+ "description": "Lifecycle describes actions that the management system should take in response to container lifecycle events. For the PostStart and PreStop lifecycle handlers, management of the container blocks until the action is complete, unless the container process fails, in which case the handler is aborted.",
+ "properties": {
+ "postStart": {
+ "description": "LifecycleHandler defines a specific action that should be taken in a lifecycle hook. One and only one of the fields, except TCPSocket must be specified.",
+ "properties": {
+ "exec": {
+ "description": "ExecAction describes a \"run in container\" action.",
+ "properties": {
+ "command": {
+ "description": "Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "httpGet": {
+ "description": "HTTPGetAction describes an action based on HTTP Get requests.",
+ "properties": {
+ "host": {
+ "description": "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "httpHeaders": {
+ "description": "Custom headers to set in the request. HTTP allows repeated headers.",
+ "items": {
+ "description": "HTTPHeader describes a custom header to be used in HTTP probes",
+ "properties": {
+ "name": {
+ "description": "The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.",
+ "type": "string"
+ },
+ "value": {
+ "description": "The header field value",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "Path to access on the HTTP server.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ },
+ "scheme": {
+ "description": "Scheme to use for connecting to the host. Defaults to HTTP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "tcpSocket": {
+ "description": "TCPSocketAction describes an action based on opening a socket",
+ "properties": {
+ "host": {
+ "description": "Optional: Host name to connect to, defaults to the pod IP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "preStop": {
+ "description": "LifecycleHandler defines a specific action that should be taken in a lifecycle hook. One and only one of the fields, except TCPSocket must be specified.",
+ "properties": {
+ "exec": {
+ "description": "ExecAction describes a \"run in container\" action.",
+ "properties": {
+ "command": {
+ "description": "Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "httpGet": {
+ "description": "HTTPGetAction describes an action based on HTTP Get requests.",
+ "properties": {
+ "host": {
+ "description": "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "httpHeaders": {
+ "description": "Custom headers to set in the request. HTTP allows repeated headers.",
+ "items": {
+ "description": "HTTPHeader describes a custom header to be used in HTTP probes",
+ "properties": {
+ "name": {
+ "description": "The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.",
+ "type": "string"
+ },
+ "value": {
+ "description": "The header field value",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "Path to access on the HTTP server.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ },
+ "scheme": {
+ "description": "Scheme to use for connecting to the host. Defaults to HTTP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "tcpSocket": {
+ "description": "TCPSocketAction describes an action based on opening a socket",
+ "properties": {
+ "host": {
+ "description": "Optional: Host name to connect to, defaults to the pod IP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "livenessProbe": {
+ "description": "Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.",
+ "properties": {
+ "exec": {
+ "description": "ExecAction describes a \"run in container\" action.",
+ "properties": {
+ "command": {
+ "description": "Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "failureThreshold": {
+ "description": "Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "grpc": {
+ "properties": {
+ "port": {
+ "description": "Port number of the gRPC service. Number must be in the range 1 to 65535.",
+ "format": "int32",
+ "type": "integer"
+ },
+ "service": {
+ "description": "Service is the name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md).\n\nIf this is not specified, the default behavior is defined by gRPC.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "httpGet": {
+ "description": "HTTPGetAction describes an action based on HTTP Get requests.",
+ "properties": {
+ "host": {
+ "description": "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "httpHeaders": {
+ "description": "Custom headers to set in the request. HTTP allows repeated headers.",
+ "items": {
+ "description": "HTTPHeader describes a custom header to be used in HTTP probes",
+ "properties": {
+ "name": {
+ "description": "The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.",
+ "type": "string"
+ },
+ "value": {
+ "description": "The header field value",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "Path to access on the HTTP server.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ },
+ "scheme": {
+ "description": "Scheme to use for connecting to the host. Defaults to HTTP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "initialDelaySeconds": {
+ "description": "Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "periodSeconds": {
+ "description": "How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "successThreshold": {
+ "description": "Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "tcpSocket": {
+ "description": "TCPSocketAction describes an action based on opening a socket",
+ "properties": {
+ "host": {
+ "description": "Optional: Host name to connect to, defaults to the pod IP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "terminationGracePeriodSeconds": {
+ "description": "Optional duration in seconds the pod needs to terminate gracefully upon probe failure. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this value overrides the value provided by the pod spec. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "timeoutSeconds": {
+ "description": "Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "name": {
+ "description": "Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated.",
+ "type": "string"
+ },
+ "ports": {
+ "description": "List of ports to expose from the container. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default \"0.0.0.0\" address inside a container will be accessible from the network. Modifying this array with strategic merge patch may corrupt the data. For more information See https://github.com/kubernetes/kubernetes/issues/108255. Cannot be updated.",
+ "items": {
+ "description": "ContainerPort represents a network port in a single container.",
+ "properties": {
+ "containerPort": {
+ "description": "Number of port to expose on the pod's IP address. This must be a valid port number, 0 < x < 65536.",
+ "format": "int32",
+ "type": "integer"
+ },
+ "hostIP": {
+ "description": "What host IP to bind the external port to.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "hostPort": {
+ "description": "Number of port to expose on the host. If specified, this must be a valid port number, 0 < x < 65536. If HostNetwork is specified, this must match ContainerPort. Most containers do not need this.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "name": {
+ "description": "If specified, this must be an IANA_SVC_NAME and unique within the pod. Each named port in a pod must have a unique name. Name for the port that can be referred to by services.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "protocol": {
+ "description": "Protocol for port. Must be UDP, TCP, or SCTP. Defaults to \"TCP\".",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "containerPort"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-list-map-keys": [
+ "containerPort",
+ "protocol"
+ ],
+ "x-kubernetes-list-type": "map",
+ "x-kubernetes-patch-merge-key": "containerPort",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "readinessProbe": {
+ "description": "Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.",
+ "properties": {
+ "exec": {
+ "description": "ExecAction describes a \"run in container\" action.",
+ "properties": {
+ "command": {
+ "description": "Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "failureThreshold": {
+ "description": "Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "grpc": {
+ "properties": {
+ "port": {
+ "description": "Port number of the gRPC service. Number must be in the range 1 to 65535.",
+ "format": "int32",
+ "type": "integer"
+ },
+ "service": {
+ "description": "Service is the name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md).\n\nIf this is not specified, the default behavior is defined by gRPC.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "httpGet": {
+ "description": "HTTPGetAction describes an action based on HTTP Get requests.",
+ "properties": {
+ "host": {
+ "description": "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "httpHeaders": {
+ "description": "Custom headers to set in the request. HTTP allows repeated headers.",
+ "items": {
+ "description": "HTTPHeader describes a custom header to be used in HTTP probes",
+ "properties": {
+ "name": {
+ "description": "The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.",
+ "type": "string"
+ },
+ "value": {
+ "description": "The header field value",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "Path to access on the HTTP server.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ },
+ "scheme": {
+ "description": "Scheme to use for connecting to the host. Defaults to HTTP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "initialDelaySeconds": {
+ "description": "Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "periodSeconds": {
+ "description": "How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "successThreshold": {
+ "description": "Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "tcpSocket": {
+ "description": "TCPSocketAction describes an action based on opening a socket",
+ "properties": {
+ "host": {
+ "description": "Optional: Host name to connect to, defaults to the pod IP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "terminationGracePeriodSeconds": {
+ "description": "Optional duration in seconds the pod needs to terminate gracefully upon probe failure. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this value overrides the value provided by the pod spec. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "timeoutSeconds": {
+ "description": "Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "resizePolicy": {
+ "description": "Resources resize policy for the container.",
+ "items": {
+ "description": "ContainerResizePolicy represents resource resize policy for the container.",
+ "properties": {
+ "resourceName": {
+ "description": "Name of the resource to which this resource resize policy applies. Supported values: cpu, memory.",
+ "type": "string"
+ },
+ "restartPolicy": {
+ "description": "Restart policy to apply when specified resource is resized. If not specified, it defaults to NotRequired.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "resourceName",
+ "restartPolicy"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-list-type": "atomic"
+ },
+ "resources": {
+ "description": "ResourceRequirements describes the compute resource requirements.",
+ "properties": {
+ "claims": {
+ "description": "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container.\n\nThis is an alpha field and requires enabling the DynamicResourceAllocation feature gate.\n\nThis field is immutable. It can only be set for containers.",
+ "items": {
+ "description": "ResourceClaim references one entry in PodSpec.ResourceClaims.",
+ "properties": {
+ "name": {
+ "description": "Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-list-map-keys": [
+ "name"
+ ],
+ "x-kubernetes-list-type": "map"
+ },
+ "limits": {
+ "additionalProperties": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ ]
+ },
+ "description": "Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/",
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "requests": {
+ "additionalProperties": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ ]
+ },
+ "description": "Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/",
+ "type": [
+ "object",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "restartPolicy": {
+ "description": "RestartPolicy defines the restart behavior of individual containers in a pod. This field may only be set for init containers, and the only allowed value is \"Always\". For non-init containers or when this field is not specified, the restart behavior is defined by the Pod's restart policy and the container type. Setting the RestartPolicy as \"Always\" for the init container will have the following effect: this init container will be continually restarted on exit until all regular containers have terminated. Once all regular containers have completed, all init containers with restartPolicy \"Always\" will be shut down. This lifecycle differs from normal init containers and is often referred to as a \"sidecar\" container. Although this init container still starts in the init container sequence, it does not wait for the container to complete before proceeding to the next init container. Instead, the next init container starts immediately after this init container is started, or after any startupProbe has successfully completed.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "securityContext": {
+ "description": "SecurityContext holds security configuration that will be applied to a container. Some fields are present in both SecurityContext and PodSecurityContext. When both are set, the values in SecurityContext take precedence.",
+ "properties": {
+ "allowPrivilegeEscalation": {
+ "description": "AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process. This bool directly controls if the no_new_privs flag will be set on the container process. AllowPrivilegeEscalation is true always when the container is: 1) run as Privileged 2) has CAP_SYS_ADMIN Note that this field cannot be set when spec.os.name is windows.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "capabilities": {
+ "description": "Adds and removes POSIX capabilities from running containers.",
+ "properties": {
+ "add": {
+ "description": "Added capabilities",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "drop": {
+ "description": "Removed capabilities",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "privileged": {
+ "description": "Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false. Note that this field cannot be set when spec.os.name is windows.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "procMount": {
+ "description": "procMount denotes the type of proc mount to use for the containers. The default is DefaultProcMount which uses the container runtime defaults for readonly paths and masked paths. This requires the ProcMountType feature flag to be enabled. Note that this field cannot be set when spec.os.name is windows.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "readOnlyRootFilesystem": {
+ "description": "Whether this container has a read-only root filesystem. Default is false. Note that this field cannot be set when spec.os.name is windows.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "runAsGroup": {
+ "description": "The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is windows.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "runAsNonRoot": {
+ "description": "Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "runAsUser": {
+ "description": "The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is windows.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "seLinuxOptions": {
+ "description": "SELinuxOptions are the labels to be applied to the container",
+ "properties": {
+ "level": {
+ "description": "Level is SELinux level label that applies to the container.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "role": {
+ "description": "Role is a SELinux role label that applies to the container.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": {
+ "description": "Type is a SELinux type label that applies to the container.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "user": {
+ "description": "User is a SELinux user label that applies to the container.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "seccompProfile": {
+ "description": "SeccompProfile defines a pod/container's seccomp profile settings. Only one profile source may be set.",
+ "properties": {
+ "localhostProfile": {
+ "description": "localhostProfile indicates a profile defined in a file on the node should be used. The profile must be preconfigured on the node to work. Must be a descending path, relative to the kubelet's configured seccomp profile location. Must be set if type is \"Localhost\". Must NOT be set for any other type.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": {
+ "description": "type indicates which kind of seccomp profile will be applied. Valid options are:\n\nLocalhost - a profile defined in a file on the node should be used. RuntimeDefault - the container runtime default profile should be used. Unconfined - no profile should be applied.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-unions": [
+ {
+ "discriminator": "type",
+ "fields-to-discriminateBy": {
+ "localhostProfile": "LocalhostProfile"
+ }
+ }
+ ],
+ "additionalProperties": false
+ },
+ "windowsOptions": {
+ "description": "WindowsSecurityContextOptions contain Windows-specific options and credentials.",
+ "properties": {
+ "gmsaCredentialSpec": {
+ "description": "GMSACredentialSpec is where the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the GMSA credential spec named by the GMSACredentialSpecName field.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "gmsaCredentialSpecName": {
+ "description": "GMSACredentialSpecName is the name of the GMSA credential spec to use.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "hostProcess": {
+ "description": "HostProcess determines if a container should be run as a 'Host Process' container. All of a Pod's containers must have the same effective HostProcess value (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). In addition, if HostProcess is true then HostNetwork must also be set to true.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "runAsUserName": {
+ "description": "The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "startupProbe": {
+ "description": "Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.",
+ "properties": {
+ "exec": {
+ "description": "ExecAction describes a \"run in container\" action.",
+ "properties": {
+ "command": {
+ "description": "Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "failureThreshold": {
+ "description": "Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "grpc": {
+ "properties": {
+ "port": {
+ "description": "Port number of the gRPC service. Number must be in the range 1 to 65535.",
+ "format": "int32",
+ "type": "integer"
+ },
+ "service": {
+ "description": "Service is the name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md).\n\nIf this is not specified, the default behavior is defined by gRPC.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "httpGet": {
+ "description": "HTTPGetAction describes an action based on HTTP Get requests.",
+ "properties": {
+ "host": {
+ "description": "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "httpHeaders": {
+ "description": "Custom headers to set in the request. HTTP allows repeated headers.",
+ "items": {
+ "description": "HTTPHeader describes a custom header to be used in HTTP probes",
+ "properties": {
+ "name": {
+ "description": "The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.",
+ "type": "string"
+ },
+ "value": {
+ "description": "The header field value",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "Path to access on the HTTP server.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ },
+ "scheme": {
+ "description": "Scheme to use for connecting to the host. Defaults to HTTP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "initialDelaySeconds": {
+ "description": "Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "periodSeconds": {
+ "description": "How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "successThreshold": {
+ "description": "Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "tcpSocket": {
+ "description": "TCPSocketAction describes an action based on opening a socket",
+ "properties": {
+ "host": {
+ "description": "Optional: Host name to connect to, defaults to the pod IP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "terminationGracePeriodSeconds": {
+ "description": "Optional duration in seconds the pod needs to terminate gracefully upon probe failure. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this value overrides the value provided by the pod spec. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "timeoutSeconds": {
+ "description": "Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "stdin": {
+ "description": "Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "stdinOnce": {
+ "description": "Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "terminationMessagePath": {
+ "description": "Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "terminationMessagePolicy": {
+ "description": "Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "tty": {
+ "description": "Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "volumeDevices": {
+ "description": "volumeDevices is the list of block devices to be used by the container.",
+ "items": {
+ "description": "volumeDevice describes a mapping of a raw block device within a container.",
+ "properties": {
+ "devicePath": {
+ "description": "devicePath is the path inside of the container that the device will be mapped to.",
+ "type": "string"
+ },
+ "name": {
+ "description": "name must match the name of a persistentVolumeClaim in the pod",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "devicePath"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-merge-key": "devicePath",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "volumeMounts": {
+ "description": "Pod volumes to mount into the container's filesystem. Cannot be updated.",
+ "items": {
+ "description": "VolumeMount describes a mounting of a Volume within a container.",
+ "properties": {
+ "mountPath": {
+ "description": "Path within the container at which the volume should be mounted. Must not contain ':'.",
+ "type": "string"
+ },
+ "mountPropagation": {
+ "description": "mountPropagation determines how mounts are propagated from the host to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "name": {
+ "description": "This must match the Name of a Volume.",
+ "type": "string"
+ },
+ "readOnly": {
+ "description": "Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "subPath": {
+ "description": "Path within the volume from which the container's volume should be mounted. Defaults to \"\" (volume's root).",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "subPathExpr": {
+ "description": "Expanded path within the volume from which the container's volume should be mounted. Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. Defaults to \"\" (volume's root). SubPathExpr and SubPath are mutually exclusive.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "name",
+ "mountPath"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-merge-key": "mountPath",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "workingDir": {
+ "description": "Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "name"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": "array",
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "dnsConfig": {
+ "description": "PodDNSConfig defines the DNS parameters of a pod in addition to those generated from DNSPolicy.",
+ "properties": {
+ "nameservers": {
+ "description": "A list of DNS name server IP addresses. This will be appended to the base nameservers generated from DNSPolicy. Duplicated nameservers will be removed.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "options": {
+ "description": "A list of DNS resolver options. This will be merged with the base options generated from DNSPolicy. Duplicated entries will be removed. Resolution options given in Options will override those that appear in the base DNSPolicy.",
+ "items": {
+ "description": "PodDNSConfigOption defines DNS resolver options of a pod.",
+ "properties": {
+ "name": {
+ "description": "Required.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "value": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "searches": {
+ "description": "A list of DNS search domains for host-name lookup. This will be appended to the base search paths generated from DNSPolicy. Duplicated search paths will be removed.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "dnsPolicy": {
+ "description": "Set DNS policy for the pod. Defaults to \"ClusterFirst\". Valid values are 'ClusterFirstWithHostNet', 'ClusterFirst', 'Default' or 'None'. DNS parameters given in DNSConfig will be merged with the policy selected with DNSPolicy. To have DNS options set along with hostNetwork, you have to specify DNS policy explicitly to 'ClusterFirstWithHostNet'.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "enableServiceLinks": {
+ "description": "EnableServiceLinks indicates whether information about services should be injected into pod's environment variables, matching the syntax of Docker links. Optional: Defaults to true.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "ephemeralContainers": {
+ "description": "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource.",
+ "items": {
+ "description": "An EphemeralContainer is a temporary container that you may add to an existing Pod for user-initiated activities such as debugging. Ephemeral containers have no resource or scheduling guarantees, and they will not be restarted when they exit or when a Pod is removed or restarted. The kubelet may evict a Pod if an ephemeral container causes the Pod to exceed its resource allocation.\n\nTo add an ephemeral container, use the ephemeralcontainers subresource of an existing Pod. Ephemeral containers may not be removed or restarted.",
+ "properties": {
+ "args": {
+ "description": "Arguments to the entrypoint. The image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "command": {
+ "description": "Entrypoint array. Not executed within a shell. The image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "env": {
+ "description": "List of environment variables to set in the container. Cannot be updated.",
+ "items": {
+ "description": "EnvVar represents an environment variable present in a Container.",
+ "properties": {
+ "name": {
+ "description": "Name of the environment variable. Must be a C_IDENTIFIER.",
+ "type": "string"
+ },
+ "value": {
+ "description": "Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to \"\".",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "valueFrom": {
+ "description": "EnvVarSource represents a source for the value of an EnvVar.",
+ "properties": {
+ "configMapKeyRef": {
+ "description": "Selects a key from a ConfigMap.",
+ "properties": {
+ "key": {
+ "description": "The key to select.",
+ "type": "string"
+ },
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "optional": {
+ "description": "Specify whether the ConfigMap or its key must be defined",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "fieldRef": {
+ "description": "ObjectFieldSelector selects an APIVersioned field of an object.",
+ "properties": {
+ "apiVersion": {
+ "description": "Version of the schema the FieldPath is written in terms of, defaults to \"v1\".",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "fieldPath": {
+ "description": "Path of the field to select in the specified API version.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "fieldPath"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "resourceFieldRef": {
+ "description": "ResourceFieldSelector represents container resources (cpu, memory) and their output format",
+ "properties": {
+ "containerName": {
+ "description": "Container name: required for volumes, optional for env vars",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "divisor": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ ]
+ },
+ "resource": {
+ "description": "Required: resource to select",
+ "type": "string"
+ }
+ },
+ "required": [
+ "resource"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "secretKeyRef": {
+ "description": "SecretKeySelector selects a key of a Secret.",
+ "properties": {
+ "key": {
+ "description": "The key of the secret to select from. Must be a valid secret key.",
+ "type": "string"
+ },
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "optional": {
+ "description": "Specify whether the Secret or its key must be defined",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "required": [
+ "name"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "envFrom": {
+ "description": "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.",
+ "items": {
+ "description": "EnvFromSource represents the source of a set of ConfigMaps",
+ "properties": {
+ "configMapRef": {
+ "description": "ConfigMapEnvSource selects a ConfigMap to populate the environment variables with.\n\nThe contents of the target ConfigMap's Data field will represent the key-value pairs as environment variables.",
+ "properties": {
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "optional": {
+ "description": "Specify whether the ConfigMap must be defined",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "prefix": {
+ "description": "An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "secretRef": {
+ "description": "SecretEnvSource selects a Secret to populate the environment variables with.\n\nThe contents of the target Secret's Data field will represent the key-value pairs as environment variables.",
+ "properties": {
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "optional": {
+ "description": "Specify whether the Secret must be defined",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "image": {
+ "description": "Container image name. More info: https://kubernetes.io/docs/concepts/containers/images",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "imagePullPolicy": {
+ "description": "Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "lifecycle": {
+ "description": "Lifecycle describes actions that the management system should take in response to container lifecycle events. For the PostStart and PreStop lifecycle handlers, management of the container blocks until the action is complete, unless the container process fails, in which case the handler is aborted.",
+ "properties": {
+ "postStart": {
+ "description": "LifecycleHandler defines a specific action that should be taken in a lifecycle hook. One and only one of the fields, except TCPSocket must be specified.",
+ "properties": {
+ "exec": {
+ "description": "ExecAction describes a \"run in container\" action.",
+ "properties": {
+ "command": {
+ "description": "Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "httpGet": {
+ "description": "HTTPGetAction describes an action based on HTTP Get requests.",
+ "properties": {
+ "host": {
+ "description": "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "httpHeaders": {
+ "description": "Custom headers to set in the request. HTTP allows repeated headers.",
+ "items": {
+ "description": "HTTPHeader describes a custom header to be used in HTTP probes",
+ "properties": {
+ "name": {
+ "description": "The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.",
+ "type": "string"
+ },
+ "value": {
+ "description": "The header field value",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "Path to access on the HTTP server.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ },
+ "scheme": {
+ "description": "Scheme to use for connecting to the host. Defaults to HTTP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "tcpSocket": {
+ "description": "TCPSocketAction describes an action based on opening a socket",
+ "properties": {
+ "host": {
+ "description": "Optional: Host name to connect to, defaults to the pod IP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "preStop": {
+ "description": "LifecycleHandler defines a specific action that should be taken in a lifecycle hook. One and only one of the fields, except TCPSocket must be specified.",
+ "properties": {
+ "exec": {
+ "description": "ExecAction describes a \"run in container\" action.",
+ "properties": {
+ "command": {
+ "description": "Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "httpGet": {
+ "description": "HTTPGetAction describes an action based on HTTP Get requests.",
+ "properties": {
+ "host": {
+ "description": "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "httpHeaders": {
+ "description": "Custom headers to set in the request. HTTP allows repeated headers.",
+ "items": {
+ "description": "HTTPHeader describes a custom header to be used in HTTP probes",
+ "properties": {
+ "name": {
+ "description": "The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.",
+ "type": "string"
+ },
+ "value": {
+ "description": "The header field value",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "Path to access on the HTTP server.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ },
+ "scheme": {
+ "description": "Scheme to use for connecting to the host. Defaults to HTTP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "tcpSocket": {
+ "description": "TCPSocketAction describes an action based on opening a socket",
+ "properties": {
+ "host": {
+ "description": "Optional: Host name to connect to, defaults to the pod IP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "livenessProbe": {
+ "description": "Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.",
+ "properties": {
+ "exec": {
+ "description": "ExecAction describes a \"run in container\" action.",
+ "properties": {
+ "command": {
+ "description": "Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "failureThreshold": {
+ "description": "Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "grpc": {
+ "properties": {
+ "port": {
+ "description": "Port number of the gRPC service. Number must be in the range 1 to 65535.",
+ "format": "int32",
+ "type": "integer"
+ },
+ "service": {
+ "description": "Service is the name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md).\n\nIf this is not specified, the default behavior is defined by gRPC.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "httpGet": {
+ "description": "HTTPGetAction describes an action based on HTTP Get requests.",
+ "properties": {
+ "host": {
+ "description": "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "httpHeaders": {
+ "description": "Custom headers to set in the request. HTTP allows repeated headers.",
+ "items": {
+ "description": "HTTPHeader describes a custom header to be used in HTTP probes",
+ "properties": {
+ "name": {
+ "description": "The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.",
+ "type": "string"
+ },
+ "value": {
+ "description": "The header field value",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "Path to access on the HTTP server.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ },
+ "scheme": {
+ "description": "Scheme to use for connecting to the host. Defaults to HTTP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "initialDelaySeconds": {
+ "description": "Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "periodSeconds": {
+ "description": "How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "successThreshold": {
+ "description": "Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "tcpSocket": {
+ "description": "TCPSocketAction describes an action based on opening a socket",
+ "properties": {
+ "host": {
+ "description": "Optional: Host name to connect to, defaults to the pod IP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "terminationGracePeriodSeconds": {
+ "description": "Optional duration in seconds the pod needs to terminate gracefully upon probe failure. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this value overrides the value provided by the pod spec. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "timeoutSeconds": {
+ "description": "Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "name": {
+ "description": "Name of the ephemeral container specified as a DNS_LABEL. This name must be unique among all containers, init containers and ephemeral containers.",
+ "type": "string"
+ },
+ "ports": {
+ "description": "Ports are not allowed for ephemeral containers.",
+ "items": {
+ "description": "ContainerPort represents a network port in a single container.",
+ "properties": {
+ "containerPort": {
+ "description": "Number of port to expose on the pod's IP address. This must be a valid port number, 0 < x < 65536.",
+ "format": "int32",
+ "type": "integer"
+ },
+ "hostIP": {
+ "description": "What host IP to bind the external port to.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "hostPort": {
+ "description": "Number of port to expose on the host. If specified, this must be a valid port number, 0 < x < 65536. If HostNetwork is specified, this must match ContainerPort. Most containers do not need this.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "name": {
+ "description": "If specified, this must be an IANA_SVC_NAME and unique within the pod. Each named port in a pod must have a unique name. Name for the port that can be referred to by services.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "protocol": {
+ "description": "Protocol for port. Must be UDP, TCP, or SCTP. Defaults to \"TCP\".",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "containerPort"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-list-map-keys": [
+ "containerPort",
+ "protocol"
+ ],
+ "x-kubernetes-list-type": "map",
+ "x-kubernetes-patch-merge-key": "containerPort",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "readinessProbe": {
+ "description": "Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.",
+ "properties": {
+ "exec": {
+ "description": "ExecAction describes a \"run in container\" action.",
+ "properties": {
+ "command": {
+ "description": "Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "failureThreshold": {
+ "description": "Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "grpc": {
+ "properties": {
+ "port": {
+ "description": "Port number of the gRPC service. Number must be in the range 1 to 65535.",
+ "format": "int32",
+ "type": "integer"
+ },
+ "service": {
+ "description": "Service is the name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md).\n\nIf this is not specified, the default behavior is defined by gRPC.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "httpGet": {
+ "description": "HTTPGetAction describes an action based on HTTP Get requests.",
+ "properties": {
+ "host": {
+ "description": "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "httpHeaders": {
+ "description": "Custom headers to set in the request. HTTP allows repeated headers.",
+ "items": {
+ "description": "HTTPHeader describes a custom header to be used in HTTP probes",
+ "properties": {
+ "name": {
+ "description": "The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.",
+ "type": "string"
+ },
+ "value": {
+ "description": "The header field value",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "Path to access on the HTTP server.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ },
+ "scheme": {
+ "description": "Scheme to use for connecting to the host. Defaults to HTTP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "initialDelaySeconds": {
+ "description": "Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "periodSeconds": {
+ "description": "How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "successThreshold": {
+ "description": "Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "tcpSocket": {
+ "description": "TCPSocketAction describes an action based on opening a socket",
+ "properties": {
+ "host": {
+ "description": "Optional: Host name to connect to, defaults to the pod IP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "terminationGracePeriodSeconds": {
+ "description": "Optional duration in seconds the pod needs to terminate gracefully upon probe failure. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this value overrides the value provided by the pod spec. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "timeoutSeconds": {
+ "description": "Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "resizePolicy": {
+ "description": "Resources resize policy for the container.",
+ "items": {
+ "description": "ContainerResizePolicy represents resource resize policy for the container.",
+ "properties": {
+ "resourceName": {
+ "description": "Name of the resource to which this resource resize policy applies. Supported values: cpu, memory.",
+ "type": "string"
+ },
+ "restartPolicy": {
+ "description": "Restart policy to apply when specified resource is resized. If not specified, it defaults to NotRequired.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "resourceName",
+ "restartPolicy"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-list-type": "atomic"
+ },
+ "resources": {
+ "description": "ResourceRequirements describes the compute resource requirements.",
+ "properties": {
+ "claims": {
+ "description": "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container.\n\nThis is an alpha field and requires enabling the DynamicResourceAllocation feature gate.\n\nThis field is immutable. It can only be set for containers.",
+ "items": {
+ "description": "ResourceClaim references one entry in PodSpec.ResourceClaims.",
+ "properties": {
+ "name": {
+ "description": "Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-list-map-keys": [
+ "name"
+ ],
+ "x-kubernetes-list-type": "map"
+ },
+ "limits": {
+ "additionalProperties": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ ]
+ },
+ "description": "Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/",
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "requests": {
+ "additionalProperties": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ ]
+ },
+ "description": "Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/",
+ "type": [
+ "object",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "restartPolicy": {
+ "description": "Restart policy for the container to manage the restart behavior of each container within a pod. This may only be set for init containers. You cannot set this field on ephemeral containers.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "securityContext": {
+ "description": "SecurityContext holds security configuration that will be applied to a container. Some fields are present in both SecurityContext and PodSecurityContext. When both are set, the values in SecurityContext take precedence.",
+ "properties": {
+ "allowPrivilegeEscalation": {
+ "description": "AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process. This bool directly controls if the no_new_privs flag will be set on the container process. AllowPrivilegeEscalation is true always when the container is: 1) run as Privileged 2) has CAP_SYS_ADMIN Note that this field cannot be set when spec.os.name is windows.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "capabilities": {
+ "description": "Adds and removes POSIX capabilities from running containers.",
+ "properties": {
+ "add": {
+ "description": "Added capabilities",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "drop": {
+ "description": "Removed capabilities",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "privileged": {
+ "description": "Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false. Note that this field cannot be set when spec.os.name is windows.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "procMount": {
+ "description": "procMount denotes the type of proc mount to use for the containers. The default is DefaultProcMount which uses the container runtime defaults for readonly paths and masked paths. This requires the ProcMountType feature flag to be enabled. Note that this field cannot be set when spec.os.name is windows.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "readOnlyRootFilesystem": {
+ "description": "Whether this container has a read-only root filesystem. Default is false. Note that this field cannot be set when spec.os.name is windows.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "runAsGroup": {
+ "description": "The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is windows.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "runAsNonRoot": {
+ "description": "Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "runAsUser": {
+ "description": "The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is windows.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "seLinuxOptions": {
+ "description": "SELinuxOptions are the labels to be applied to the container",
+ "properties": {
+ "level": {
+ "description": "Level is SELinux level label that applies to the container.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "role": {
+ "description": "Role is a SELinux role label that applies to the container.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": {
+ "description": "Type is a SELinux type label that applies to the container.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "user": {
+ "description": "User is a SELinux user label that applies to the container.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "seccompProfile": {
+ "description": "SeccompProfile defines a pod/container's seccomp profile settings. Only one profile source may be set.",
+ "properties": {
+ "localhostProfile": {
+ "description": "localhostProfile indicates a profile defined in a file on the node should be used. The profile must be preconfigured on the node to work. Must be a descending path, relative to the kubelet's configured seccomp profile location. Must be set if type is \"Localhost\". Must NOT be set for any other type.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": {
+ "description": "type indicates which kind of seccomp profile will be applied. Valid options are:\n\nLocalhost - a profile defined in a file on the node should be used. RuntimeDefault - the container runtime default profile should be used. Unconfined - no profile should be applied.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-unions": [
+ {
+ "discriminator": "type",
+ "fields-to-discriminateBy": {
+ "localhostProfile": "LocalhostProfile"
+ }
+ }
+ ],
+ "additionalProperties": false
+ },
+ "windowsOptions": {
+ "description": "WindowsSecurityContextOptions contain Windows-specific options and credentials.",
+ "properties": {
+ "gmsaCredentialSpec": {
+ "description": "GMSACredentialSpec is where the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the GMSA credential spec named by the GMSACredentialSpecName field.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "gmsaCredentialSpecName": {
+ "description": "GMSACredentialSpecName is the name of the GMSA credential spec to use.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "hostProcess": {
+ "description": "HostProcess determines if a container should be run as a 'Host Process' container. All of a Pod's containers must have the same effective HostProcess value (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). In addition, if HostProcess is true then HostNetwork must also be set to true.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "runAsUserName": {
+ "description": "The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "startupProbe": {
+ "description": "Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.",
+ "properties": {
+ "exec": {
+ "description": "ExecAction describes a \"run in container\" action.",
+ "properties": {
+ "command": {
+ "description": "Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "failureThreshold": {
+ "description": "Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "grpc": {
+ "properties": {
+ "port": {
+ "description": "Port number of the gRPC service. Number must be in the range 1 to 65535.",
+ "format": "int32",
+ "type": "integer"
+ },
+ "service": {
+ "description": "Service is the name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md).\n\nIf this is not specified, the default behavior is defined by gRPC.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "httpGet": {
+ "description": "HTTPGetAction describes an action based on HTTP Get requests.",
+ "properties": {
+ "host": {
+ "description": "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "httpHeaders": {
+ "description": "Custom headers to set in the request. HTTP allows repeated headers.",
+ "items": {
+ "description": "HTTPHeader describes a custom header to be used in HTTP probes",
+ "properties": {
+ "name": {
+ "description": "The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.",
+ "type": "string"
+ },
+ "value": {
+ "description": "The header field value",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "Path to access on the HTTP server.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ },
+ "scheme": {
+ "description": "Scheme to use for connecting to the host. Defaults to HTTP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "initialDelaySeconds": {
+ "description": "Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "periodSeconds": {
+ "description": "How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "successThreshold": {
+ "description": "Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "tcpSocket": {
+ "description": "TCPSocketAction describes an action based on opening a socket",
+ "properties": {
+ "host": {
+ "description": "Optional: Host name to connect to, defaults to the pod IP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "terminationGracePeriodSeconds": {
+ "description": "Optional duration in seconds the pod needs to terminate gracefully upon probe failure. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this value overrides the value provided by the pod spec. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "timeoutSeconds": {
+ "description": "Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "stdin": {
+ "description": "Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "stdinOnce": {
+ "description": "Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "targetContainerName": {
+ "description": "If set, the name of the container from PodSpec that this ephemeral container targets. The ephemeral container will be run in the namespaces (IPC, PID, etc) of this container. If not set then the ephemeral container uses the namespaces configured in the Pod spec.\n\nThe container runtime must implement support for this feature. If the runtime does not support namespace targeting then the result of setting this field is undefined.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "terminationMessagePath": {
+ "description": "Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "terminationMessagePolicy": {
+ "description": "Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "tty": {
+ "description": "Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "volumeDevices": {
+ "description": "volumeDevices is the list of block devices to be used by the container.",
+ "items": {
+ "description": "volumeDevice describes a mapping of a raw block device within a container.",
+ "properties": {
+ "devicePath": {
+ "description": "devicePath is the path inside of the container that the device will be mapped to.",
+ "type": "string"
+ },
+ "name": {
+ "description": "name must match the name of a persistentVolumeClaim in the pod",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "devicePath"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-merge-key": "devicePath",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "volumeMounts": {
+ "description": "Pod volumes to mount into the container's filesystem. Subpath mounts are not allowed for ephemeral containers. Cannot be updated.",
+ "items": {
+ "description": "VolumeMount describes a mounting of a Volume within a container.",
+ "properties": {
+ "mountPath": {
+ "description": "Path within the container at which the volume should be mounted. Must not contain ':'.",
+ "type": "string"
+ },
+ "mountPropagation": {
+ "description": "mountPropagation determines how mounts are propagated from the host to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "name": {
+ "description": "This must match the Name of a Volume.",
+ "type": "string"
+ },
+ "readOnly": {
+ "description": "Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "subPath": {
+ "description": "Path within the volume from which the container's volume should be mounted. Defaults to \"\" (volume's root).",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "subPathExpr": {
+ "description": "Expanded path within the volume from which the container's volume should be mounted. Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. Defaults to \"\" (volume's root). SubPathExpr and SubPath are mutually exclusive.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "name",
+ "mountPath"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-merge-key": "mountPath",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "workingDir": {
+ "description": "Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "name"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "hostAliases": {
+ "description": "HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods.",
+ "items": {
+ "description": "HostAlias holds the mapping between IP and hostnames that will be injected as an entry in the pod's hosts file.",
+ "properties": {
+ "hostnames": {
+ "description": "Hostnames for the above IP address.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "ip": {
+ "description": "IP address of the host file entry.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-merge-key": "ip",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "hostIPC": {
+ "description": "Use the host's ipc namespace. Optional: Default to false.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "hostNetwork": {
+ "description": "Host networking requested for this pod. Use the host's network namespace. If this option is set, the ports that will be used must be specified. Default to false.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "hostPID": {
+ "description": "Use the host's pid namespace. Optional: Default to false.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "hostUsers": {
+ "description": "Use the host's user namespace. Optional: Default to true. If set to true or not present, the pod will be run in the host user namespace, useful for when the pod needs a feature only available to the host user namespace, such as loading a kernel module with CAP_SYS_MODULE. When set to false, a new userns is created for the pod. Setting false is useful for mitigating container breakout vulnerabilities even allowing users to run their containers as root without actually having root privileges on the host. This field is alpha-level and is only honored by servers that enable the UserNamespacesSupport feature.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "hostname": {
+ "description": "Specifies the hostname of the Pod If not specified, the pod's hostname will be set to a system-defined value.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "imagePullSecrets": {
+ "description": "ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod",
+ "items": {
+ "description": "LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace.",
+ "properties": {
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "initContainers": {
+ "description": "List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/",
+ "items": {
+ "description": "A single application container that you want to run within a pod.",
+ "properties": {
+ "args": {
+ "description": "Arguments to the entrypoint. The container image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "command": {
+ "description": "Entrypoint array. Not executed within a shell. The container image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "env": {
+ "description": "List of environment variables to set in the container. Cannot be updated.",
+ "items": {
+ "description": "EnvVar represents an environment variable present in a Container.",
+ "properties": {
+ "name": {
+ "description": "Name of the environment variable. Must be a C_IDENTIFIER.",
+ "type": "string"
+ },
+ "value": {
+ "description": "Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to \"\".",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "valueFrom": {
+ "description": "EnvVarSource represents a source for the value of an EnvVar.",
+ "properties": {
+ "configMapKeyRef": {
+ "description": "Selects a key from a ConfigMap.",
+ "properties": {
+ "key": {
+ "description": "The key to select.",
+ "type": "string"
+ },
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "optional": {
+ "description": "Specify whether the ConfigMap or its key must be defined",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "fieldRef": {
+ "description": "ObjectFieldSelector selects an APIVersioned field of an object.",
+ "properties": {
+ "apiVersion": {
+ "description": "Version of the schema the FieldPath is written in terms of, defaults to \"v1\".",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "fieldPath": {
+ "description": "Path of the field to select in the specified API version.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "fieldPath"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "resourceFieldRef": {
+ "description": "ResourceFieldSelector represents container resources (cpu, memory) and their output format",
+ "properties": {
+ "containerName": {
+ "description": "Container name: required for volumes, optional for env vars",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "divisor": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ ]
+ },
+ "resource": {
+ "description": "Required: resource to select",
+ "type": "string"
+ }
+ },
+ "required": [
+ "resource"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "secretKeyRef": {
+ "description": "SecretKeySelector selects a key of a Secret.",
+ "properties": {
+ "key": {
+ "description": "The key of the secret to select from. Must be a valid secret key.",
+ "type": "string"
+ },
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "optional": {
+ "description": "Specify whether the Secret or its key must be defined",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "required": [
+ "name"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "envFrom": {
+ "description": "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.",
+ "items": {
+ "description": "EnvFromSource represents the source of a set of ConfigMaps",
+ "properties": {
+ "configMapRef": {
+ "description": "ConfigMapEnvSource selects a ConfigMap to populate the environment variables with.\n\nThe contents of the target ConfigMap's Data field will represent the key-value pairs as environment variables.",
+ "properties": {
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "optional": {
+ "description": "Specify whether the ConfigMap must be defined",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "prefix": {
+ "description": "An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "secretRef": {
+ "description": "SecretEnvSource selects a Secret to populate the environment variables with.\n\nThe contents of the target Secret's Data field will represent the key-value pairs as environment variables.",
+ "properties": {
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "optional": {
+ "description": "Specify whether the Secret must be defined",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "image": {
+ "description": "Container image name. More info: https://kubernetes.io/docs/concepts/containers/images This field is optional to allow higher level config management to default or override container images in workload controllers like Deployments and StatefulSets.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "imagePullPolicy": {
+ "description": "Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "lifecycle": {
+ "description": "Lifecycle describes actions that the management system should take in response to container lifecycle events. For the PostStart and PreStop lifecycle handlers, management of the container blocks until the action is complete, unless the container process fails, in which case the handler is aborted.",
+ "properties": {
+ "postStart": {
+ "description": "LifecycleHandler defines a specific action that should be taken in a lifecycle hook. One and only one of the fields, except TCPSocket must be specified.",
+ "properties": {
+ "exec": {
+ "description": "ExecAction describes a \"run in container\" action.",
+ "properties": {
+ "command": {
+ "description": "Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "httpGet": {
+ "description": "HTTPGetAction describes an action based on HTTP Get requests.",
+ "properties": {
+ "host": {
+ "description": "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "httpHeaders": {
+ "description": "Custom headers to set in the request. HTTP allows repeated headers.",
+ "items": {
+ "description": "HTTPHeader describes a custom header to be used in HTTP probes",
+ "properties": {
+ "name": {
+ "description": "The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.",
+ "type": "string"
+ },
+ "value": {
+ "description": "The header field value",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "Path to access on the HTTP server.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ },
+ "scheme": {
+ "description": "Scheme to use for connecting to the host. Defaults to HTTP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "tcpSocket": {
+ "description": "TCPSocketAction describes an action based on opening a socket",
+ "properties": {
+ "host": {
+ "description": "Optional: Host name to connect to, defaults to the pod IP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "preStop": {
+ "description": "LifecycleHandler defines a specific action that should be taken in a lifecycle hook. One and only one of the fields, except TCPSocket must be specified.",
+ "properties": {
+ "exec": {
+ "description": "ExecAction describes a \"run in container\" action.",
+ "properties": {
+ "command": {
+ "description": "Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "httpGet": {
+ "description": "HTTPGetAction describes an action based on HTTP Get requests.",
+ "properties": {
+ "host": {
+ "description": "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "httpHeaders": {
+ "description": "Custom headers to set in the request. HTTP allows repeated headers.",
+ "items": {
+ "description": "HTTPHeader describes a custom header to be used in HTTP probes",
+ "properties": {
+ "name": {
+ "description": "The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.",
+ "type": "string"
+ },
+ "value": {
+ "description": "The header field value",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "Path to access on the HTTP server.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ },
+ "scheme": {
+ "description": "Scheme to use for connecting to the host. Defaults to HTTP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "tcpSocket": {
+ "description": "TCPSocketAction describes an action based on opening a socket",
+ "properties": {
+ "host": {
+ "description": "Optional: Host name to connect to, defaults to the pod IP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "livenessProbe": {
+ "description": "Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.",
+ "properties": {
+ "exec": {
+ "description": "ExecAction describes a \"run in container\" action.",
+ "properties": {
+ "command": {
+ "description": "Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "failureThreshold": {
+ "description": "Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "grpc": {
+ "properties": {
+ "port": {
+ "description": "Port number of the gRPC service. Number must be in the range 1 to 65535.",
+ "format": "int32",
+ "type": "integer"
+ },
+ "service": {
+ "description": "Service is the name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md).\n\nIf this is not specified, the default behavior is defined by gRPC.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "httpGet": {
+ "description": "HTTPGetAction describes an action based on HTTP Get requests.",
+ "properties": {
+ "host": {
+ "description": "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "httpHeaders": {
+ "description": "Custom headers to set in the request. HTTP allows repeated headers.",
+ "items": {
+ "description": "HTTPHeader describes a custom header to be used in HTTP probes",
+ "properties": {
+ "name": {
+ "description": "The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.",
+ "type": "string"
+ },
+ "value": {
+ "description": "The header field value",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "Path to access on the HTTP server.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ },
+ "scheme": {
+ "description": "Scheme to use for connecting to the host. Defaults to HTTP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "initialDelaySeconds": {
+ "description": "Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "periodSeconds": {
+ "description": "How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "successThreshold": {
+ "description": "Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "tcpSocket": {
+ "description": "TCPSocketAction describes an action based on opening a socket",
+ "properties": {
+ "host": {
+ "description": "Optional: Host name to connect to, defaults to the pod IP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "terminationGracePeriodSeconds": {
+ "description": "Optional duration in seconds the pod needs to terminate gracefully upon probe failure. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this value overrides the value provided by the pod spec. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "timeoutSeconds": {
+ "description": "Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "name": {
+ "description": "Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated.",
+ "type": "string"
+ },
+ "ports": {
+ "description": "List of ports to expose from the container. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default \"0.0.0.0\" address inside a container will be accessible from the network. Modifying this array with strategic merge patch may corrupt the data. For more information See https://github.com/kubernetes/kubernetes/issues/108255. Cannot be updated.",
+ "items": {
+ "description": "ContainerPort represents a network port in a single container.",
+ "properties": {
+ "containerPort": {
+ "description": "Number of port to expose on the pod's IP address. This must be a valid port number, 0 < x < 65536.",
+ "format": "int32",
+ "type": "integer"
+ },
+ "hostIP": {
+ "description": "What host IP to bind the external port to.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "hostPort": {
+ "description": "Number of port to expose on the host. If specified, this must be a valid port number, 0 < x < 65536. If HostNetwork is specified, this must match ContainerPort. Most containers do not need this.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "name": {
+ "description": "If specified, this must be an IANA_SVC_NAME and unique within the pod. Each named port in a pod must have a unique name. Name for the port that can be referred to by services.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "protocol": {
+ "description": "Protocol for port. Must be UDP, TCP, or SCTP. Defaults to \"TCP\".",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "containerPort"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-list-map-keys": [
+ "containerPort",
+ "protocol"
+ ],
+ "x-kubernetes-list-type": "map",
+ "x-kubernetes-patch-merge-key": "containerPort",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "readinessProbe": {
+ "description": "Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.",
+ "properties": {
+ "exec": {
+ "description": "ExecAction describes a \"run in container\" action.",
+ "properties": {
+ "command": {
+ "description": "Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "failureThreshold": {
+ "description": "Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "grpc": {
+ "properties": {
+ "port": {
+ "description": "Port number of the gRPC service. Number must be in the range 1 to 65535.",
+ "format": "int32",
+ "type": "integer"
+ },
+ "service": {
+ "description": "Service is the name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md).\n\nIf this is not specified, the default behavior is defined by gRPC.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "httpGet": {
+ "description": "HTTPGetAction describes an action based on HTTP Get requests.",
+ "properties": {
+ "host": {
+ "description": "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "httpHeaders": {
+ "description": "Custom headers to set in the request. HTTP allows repeated headers.",
+ "items": {
+ "description": "HTTPHeader describes a custom header to be used in HTTP probes",
+ "properties": {
+ "name": {
+ "description": "The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.",
+ "type": "string"
+ },
+ "value": {
+ "description": "The header field value",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "Path to access on the HTTP server.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ },
+ "scheme": {
+ "description": "Scheme to use for connecting to the host. Defaults to HTTP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "initialDelaySeconds": {
+ "description": "Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "periodSeconds": {
+ "description": "How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "successThreshold": {
+ "description": "Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "tcpSocket": {
+ "description": "TCPSocketAction describes an action based on opening a socket",
+ "properties": {
+ "host": {
+ "description": "Optional: Host name to connect to, defaults to the pod IP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "terminationGracePeriodSeconds": {
+ "description": "Optional duration in seconds the pod needs to terminate gracefully upon probe failure. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this value overrides the value provided by the pod spec. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "timeoutSeconds": {
+ "description": "Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "resizePolicy": {
+ "description": "Resources resize policy for the container.",
+ "items": {
+ "description": "ContainerResizePolicy represents resource resize policy for the container.",
+ "properties": {
+ "resourceName": {
+ "description": "Name of the resource to which this resource resize policy applies. Supported values: cpu, memory.",
+ "type": "string"
+ },
+ "restartPolicy": {
+ "description": "Restart policy to apply when specified resource is resized. If not specified, it defaults to NotRequired.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "resourceName",
+ "restartPolicy"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-list-type": "atomic"
+ },
+ "resources": {
+ "description": "ResourceRequirements describes the compute resource requirements.",
+ "properties": {
+ "claims": {
+ "description": "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container.\n\nThis is an alpha field and requires enabling the DynamicResourceAllocation feature gate.\n\nThis field is immutable. It can only be set for containers.",
+ "items": {
+ "description": "ResourceClaim references one entry in PodSpec.ResourceClaims.",
+ "properties": {
+ "name": {
+ "description": "Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-list-map-keys": [
+ "name"
+ ],
+ "x-kubernetes-list-type": "map"
+ },
+ "limits": {
+ "additionalProperties": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ ]
+ },
+ "description": "Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/",
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "requests": {
+ "additionalProperties": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ ]
+ },
+ "description": "Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/",
+ "type": [
+ "object",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "restartPolicy": {
+ "description": "RestartPolicy defines the restart behavior of individual containers in a pod. This field may only be set for init containers, and the only allowed value is \"Always\". For non-init containers or when this field is not specified, the restart behavior is defined by the Pod's restart policy and the container type. Setting the RestartPolicy as \"Always\" for the init container will have the following effect: this init container will be continually restarted on exit until all regular containers have terminated. Once all regular containers have completed, all init containers with restartPolicy \"Always\" will be shut down. This lifecycle differs from normal init containers and is often referred to as a \"sidecar\" container. Although this init container still starts in the init container sequence, it does not wait for the container to complete before proceeding to the next init container. Instead, the next init container starts immediately after this init container is started, or after any startupProbe has successfully completed.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "securityContext": {
+ "description": "SecurityContext holds security configuration that will be applied to a container. Some fields are present in both SecurityContext and PodSecurityContext. When both are set, the values in SecurityContext take precedence.",
+ "properties": {
+ "allowPrivilegeEscalation": {
+ "description": "AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process. This bool directly controls if the no_new_privs flag will be set on the container process. AllowPrivilegeEscalation is true always when the container is: 1) run as Privileged 2) has CAP_SYS_ADMIN Note that this field cannot be set when spec.os.name is windows.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "capabilities": {
+ "description": "Adds and removes POSIX capabilities from running containers.",
+ "properties": {
+ "add": {
+ "description": "Added capabilities",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "drop": {
+ "description": "Removed capabilities",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "privileged": {
+ "description": "Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false. Note that this field cannot be set when spec.os.name is windows.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "procMount": {
+ "description": "procMount denotes the type of proc mount to use for the containers. The default is DefaultProcMount which uses the container runtime defaults for readonly paths and masked paths. This requires the ProcMountType feature flag to be enabled. Note that this field cannot be set when spec.os.name is windows.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "readOnlyRootFilesystem": {
+ "description": "Whether this container has a read-only root filesystem. Default is false. Note that this field cannot be set when spec.os.name is windows.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "runAsGroup": {
+ "description": "The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is windows.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "runAsNonRoot": {
+ "description": "Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "runAsUser": {
+ "description": "The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is windows.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "seLinuxOptions": {
+ "description": "SELinuxOptions are the labels to be applied to the container",
+ "properties": {
+ "level": {
+ "description": "Level is SELinux level label that applies to the container.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "role": {
+ "description": "Role is a SELinux role label that applies to the container.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": {
+ "description": "Type is a SELinux type label that applies to the container.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "user": {
+ "description": "User is a SELinux user label that applies to the container.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "seccompProfile": {
+ "description": "SeccompProfile defines a pod/container's seccomp profile settings. Only one profile source may be set.",
+ "properties": {
+ "localhostProfile": {
+ "description": "localhostProfile indicates a profile defined in a file on the node should be used. The profile must be preconfigured on the node to work. Must be a descending path, relative to the kubelet's configured seccomp profile location. Must be set if type is \"Localhost\". Must NOT be set for any other type.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": {
+ "description": "type indicates which kind of seccomp profile will be applied. Valid options are:\n\nLocalhost - a profile defined in a file on the node should be used. RuntimeDefault - the container runtime default profile should be used. Unconfined - no profile should be applied.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-unions": [
+ {
+ "discriminator": "type",
+ "fields-to-discriminateBy": {
+ "localhostProfile": "LocalhostProfile"
+ }
+ }
+ ],
+ "additionalProperties": false
+ },
+ "windowsOptions": {
+ "description": "WindowsSecurityContextOptions contain Windows-specific options and credentials.",
+ "properties": {
+ "gmsaCredentialSpec": {
+ "description": "GMSACredentialSpec is where the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the GMSA credential spec named by the GMSACredentialSpecName field.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "gmsaCredentialSpecName": {
+ "description": "GMSACredentialSpecName is the name of the GMSA credential spec to use.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "hostProcess": {
+ "description": "HostProcess determines if a container should be run as a 'Host Process' container. All of a Pod's containers must have the same effective HostProcess value (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). In addition, if HostProcess is true then HostNetwork must also be set to true.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "runAsUserName": {
+ "description": "The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "startupProbe": {
+ "description": "Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.",
+ "properties": {
+ "exec": {
+ "description": "ExecAction describes a \"run in container\" action.",
+ "properties": {
+ "command": {
+ "description": "Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "failureThreshold": {
+ "description": "Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "grpc": {
+ "properties": {
+ "port": {
+ "description": "Port number of the gRPC service. Number must be in the range 1 to 65535.",
+ "format": "int32",
+ "type": "integer"
+ },
+ "service": {
+ "description": "Service is the name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md).\n\nIf this is not specified, the default behavior is defined by gRPC.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "httpGet": {
+ "description": "HTTPGetAction describes an action based on HTTP Get requests.",
+ "properties": {
+ "host": {
+ "description": "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "httpHeaders": {
+ "description": "Custom headers to set in the request. HTTP allows repeated headers.",
+ "items": {
+ "description": "HTTPHeader describes a custom header to be used in HTTP probes",
+ "properties": {
+ "name": {
+ "description": "The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.",
+ "type": "string"
+ },
+ "value": {
+ "description": "The header field value",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "Path to access on the HTTP server.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ },
+ "scheme": {
+ "description": "Scheme to use for connecting to the host. Defaults to HTTP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "initialDelaySeconds": {
+ "description": "Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "periodSeconds": {
+ "description": "How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "successThreshold": {
+ "description": "Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "tcpSocket": {
+ "description": "TCPSocketAction describes an action based on opening a socket",
+ "properties": {
+ "host": {
+ "description": "Optional: Host name to connect to, defaults to the pod IP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "terminationGracePeriodSeconds": {
+ "description": "Optional duration in seconds the pod needs to terminate gracefully upon probe failure. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this value overrides the value provided by the pod spec. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "timeoutSeconds": {
+ "description": "Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "stdin": {
+ "description": "Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "stdinOnce": {
+ "description": "Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "terminationMessagePath": {
+ "description": "Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "terminationMessagePolicy": {
+ "description": "Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "tty": {
+ "description": "Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "volumeDevices": {
+ "description": "volumeDevices is the list of block devices to be used by the container.",
+ "items": {
+ "description": "volumeDevice describes a mapping of a raw block device within a container.",
+ "properties": {
+ "devicePath": {
+ "description": "devicePath is the path inside of the container that the device will be mapped to.",
+ "type": "string"
+ },
+ "name": {
+ "description": "name must match the name of a persistentVolumeClaim in the pod",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "devicePath"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-merge-key": "devicePath",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "volumeMounts": {
+ "description": "Pod volumes to mount into the container's filesystem. Cannot be updated.",
+ "items": {
+ "description": "VolumeMount describes a mounting of a Volume within a container.",
+ "properties": {
+ "mountPath": {
+ "description": "Path within the container at which the volume should be mounted. Must not contain ':'.",
+ "type": "string"
+ },
+ "mountPropagation": {
+ "description": "mountPropagation determines how mounts are propagated from the host to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "name": {
+ "description": "This must match the Name of a Volume.",
+ "type": "string"
+ },
+ "readOnly": {
+ "description": "Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "subPath": {
+ "description": "Path within the volume from which the container's volume should be mounted. Defaults to \"\" (volume's root).",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "subPathExpr": {
+ "description": "Expanded path within the volume from which the container's volume should be mounted. Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. Defaults to \"\" (volume's root). SubPathExpr and SubPath are mutually exclusive.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "name",
+ "mountPath"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-merge-key": "mountPath",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "workingDir": {
+ "description": "Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "name"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "nodeName": {
+ "description": "NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "nodeSelector": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "NodeSelector is a selector which must be true for the pod to fit on a node. Selector which must match a node's labels for the pod to be scheduled on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/",
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic"
+ },
+ "os": {
+ "description": "PodOS defines the OS parameters of a pod.",
+ "properties": {
+ "name": {
+ "description": "Name is the name of the operating system. The currently supported values are linux and windows. Additional value may be defined in future and can be one of: https://github.com/opencontainers/runtime-spec/blob/master/config.md#platform-specific-configuration Clients should expect to handle additional values and treat unrecognized values in this field as os: null",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "overhead": {
+ "additionalProperties": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ ]
+ },
+ "description": "Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md",
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "preemptionPolicy": {
+ "description": "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "priority": {
+ "description": "The priority value. Various system components use this field to find the priority of the pod. When Priority Admission Controller is enabled, it prevents users from setting this field. The admission controller populates this field from PriorityClassName. The higher the value, the higher the priority.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "priorityClassName": {
+ "description": "If specified, indicates the pod's priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "readinessGates": {
+ "description": "If specified, all readiness gates will be evaluated for pod readiness. A pod is ready when all its containers are ready AND all conditions specified in the readiness gates have status equal to \"True\" More info: https://git.k8s.io/enhancements/keps/sig-network/580-pod-readiness-gates",
+ "items": {
+ "description": "PodReadinessGate contains the reference to a pod condition",
+ "properties": {
+ "conditionType": {
+ "description": "ConditionType refers to a condition in the pod's condition list with matching type.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "conditionType"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "resourceClaims": {
+ "description": "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.\n\nThis is an alpha field and requires enabling the DynamicResourceAllocation feature gate.\n\nThis field is immutable.",
+ "items": {
+ "description": "PodResourceClaim references exactly one ResourceClaim through a ClaimSource. It adds a name to it that uniquely identifies the ResourceClaim inside the Pod. Containers that need access to the ResourceClaim reference it with this name.",
+ "properties": {
+ "name": {
+ "description": "Name uniquely identifies this resource claim inside the pod. This must be a DNS_LABEL.",
+ "type": "string"
+ },
+ "source": {
+ "description": "ClaimSource describes a reference to a ResourceClaim.\n\nExactly one of these fields should be set. Consumers of this type must treat an empty object as if it has an unknown value.",
+ "properties": {
+ "resourceClaimName": {
+ "description": "ResourceClaimName is the name of a ResourceClaim object in the same namespace as this pod.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "resourceClaimTemplateName": {
+ "description": "ResourceClaimTemplateName is the name of a ResourceClaimTemplate object in the same namespace as this pod.\n\nThe template will be used to create a new ResourceClaim, which will be bound to this pod. When this pod is deleted, the ResourceClaim will also be deleted. The pod name and resource name, along with a generated component, will be used to form a unique name for the ResourceClaim, which will be recorded in pod.status.resourceClaimStatuses.\n\nThis field is immutable and no changes will be made to the corresponding ResourceClaim by the control plane after creating the ResourceClaim.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "required": [
+ "name"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-list-map-keys": [
+ "name"
+ ],
+ "x-kubernetes-list-type": "map",
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge,retainKeys"
+ },
+ "restartPolicy": {
+ "description": "Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "runtimeClassName": {
+ "description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "schedulerName": {
+ "description": "If specified, the pod will be dispatched by specified scheduler. If not specified, the pod will be dispatched by default scheduler.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "schedulingGates": {
+ "description": "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.\n\nThis is a beta feature enabled by the PodSchedulingReadiness feature gate.",
+ "items": {
+ "description": "PodSchedulingGate is associated to a Pod to guard its scheduling.",
+ "properties": {
+ "name": {
+ "description": "Name of the scheduling gate. Each scheduling gate must have a unique name field.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-list-map-keys": [
+ "name"
+ ],
+ "x-kubernetes-list-type": "map",
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "securityContext": {
+ "description": "PodSecurityContext holds pod-level security attributes and common container settings. Some fields are also present in container.securityContext. Field values of container.securityContext take precedence over field values of PodSecurityContext.",
+ "properties": {
+ "fsGroup": {
+ "description": "A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to change the ownership of that volume to be owned by the pod:\n\n1. The owning GID will be the FSGroup 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) 3. The permission bits are OR'd with rw-rw----\n\nIf unset, the Kubelet will not modify the ownership and permissions of any volume. Note that this field cannot be set when spec.os.name is windows.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "fsGroupChangePolicy": {
+ "description": "fsGroupChangePolicy defines behavior of changing ownership and permission of the volume before being exposed inside Pod. This field will only apply to volume types which support fsGroup based ownership(and permissions). It will have no effect on ephemeral volume types such as: secret, configmaps and emptydir. Valid values are \"OnRootMismatch\" and \"Always\". If not specified, \"Always\" is used. Note that this field cannot be set when spec.os.name is windows.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "runAsGroup": {
+ "description": "The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container. Note that this field cannot be set when spec.os.name is windows.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "runAsNonRoot": {
+ "description": "Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "runAsUser": {
+ "description": "The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container. Note that this field cannot be set when spec.os.name is windows.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "seLinuxOptions": {
+ "description": "SELinuxOptions are the labels to be applied to the container",
+ "properties": {
+ "level": {
+ "description": "Level is SELinux level label that applies to the container.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "role": {
+ "description": "Role is a SELinux role label that applies to the container.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": {
+ "description": "Type is a SELinux type label that applies to the container.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "user": {
+ "description": "User is a SELinux user label that applies to the container.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "seccompProfile": {
+ "description": "SeccompProfile defines a pod/container's seccomp profile settings. Only one profile source may be set.",
+ "properties": {
+ "localhostProfile": {
+ "description": "localhostProfile indicates a profile defined in a file on the node should be used. The profile must be preconfigured on the node to work. Must be a descending path, relative to the kubelet's configured seccomp profile location. Must be set if type is \"Localhost\". Must NOT be set for any other type.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": {
+ "description": "type indicates which kind of seccomp profile will be applied. Valid options are:\n\nLocalhost - a profile defined in a file on the node should be used. RuntimeDefault - the container runtime default profile should be used. Unconfined - no profile should be applied.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-unions": [
+ {
+ "discriminator": "type",
+ "fields-to-discriminateBy": {
+ "localhostProfile": "LocalhostProfile"
+ }
+ }
+ ],
+ "additionalProperties": false
+ },
+ "supplementalGroups": {
+ "description": "A list of groups applied to the first process run in each container, in addition to the container's primary GID, the fsGroup (if specified), and group memberships defined in the container image for the uid of the container process. If unspecified, no additional groups are added to any container. Note that group memberships defined in the container image for the uid of the container process are still effective, even if they are not included in this list. Note that this field cannot be set when spec.os.name is windows.",
+ "items": {
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "sysctls": {
+ "description": "Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported sysctls (by the container runtime) might fail to launch. Note that this field cannot be set when spec.os.name is windows.",
+ "items": {
+ "description": "Sysctl defines a kernel parameter to be set",
+ "properties": {
+ "name": {
+ "description": "Name of a property to set",
+ "type": "string"
+ },
+ "value": {
+ "description": "Value of a property to set",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "windowsOptions": {
+ "description": "WindowsSecurityContextOptions contain Windows-specific options and credentials.",
+ "properties": {
+ "gmsaCredentialSpec": {
+ "description": "GMSACredentialSpec is where the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the GMSA credential spec named by the GMSACredentialSpecName field.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "gmsaCredentialSpecName": {
+ "description": "GMSACredentialSpecName is the name of the GMSA credential spec to use.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "hostProcess": {
+ "description": "HostProcess determines if a container should be run as a 'Host Process' container. All of a Pod's containers must have the same effective HostProcess value (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). In addition, if HostProcess is true then HostNetwork must also be set to true.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "runAsUserName": {
+ "description": "The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "serviceAccount": {
+ "description": "DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "serviceAccountName": {
+ "description": "ServiceAccountName is the name of the ServiceAccount to use to run this pod. More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "setHostnameAsFQDN": {
+ "description": "If true the pod's hostname will be configured as the pod's FQDN, rather than the leaf name (the default). In Linux containers, this means setting the FQDN in the hostname field of the kernel (the nodename field of struct utsname). In Windows containers, this means setting the registry value of hostname for the registry key HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters to FQDN. If a pod does not have FQDN, this has no effect. Default to false.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "shareProcessNamespace": {
+ "description": "Share a single process namespace between all of the containers in a pod. When this is set containers will be able to view and signal processes from other containers in the same pod, and the first process in each container will not be assigned PID 1. HostPID and ShareProcessNamespace cannot both be set. Optional: Default to false.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "subdomain": {
+ "description": "If specified, the fully qualified Pod hostname will be \"...svc.\". If not specified, the pod will not have a domainname at all.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "terminationGracePeriodSeconds": {
+ "description": "Optional duration in seconds the pod needs to terminate gracefully. May be decreased in delete request. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). If this value is nil, the default grace period will be used instead. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. Defaults to 30 seconds.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "tolerations": {
+ "description": "If specified, the pod's tolerations.",
+ "items": {
+ "description": "The pod this Toleration is attached to tolerates any taint that matches the triple using the matching operator .",
+ "properties": {
+ "effect": {
+ "description": "Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "key": {
+ "description": "Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "operator": {
+ "description": "Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "tolerationSeconds": {
+ "description": "TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "value": {
+ "description": "Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "topologySpreadConstraints": {
+ "description": "TopologySpreadConstraints describes how a group of pods ought to spread across topology domains. Scheduler will schedule pods in a way which abides by the constraints. All topologySpreadConstraints are ANDed.",
+ "items": {
+ "description": "TopologySpreadConstraint specifies how to spread matching pods among the given topology.",
+ "properties": {
+ "labelSelector": {
+ "description": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.",
+ "properties": {
+ "matchExpressions": {
+ "description": "matchExpressions is a list of label selector requirements. The requirements are ANDed.",
+ "items": {
+ "description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
+ "properties": {
+ "key": {
+ "description": "key is the label key that the selector applies to.",
+ "type": "string"
+ },
+ "operator": {
+ "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.",
+ "type": "string"
+ },
+ "values": {
+ "description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key",
+ "operator"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "matchLabels": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.",
+ "type": [
+ "object",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "matchLabelKeys": {
+ "description": "MatchLabelKeys is a set of pod label keys to select the pods over which spreading will be calculated. The keys are used to lookup values from the incoming pod labels, those key-value labels are ANDed with labelSelector to select the group of existing pods over which spreading will be calculated for the incoming pod. The same key is forbidden to exist in both MatchLabelKeys and LabelSelector. MatchLabelKeys cannot be set when LabelSelector isn't set. Keys that don't exist in the incoming pod labels will be ignored. A null or empty list means only match against labelSelector.\n\nThis is a beta field and requires the MatchLabelKeysInPodTopologySpread feature gate to be enabled (enabled by default).",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-list-type": "atomic"
+ },
+ "maxSkew": {
+ "description": "MaxSkew describes the degree to which pods may be unevenly distributed. When `whenUnsatisfiable=DoNotSchedule`, it is the maximum permitted difference between the number of matching pods in the target topology and the global minimum. The global minimum is the minimum number of matching pods in an eligible domain or zero if the number of eligible domains is less than MinDomains. For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same labelSelector spread as 2/2/1: In this case, the global minimum is 1. | zone1 | zone2 | zone3 | | P P | P P | P | - if MaxSkew is 1, incoming pod can only be scheduled to zone3 to become 2/2/2; scheduling it onto zone1(zone2) would make the ActualSkew(3-1) on zone1(zone2) violate MaxSkew(1). - if MaxSkew is 2, incoming pod can be scheduled onto any zone. When `whenUnsatisfiable=ScheduleAnyway`, it is used to give higher precedence to topologies that satisfy it. It's a required field. Default value is 1 and 0 is not allowed.",
+ "format": "int32",
+ "type": "integer"
+ },
+ "minDomains": {
+ "description": "MinDomains indicates a minimum number of eligible domains. When the number of eligible domains with matching topology keys is less than minDomains, Pod Topology Spread treats \"global minimum\" as 0, and then the calculation of Skew is performed. And when the number of eligible domains with matching topology keys equals or greater than minDomains, this value has no effect on scheduling. As a result, when the number of eligible domains is less than minDomains, scheduler won't schedule more than maxSkew Pods to those domains. If value is nil, the constraint behaves as if MinDomains is equal to 1. Valid values are integers greater than 0. When value is not nil, WhenUnsatisfiable must be DoNotSchedule.\n\nFor example, in a 3-zone cluster, MaxSkew is set to 2, MinDomains is set to 5 and pods with the same labelSelector spread as 2/2/2: | zone1 | zone2 | zone3 | | P P | P P | P P | The number of domains is less than 5(MinDomains), so \"global minimum\" is treated as 0. In this situation, new pod with the same labelSelector cannot be scheduled, because computed skew will be 3(3 - 0) if new Pod is scheduled to any of the three zones, it will violate MaxSkew.\n\nThis is a beta field and requires the MinDomainsInPodTopologySpread feature gate to be enabled (enabled by default).",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "nodeAffinityPolicy": {
+ "description": "NodeAffinityPolicy indicates how we will treat Pod's nodeAffinity/nodeSelector when calculating pod topology spread skew. Options are: - Honor: only nodes matching nodeAffinity/nodeSelector are included in the calculations. - Ignore: nodeAffinity/nodeSelector are ignored. All nodes are included in the calculations.\n\nIf this value is nil, the behavior is equivalent to the Honor policy. This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "nodeTaintsPolicy": {
+ "description": "NodeTaintsPolicy indicates how we will treat node taints when calculating pod topology spread skew. Options are: - Honor: nodes without taints, along with tainted nodes for which the incoming pod has a toleration, are included. - Ignore: node taints are ignored. All nodes are included.\n\nIf this value is nil, the behavior is equivalent to the Ignore policy. This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "topologyKey": {
+ "description": "TopologyKey is the key of node labels. Nodes that have a label with this key and identical values are considered to be in the same topology. We consider each as a \"bucket\", and try to put balanced number of pods into each bucket. We define a domain as a particular instance of a topology. Also, we define an eligible domain as a domain whose nodes meet the requirements of nodeAffinityPolicy and nodeTaintsPolicy. e.g. If TopologyKey is \"kubernetes.io/hostname\", each Node is a domain of that topology. And, if TopologyKey is \"topology.kubernetes.io/zone\", each zone is a domain of that topology. It's a required field.",
+ "type": "string"
+ },
+ "whenUnsatisfiable": {
+ "description": "WhenUnsatisfiable indicates how to deal with a pod if it doesn't satisfy the spread constraint. - DoNotSchedule (default) tells the scheduler not to schedule it. - ScheduleAnyway tells the scheduler to schedule the pod in any location,\n but giving higher precedence to topologies that would help reduce the\n skew.\nA constraint is considered \"Unsatisfiable\" for an incoming pod if and only if every possible node assignment for that pod would violate \"MaxSkew\" on some topology. For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same labelSelector spread as 3/1/1: | zone1 | zone2 | zone3 | | P P P | P | P | If WhenUnsatisfiable is set to DoNotSchedule, incoming pod can only be scheduled to zone2(zone3) to become 3/2/1(3/1/2) as ActualSkew(2-1) on zone2(zone3) satisfies MaxSkew(1). In other words, the cluster can still be imbalanced, but scheduler won't make it *more* imbalanced. It's a required field.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "maxSkew",
+ "topologyKey",
+ "whenUnsatisfiable"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-list-map-keys": [
+ "topologyKey",
+ "whenUnsatisfiable"
+ ],
+ "x-kubernetes-list-type": "map",
+ "x-kubernetes-patch-merge-key": "topologyKey",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "volumes": {
+ "description": "List of volumes that can be mounted by containers belonging to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes",
+ "items": {
+ "description": "Volume represents a named volume in a pod that may be accessed by any container in the pod.",
+ "properties": {
+ "awsElasticBlockStore": {
+ "description": "Represents a Persistent Disk resource in AWS.\n\nAn AWS EBS disk must exist before mounting to a container. The disk must also be in the same AWS zone as the kubelet. An AWS EBS disk can only be mounted as read/write once. AWS EBS volumes support ownership management and SELinux relabeling.",
+ "properties": {
+ "fsType": {
+ "description": "fsType is the filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "partition": {
+ "description": "partition is the partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify the partition as \"1\". Similarly, the volume partition for /dev/sda is \"0\" (or you can leave the property empty).",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "readOnly": {
+ "description": "readOnly value true will force the readOnly setting in VolumeMounts. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "volumeID": {
+ "description": "volumeID is unique ID of the persistent disk resource in AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore",
+ "type": "string"
+ }
+ },
+ "required": [
+ "volumeID"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "azureDisk": {
+ "description": "AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.",
+ "properties": {
+ "cachingMode": {
+ "description": "cachingMode is the Host Caching mode: None, Read Only, Read Write.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "diskName": {
+ "description": "diskName is the Name of the data disk in the blob storage",
+ "type": "string"
+ },
+ "diskURI": {
+ "description": "diskURI is the URI of data disk in the blob storage",
+ "type": "string"
+ },
+ "fsType": {
+ "description": "fsType is Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "kind": {
+ "description": "kind expected values are Shared: multiple blob disks per storage account Dedicated: single blob disk per storage account Managed: azure managed data disk (only in managed availability set). defaults to shared",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "readOnly": {
+ "description": "readOnly Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "diskName",
+ "diskURI"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "azureFile": {
+ "description": "AzureFile represents an Azure File Service mount on the host and bind mount to the pod.",
+ "properties": {
+ "readOnly": {
+ "description": "readOnly defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "secretName": {
+ "description": "secretName is the name of secret that contains Azure Storage Account Name and Key",
+ "type": "string"
+ },
+ "shareName": {
+ "description": "shareName is the azure share Name",
+ "type": "string"
+ }
+ },
+ "required": [
+ "secretName",
+ "shareName"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "cephfs": {
+ "description": "Represents a Ceph Filesystem mount that lasts the lifetime of a pod Cephfs volumes do not support ownership management or SELinux relabeling.",
+ "properties": {
+ "monitors": {
+ "description": "monitors is Required: Monitors is a collection of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": "array"
+ },
+ "path": {
+ "description": "path is Optional: Used as the mounted root, rather than the full Ceph tree, default is /",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "readOnly": {
+ "description": "readOnly is Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "secretFile": {
+ "description": "secretFile is Optional: SecretFile is the path to key ring for User, default is /etc/ceph/user.secret More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "secretRef": {
+ "description": "LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace.",
+ "properties": {
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "user": {
+ "description": "user is optional: User is the rados user name, default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "monitors"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "cinder": {
+ "description": "Represents a cinder volume resource in Openstack. A Cinder volume must exist before mounting to a container. The volume must also be in the same region as the kubelet. Cinder volumes support ownership management and SELinux relabeling.",
+ "properties": {
+ "fsType": {
+ "description": "fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "readOnly": {
+ "description": "readOnly defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/mysql-cinder-pd/README.md",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "secretRef": {
+ "description": "LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace.",
+ "properties": {
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "volumeID": {
+ "description": "volumeID used to identify the volume in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md",
+ "type": "string"
+ }
+ },
+ "required": [
+ "volumeID"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "configMap": {
+ "description": "Adapts a ConfigMap into a volume.\n\nThe contents of the target ConfigMap's Data field will be presented in a volume as files using the keys in the Data field as the file names, unless the items element is populated with specific mappings of keys to paths. ConfigMap volumes support ownership management and SELinux relabeling.",
+ "properties": {
+ "defaultMode": {
+ "description": "defaultMode is optional: mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "items": {
+ "description": "items if unspecified, each key-value pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the ConfigMap, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.",
+ "items": {
+ "description": "Maps a string key to a path within a volume.",
+ "properties": {
+ "key": {
+ "description": "key is the key to project.",
+ "type": "string"
+ },
+ "mode": {
+ "description": "mode is Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "path is the relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "key",
+ "path"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "optional": {
+ "description": "optional specify whether the ConfigMap or its keys must be defined",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "csi": {
+ "description": "Represents a source location of a volume to mount, managed by an external CSI driver",
+ "properties": {
+ "driver": {
+ "description": "driver is the name of the CSI driver that handles this volume. Consult with your admin for the correct name as registered in the cluster.",
+ "type": "string"
+ },
+ "fsType": {
+ "description": "fsType to mount. Ex. \"ext4\", \"xfs\", \"ntfs\". If not provided, the empty value is passed to the associated CSI driver which will determine the default filesystem to apply.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "nodePublishSecretRef": {
+ "description": "LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace.",
+ "properties": {
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "readOnly": {
+ "description": "readOnly specifies a read-only configuration for the volume. Defaults to false (read/write).",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "volumeAttributes": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "volumeAttributes stores driver-specific properties that are passed to the CSI driver. Consult your driver's documentation for supported values.",
+ "type": [
+ "object",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "driver"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "downwardAPI": {
+ "description": "DownwardAPIVolumeSource represents a volume containing downward API info. Downward API volumes support ownership management and SELinux relabeling.",
+ "properties": {
+ "defaultMode": {
+ "description": "Optional: mode bits to use on created files by default. Must be a Optional: mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "items": {
+ "description": "Items is a list of downward API volume file",
+ "items": {
+ "description": "DownwardAPIVolumeFile represents information to create the file containing the pod field",
+ "properties": {
+ "fieldRef": {
+ "description": "ObjectFieldSelector selects an APIVersioned field of an object.",
+ "properties": {
+ "apiVersion": {
+ "description": "Version of the schema the FieldPath is written in terms of, defaults to \"v1\".",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "fieldPath": {
+ "description": "Path of the field to select in the specified API version.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "fieldPath"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "mode": {
+ "description": "Optional: mode bits used to set permissions on this file, must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "Required: Path is the relative path name of the file to be created. Must not be absolute or contain the '..' path. Must be utf-8 encoded. The first item of the relative path must not start with '..'",
+ "type": "string"
+ },
+ "resourceFieldRef": {
+ "description": "ResourceFieldSelector represents container resources (cpu, memory) and their output format",
+ "properties": {
+ "containerName": {
+ "description": "Container name: required for volumes, optional for env vars",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "divisor": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ ]
+ },
+ "resource": {
+ "description": "Required: resource to select",
+ "type": "string"
+ }
+ },
+ "required": [
+ "resource"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ }
+ },
+ "required": [
+ "path"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "emptyDir": {
+ "description": "Represents an empty directory for a pod. Empty directory volumes support ownership management and SELinux relabeling.",
+ "properties": {
+ "medium": {
+ "description": "medium represents what type of storage medium should back this directory. The default is \"\" which means to use the node's default medium. Must be an empty string (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "sizeLimit": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "ephemeral": {
+ "description": "Represents an ephemeral volume that is handled by a normal storage driver.",
+ "properties": {
+ "volumeClaimTemplate": {
+ "description": "PersistentVolumeClaimTemplate is used to produce PersistentVolumeClaim objects as part of an EphemeralVolumeSource.",
+ "properties": {
+ "metadata": {
+ "description": "ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.",
+ "properties": {
+ "annotations": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations",
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "creationTimestamp": {
+ "description": "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.",
+ "format": "date-time",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "deletionGracePeriodSeconds": {
+ "description": "Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "deletionTimestamp": {
+ "description": "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.",
+ "format": "date-time",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "finalizers": {
+ "description": "Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed. Finalizers may be processed and removed in any order. Order is NOT enforced because it introduces significant risk of stuck finalizers. finalizers is a shared field, any actor with permission can reorder it. If the finalizer list is processed in order, then this can lead to a situation in which the component responsible for the first finalizer in the list is waiting for a signal (field value, external system, or other) produced by a component responsible for a finalizer later in the list, resulting in a deadlock. Without enforced ordering finalizers are free to order amongst themselves and are not vulnerable to ordering changes in the list.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "generateName": {
+ "description": "GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\n\nIf this field is specified and the generated name exists, the server will return a 409.\n\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "generation": {
+ "description": "A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "labels": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels",
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "managedFields": {
+ "description": "ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like \"ci-cd\". The set of fields is always in the version that the workflow used when modifying the object.",
+ "items": {
+ "description": "ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource that the fieldset applies to.",
+ "properties": {
+ "apiVersion": {
+ "description": "APIVersion defines the version of this resource that this field set applies to. The format is \"group/version\" just like the top-level APIVersion field. It is necessary to track the version of a field set because it cannot be automatically converted.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "fieldsType": {
+ "description": "FieldsType is the discriminator for the different fields format and version. There is currently only one possible value: \"FieldsV1\"",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "fieldsV1": {
+ "description": "FieldsV1 stores a set of fields in a data structure like a Trie, in JSON format.\n\nEach key is either a '.' representing the field itself, and will always map to an empty set, or a string representing a sub-field or item. The string will follow one of these four formats: 'f:', where is the name of a field in a struct, or key in a map 'v:', where is the exact json formatted value of a list item 'i:', where is position of a item in a list 'k:', where is a map of a list item's key fields to their unique values If a key maps to an empty Fields value, the field that key represents is part of the set.\n\nThe exact format is defined in sigs.k8s.io/structured-merge-diff",
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "manager": {
+ "description": "Manager is an identifier of the workflow managing these fields.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "operation": {
+ "description": "Operation is the type of operation which lead to this ManagedFieldsEntry being created. The only valid values for this field are 'Apply' and 'Update'.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "subresource": {
+ "description": "Subresource is the name of the subresource used to update that object, or empty string if the object was updated through the main resource. The value of this field is used to distinguish between managers, even if they share the same name. For example, a status update will be distinct from a regular update using the same manager name. Note that the APIVersion field is not related to the Subresource field and it always corresponds to the version of the main resource.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "time": {
+ "description": "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.",
+ "format": "date-time",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "name": {
+ "description": "Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "namespace": {
+ "description": "Namespace defines the space within which each name must be unique. An empty namespace is equivalent to the \"default\" namespace, but \"default\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\n\nMust be a DNS_LABEL. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "ownerReferences": {
+ "description": "List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.",
+ "items": {
+ "description": "OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.",
+ "properties": {
+ "apiVersion": {
+ "description": "API version of the referent.",
+ "type": "string"
+ },
+ "blockOwnerDeletion": {
+ "description": "If true, AND if the owner has the \"foregroundDeletion\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. See https://kubernetes.io/docs/concepts/architecture/garbage-collection/#foreground-deletion for how the garbage collector interacts with this field and enforces the foreground deletion. Defaults to false. To set this field, a user needs \"delete\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "controller": {
+ "description": "If true, this reference points to the managing controller.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "kind": {
+ "description": "Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
+ "type": "string"
+ },
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names",
+ "type": "string"
+ },
+ "uid": {
+ "description": "UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids",
+ "type": "string"
+ }
+ },
+ "required": [
+ "apiVersion",
+ "kind",
+ "name",
+ "uid"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-merge-key": "uid",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "resourceVersion": {
+ "description": "An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\n\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "selfLink": {
+ "description": "Deprecated: selfLink is a legacy read-only field that is no longer populated by the system.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "uid": {
+ "description": "UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\n\nPopulated by the system. Read-only. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "spec": {
+ "description": "PersistentVolumeClaimSpec describes the common attributes of storage devices and allows a Source for provider-specific attributes",
+ "properties": {
+ "accessModes": {
+ "description": "accessModes contains the desired access modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "dataSource": {
+ "description": "TypedLocalObjectReference contains enough information to let you locate the typed referenced object inside the same namespace.",
+ "properties": {
+ "apiGroup": {
+ "description": "APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "kind": {
+ "description": "Kind is the type of resource being referenced",
+ "type": "string"
+ },
+ "name": {
+ "description": "Name is the name of resource being referenced",
+ "type": "string"
+ }
+ },
+ "required": [
+ "kind",
+ "name"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "dataSourceRef": {
+ "properties": {
+ "apiGroup": {
+ "description": "APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "kind": {
+ "description": "Kind is the type of resource being referenced",
+ "type": "string"
+ },
+ "name": {
+ "description": "Name is the name of resource being referenced",
+ "type": "string"
+ },
+ "namespace": {
+ "description": "Namespace is the namespace of resource being referenced Note that when a namespace is specified, a gateway.networking.k8s.io/ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. (Alpha) This field requires the CrossNamespaceVolumeDataSource feature gate to be enabled.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "kind",
+ "name"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "resources": {
+ "description": "ResourceRequirements describes the compute resource requirements.",
+ "properties": {
+ "claims": {
+ "description": "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container.\n\nThis is an alpha field and requires enabling the DynamicResourceAllocation feature gate.\n\nThis field is immutable. It can only be set for containers.",
+ "items": {
+ "description": "ResourceClaim references one entry in PodSpec.ResourceClaims.",
+ "properties": {
+ "name": {
+ "description": "Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-list-map-keys": [
+ "name"
+ ],
+ "x-kubernetes-list-type": "map"
+ },
+ "limits": {
+ "additionalProperties": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ ]
+ },
+ "description": "Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/",
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "requests": {
+ "additionalProperties": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ ]
+ },
+ "description": "Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/",
+ "type": [
+ "object",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "selector": {
+ "description": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.",
+ "properties": {
+ "matchExpressions": {
+ "description": "matchExpressions is a list of label selector requirements. The requirements are ANDed.",
+ "items": {
+ "description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
+ "properties": {
+ "key": {
+ "description": "key is the label key that the selector applies to.",
+ "type": "string"
+ },
+ "operator": {
+ "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.",
+ "type": "string"
+ },
+ "values": {
+ "description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key",
+ "operator"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "matchLabels": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.",
+ "type": [
+ "object",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "storageClassName": {
+ "description": "storageClassName is the name of the StorageClass required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "volumeMode": {
+ "description": "volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "volumeName": {
+ "description": "volumeName is the binding reference to the PersistentVolume backing this claim.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": "object",
+ "additionalProperties": false
+ }
+ },
+ "required": [
+ "spec"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "fc": {
+ "description": "Represents a Fibre Channel volume. Fibre Channel volumes can only be mounted as read/write once. Fibre Channel volumes support ownership management and SELinux relabeling.",
+ "properties": {
+ "fsType": {
+ "description": "fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "lun": {
+ "description": "lun is Optional: FC target lun number",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "readOnly": {
+ "description": "readOnly is Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "targetWWNs": {
+ "description": "targetWWNs is Optional: FC target worldwide names (WWNs)",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "wwids": {
+ "description": "wwids Optional: FC volume world wide identifiers (wwids) Either wwids or combination of targetWWNs and lun must be set, but not both simultaneously.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "flexVolume": {
+ "description": "FlexVolume represents a generic volume resource that is provisioned/attached using an exec based plugin.",
+ "properties": {
+ "driver": {
+ "description": "driver is the name of the driver to use for this volume.",
+ "type": "string"
+ },
+ "fsType": {
+ "description": "fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". The default filesystem depends on FlexVolume script.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "options": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "options is Optional: this field holds extra command options if any.",
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "readOnly": {
+ "description": "readOnly is Optional: defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "secretRef": {
+ "description": "LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace.",
+ "properties": {
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ }
+ },
+ "required": [
+ "driver"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "flocker": {
+ "description": "Represents a Flocker volume mounted by the Flocker agent. One and only one of datasetName and datasetUUID should be set. Flocker volumes do not support ownership management or SELinux relabeling.",
+ "properties": {
+ "datasetName": {
+ "description": "datasetName is Name of the dataset stored as metadata -> name on the dataset for Flocker should be considered as deprecated",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "datasetUUID": {
+ "description": "datasetUUID is the UUID of the dataset. This is unique identifier of a Flocker dataset",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "gcePersistentDisk": {
+ "description": "Represents a Persistent Disk resource in Google Compute Engine.\n\nA GCE PD must exist before mounting to a container. The disk must also be in the same GCE project and zone as the kubelet. A GCE PD can only be mounted as read/write once or read-only many times. GCE PDs support ownership management and SELinux relabeling.",
+ "properties": {
+ "fsType": {
+ "description": "fsType is filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "partition": {
+ "description": "partition is the partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify the partition as \"1\". Similarly, the volume partition for /dev/sda is \"0\" (or you can leave the property empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "pdName": {
+ "description": "pdName is unique name of the PD resource in GCE. Used to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk",
+ "type": "string"
+ },
+ "readOnly": {
+ "description": "readOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "pdName"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "gitRepo": {
+ "description": "Represents a volume that is populated with the contents of a git repository. Git repo volumes do not support ownership management. Git repo volumes support SELinux relabeling.\n\nDEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir into the Pod's container.",
+ "properties": {
+ "directory": {
+ "description": "directory is the target directory name. Must not contain or start with '..'. If '.' is supplied, the volume directory will be the git repository. Otherwise, if specified, the volume will contain the git repository in the subdirectory with the given name.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "repository": {
+ "description": "repository is the URL",
+ "type": "string"
+ },
+ "revision": {
+ "description": "revision is the commit hash for the specified revision.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "repository"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "glusterfs": {
+ "description": "Represents a Glusterfs mount that lasts the lifetime of a pod. Glusterfs volumes do not support ownership management or SELinux relabeling.",
+ "properties": {
+ "endpoints": {
+ "description": "endpoints is the endpoint name that details Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod",
+ "type": "string"
+ },
+ "path": {
+ "description": "path is the Glusterfs volume path. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod",
+ "type": "string"
+ },
+ "readOnly": {
+ "description": "readOnly here will force the Glusterfs volume to be mounted with read-only permissions. Defaults to false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "endpoints",
+ "path"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "hostPath": {
+ "description": "Represents a host path mapped into a pod. Host path volumes do not support ownership management or SELinux relabeling.",
+ "properties": {
+ "path": {
+ "description": "path of the directory on the host. If the path is a symlink, it will follow the link to the real path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath",
+ "type": "string"
+ },
+ "type": {
+ "description": "type for HostPath Volume Defaults to \"\" More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "path"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "iscsi": {
+ "description": "Represents an ISCSI disk. ISCSI volumes can only be mounted as read/write once. ISCSI volumes support ownership management and SELinux relabeling.",
+ "properties": {
+ "chapAuthDiscovery": {
+ "description": "chapAuthDiscovery defines whether support iSCSI Discovery CHAP authentication",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "chapAuthSession": {
+ "description": "chapAuthSession defines whether support iSCSI Session CHAP authentication",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "fsType": {
+ "description": "fsType is the filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "initiatorName": {
+ "description": "initiatorName is the custom iSCSI Initiator Name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface : will be created for the connection.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "iqn": {
+ "description": "iqn is the target iSCSI Qualified Name.",
+ "type": "string"
+ },
+ "iscsiInterface": {
+ "description": "iscsiInterface is the interface Name that uses an iSCSI transport. Defaults to 'default' (tcp).",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "lun": {
+ "description": "lun represents iSCSI Target Lun number.",
+ "format": "int32",
+ "type": "integer"
+ },
+ "portals": {
+ "description": "portals is the iSCSI Target Portal List. The portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260).",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "readOnly": {
+ "description": "readOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "secretRef": {
+ "description": "LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace.",
+ "properties": {
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "targetPortal": {
+ "description": "targetPortal is iSCSI Target Portal. The Portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260).",
+ "type": "string"
+ }
+ },
+ "required": [
+ "targetPortal",
+ "iqn",
+ "lun"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "name": {
+ "description": "name of the volume. Must be a DNS_LABEL and unique within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": "string"
+ },
+ "nfs": {
+ "description": "Represents an NFS mount that lasts the lifetime of a pod. NFS volumes do not support ownership management or SELinux relabeling.",
+ "properties": {
+ "path": {
+ "description": "path that is exported by the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs",
+ "type": "string"
+ },
+ "readOnly": {
+ "description": "readOnly here will force the NFS export to be mounted with read-only permissions. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "server": {
+ "description": "server is the hostname or IP address of the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs",
+ "type": "string"
+ }
+ },
+ "required": [
+ "server",
+ "path"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "persistentVolumeClaim": {
+ "description": "PersistentVolumeClaimVolumeSource references the user's PVC in the same namespace. This volume finds the bound PV and mounts that volume for the pod. A PersistentVolumeClaimVolumeSource is, essentially, a wrapper around another type of volume that is owned by someone else (the system).",
+ "properties": {
+ "claimName": {
+ "description": "claimName is the name of a PersistentVolumeClaim in the same namespace as the pod using this volume. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims",
+ "type": "string"
+ },
+ "readOnly": {
+ "description": "readOnly Will force the ReadOnly setting in VolumeMounts. Default false.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "claimName"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "photonPersistentDisk": {
+ "description": "Represents a Photon Controller persistent disk resource.",
+ "properties": {
+ "fsType": {
+ "description": "fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "pdID": {
+ "description": "pdID is the ID that identifies Photon Controller persistent disk",
+ "type": "string"
+ }
+ },
+ "required": [
+ "pdID"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "portworxVolume": {
+ "description": "PortworxVolumeSource represents a Portworx volume resource.",
+ "properties": {
+ "fsType": {
+ "description": "fSType represents the filesystem type to mount Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\". Implicitly inferred to be \"ext4\" if unspecified.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "readOnly": {
+ "description": "readOnly defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "volumeID": {
+ "description": "volumeID uniquely identifies a Portworx volume",
+ "type": "string"
+ }
+ },
+ "required": [
+ "volumeID"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "projected": {
+ "description": "Represents a projected volume source",
+ "properties": {
+ "defaultMode": {
+ "description": "defaultMode are the mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "sources": {
+ "description": "sources is the list of volume projections",
+ "items": {
+ "description": "Projection that may be projected along with other supported volume types",
+ "properties": {
+ "configMap": {
+ "description": "Adapts a ConfigMap into a projected volume.\n\nThe contents of the target ConfigMap's Data field will be presented in a projected volume as files using the keys in the Data field as the file names, unless the items element is populated with specific mappings of keys to paths. Note that this is identical to a configmap volume source without the default mode.",
+ "properties": {
+ "items": {
+ "description": "items if unspecified, each key-value pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the ConfigMap, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.",
+ "items": {
+ "description": "Maps a string key to a path within a volume.",
+ "properties": {
+ "key": {
+ "description": "key is the key to project.",
+ "type": "string"
+ },
+ "mode": {
+ "description": "mode is Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "path is the relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "key",
+ "path"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "optional": {
+ "description": "optional specify whether the ConfigMap or its keys must be defined",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "downwardAPI": {
+ "description": "Represents downward API info for projecting into a projected volume. Note that this is identical to a downwardAPI volume source without the default mode.",
+ "properties": {
+ "items": {
+ "description": "Items is a list of DownwardAPIVolume file",
+ "items": {
+ "description": "DownwardAPIVolumeFile represents information to create the file containing the pod field",
+ "properties": {
+ "fieldRef": {
+ "description": "ObjectFieldSelector selects an APIVersioned field of an object.",
+ "properties": {
+ "apiVersion": {
+ "description": "Version of the schema the FieldPath is written in terms of, defaults to \"v1\".",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "fieldPath": {
+ "description": "Path of the field to select in the specified API version.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "fieldPath"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "mode": {
+ "description": "Optional: mode bits used to set permissions on this file, must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "Required: Path is the relative path name of the file to be created. Must not be absolute or contain the '..' path. Must be utf-8 encoded. The first item of the relative path must not start with '..'",
+ "type": "string"
+ },
+ "resourceFieldRef": {
+ "description": "ResourceFieldSelector represents container resources (cpu, memory) and their output format",
+ "properties": {
+ "containerName": {
+ "description": "Container name: required for volumes, optional for env vars",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "divisor": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ ]
+ },
+ "resource": {
+ "description": "Required: resource to select",
+ "type": "string"
+ }
+ },
+ "required": [
+ "resource"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ }
+ },
+ "required": [
+ "path"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "secret": {
+ "description": "Adapts a secret into a projected volume.\n\nThe contents of the target Secret's Data field will be presented in a projected volume as files using the keys in the Data field as the file names. Note that this is identical to a secret volume source without the default mode.",
+ "properties": {
+ "items": {
+ "description": "items if unspecified, each key-value pair in the Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the Secret, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.",
+ "items": {
+ "description": "Maps a string key to a path within a volume.",
+ "properties": {
+ "key": {
+ "description": "key is the key to project.",
+ "type": "string"
+ },
+ "mode": {
+ "description": "mode is Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "path is the relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "key",
+ "path"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "optional": {
+ "description": "optional field specify whether the Secret or its key must be defined",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "serviceAccountToken": {
+ "description": "ServiceAccountTokenProjection represents a projected service account token volume. This projection can be used to insert a service account token into the pods runtime filesystem for use against APIs (Kubernetes API Server or otherwise).",
+ "properties": {
+ "audience": {
+ "description": "audience is the intended audience of the token. A recipient of a token must identify itself with an identifier specified in the audience of the token, and otherwise should reject the token. The audience defaults to the identifier of the apiserver.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "expirationSeconds": {
+ "description": "expirationSeconds is the requested duration of validity of the service account token. As the token approaches expiration, the kubelet volume plugin will proactively rotate the service account token. The kubelet will start trying to rotate the token if the token is older than 80 percent of its time to live or if the token is older than 24 hours.Defaults to 1 hour and must be at least 10 minutes.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "path is the path relative to the mount point of the file to project the token into.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "path"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "quobyte": {
+ "description": "Represents a Quobyte mount that lasts the lifetime of a pod. Quobyte volumes do not support ownership management or SELinux relabeling.",
+ "properties": {
+ "group": {
+ "description": "group to map volume access to Default is no group",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "readOnly": {
+ "description": "readOnly here will force the Quobyte volume to be mounted with read-only permissions. Defaults to false.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "registry": {
+ "description": "registry represents a single or multiple Quobyte Registry services specified as a string as host:port pair (multiple entries are separated with commas) which acts as the central registry for volumes",
+ "type": "string"
+ },
+ "tenant": {
+ "description": "tenant owning the given Quobyte volume in the Backend Used with dynamically provisioned Quobyte volumes, value is set by the plugin",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "user": {
+ "description": "user to map volume access to Defaults to serivceaccount user",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "volume": {
+ "description": "volume is a string that references an already created Quobyte volume by name.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "registry",
+ "volume"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "rbd": {
+ "description": "Represents a Rados Block Device mount that lasts the lifetime of a pod. RBD volumes support ownership management and SELinux relabeling.",
+ "properties": {
+ "fsType": {
+ "description": "fsType is the filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "image": {
+ "description": "image is the rados image name. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it",
+ "type": "string"
+ },
+ "keyring": {
+ "description": "keyring is the path to key ring for RBDUser. Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "monitors": {
+ "description": "monitors is a collection of Ceph monitors. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": "array"
+ },
+ "pool": {
+ "description": "pool is the rados pool name. Default is rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "readOnly": {
+ "description": "readOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "secretRef": {
+ "description": "LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace.",
+ "properties": {
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "user": {
+ "description": "user is the rados user name. Default is admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "monitors",
+ "image"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "scaleIO": {
+ "description": "ScaleIOVolumeSource represents a persistent ScaleIO volume",
+ "properties": {
+ "fsType": {
+ "description": "fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Default is \"xfs\".",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "gateway": {
+ "description": "gateway is the host address of the ScaleIO API Gateway.",
+ "type": "string"
+ },
+ "protectionDomain": {
+ "description": "protectionDomain is the name of the ScaleIO Protection Domain for the configured storage.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "readOnly": {
+ "description": "readOnly Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "secretRef": {
+ "description": "LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace.",
+ "properties": {
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": "object",
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "sslEnabled": {
+ "description": "sslEnabled Flag enable/disable SSL communication with Gateway, default false",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "storageMode": {
+ "description": "storageMode indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned. Default is ThinProvisioned.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "storagePool": {
+ "description": "storagePool is the ScaleIO Storage Pool associated with the protection domain.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "system": {
+ "description": "system is the name of the storage system as configured in ScaleIO.",
+ "type": "string"
+ },
+ "volumeName": {
+ "description": "volumeName is the name of a volume already created in the ScaleIO system that is associated with this volume source.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "gateway",
+ "system",
+ "secretRef"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "secret": {
+ "description": "Adapts a Secret into a volume.\n\nThe contents of the target Secret's Data field will be presented in a volume as files using the keys in the Data field as the file names. Secret volumes support ownership management and SELinux relabeling.",
+ "properties": {
+ "defaultMode": {
+ "description": "defaultMode is Optional: mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "items": {
+ "description": "items If unspecified, each key-value pair in the Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the Secret, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.",
+ "items": {
+ "description": "Maps a string key to a path within a volume.",
+ "properties": {
+ "key": {
+ "description": "key is the key to project.",
+ "type": "string"
+ },
+ "mode": {
+ "description": "mode is Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "path is the relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "key",
+ "path"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "optional": {
+ "description": "optional field specify whether the Secret or its keys must be defined",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "secretName": {
+ "description": "secretName is the name of the secret in the pod's namespace to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "storageos": {
+ "description": "Represents a StorageOS persistent volume resource.",
+ "properties": {
+ "fsType": {
+ "description": "fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "readOnly": {
+ "description": "readOnly defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "secretRef": {
+ "description": "LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace.",
+ "properties": {
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "volumeName": {
+ "description": "volumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "volumeNamespace": {
+ "description": "volumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod's namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to \"default\" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "vsphereVolume": {
+ "description": "Represents a vSphere volume resource.",
+ "properties": {
+ "fsType": {
+ "description": "fsType is filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "storagePolicyID": {
+ "description": "storagePolicyID is the storage Policy Based Management (SPBM) profile ID associated with the StoragePolicyName.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "storagePolicyName": {
+ "description": "storagePolicyName is the storage Policy Based Management (SPBM) profile name.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "volumePath": {
+ "description": "volumePath is the path that identifies vSphere volume vmdk",
+ "type": "string"
+ }
+ },
+ "required": [
+ "volumePath"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "required": [
+ "name"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge,retainKeys"
+ }
+ },
+ "required": [
+ "containers"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": "object",
+ "additionalProperties": false
+ }
+ },
+ "required": [
+ "selector",
+ "template"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "status": {
+ "description": "DeploymentStatus is the most recently observed status of the Deployment.",
+ "properties": {
+ "availableReplicas": {
+ "description": "Total number of available pods (ready for at least minReadySeconds) targeted by this deployment.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "collisionCount": {
+ "description": "Count of hash collisions for the Deployment. The Deployment controller uses this field as a collision avoidance mechanism when it needs to create the name for the newest ReplicaSet.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "conditions": {
+ "description": "Represents the latest available observations of a deployment's current state.",
+ "items": {
+ "description": "DeploymentCondition describes the state of a deployment at a certain point.",
+ "properties": {
+ "lastTransitionTime": {
+ "description": "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.",
+ "format": "date-time",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "lastUpdateTime": {
+ "description": "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.",
+ "format": "date-time",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "message": {
+ "description": "A human readable message indicating details about the transition.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "reason": {
+ "description": "The reason for the condition's last transition.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "status": {
+ "description": "Status of the condition, one of True, False, Unknown.",
+ "type": "string"
+ },
+ "type": {
+ "description": "Type of deployment condition.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type",
+ "status"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-merge-key": "type",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "observedGeneration": {
+ "description": "The generation observed by the deployment controller.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "readyReplicas": {
+ "description": "readyReplicas is the number of pods targeted by this Deployment with a Ready Condition.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "replicas": {
+ "description": "Total number of non-terminated pods targeted by this deployment (their labels match the selector).",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "unavailableReplicas": {
+ "description": "Total number of unavailable pods targeted by this deployment. This is the total number of pods that are still required for the deployment to have 100% available capacity. They may either be pods that are running but not yet available or pods that still have not been created.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "updatedReplicas": {
+ "description": "Total number of non-terminated pods targeted by this deployment that have the desired template spec.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": "object",
+ "x-kubernetes-group-version-kind": [
+ {
+ "group": "apps",
+ "kind": "Deployment",
+ "version": "v1"
+ }
+ ],
+ "additionalProperties": false,
+ "$schema": "http://json-schema.org/schema#"
+}
\ No newline at end of file
diff --git a/packages/editors/src/schemas/kubernetes/index.ts b/packages/editors/src/schemas/kubernetes/index.ts
new file mode 100644
index 0000000..0fea2bf
--- /dev/null
+++ b/packages/editors/src/schemas/kubernetes/index.ts
@@ -0,0 +1,68 @@
+/**
+ * Official Kubernetes JSON Schemas (v1.28.0, standalone-strict).
+ * Source: https://github.com/yannh/kubernetes-json-schema
+ *
+ * Loaded lazily via dynamic import() so they don't bloat the main bundle.
+ */
+
+export interface K8sSchemaEntry {
+ name: string;
+ kind: string;
+ /** Full API group/version, e.g. "v1", "apps/v1", "networking.k8s.io/v1". */
+ apiVersion: string;
+ /** The group::version::resource segment used in structured filenames. */
+ gvr: string;
+ load: () => Promise<{ default: Record }>;
+}
+
+/**
+ * Build a fileMatch pattern for the structured naming convention:
+ * |"_global"/|"_global"/::::.yaml
+ *
+ * Monaco URL-encodes `:` to `%3A` in model URIs, so fileMatch patterns use
+ * the encoded form. The `*` prefix matches any scheme/path prefix.
+ */
+function gvrFileMatch(gvr: string): string[] {
+ const encoded = gvr.replace(/:/g, '%3A');
+ return [`*${encoded}.yaml`, `*${encoded}.yml`];
+}
+
+export const k8sSchemas: K8sSchemaEntry[] = [
+ {
+ name: 'Pod',
+ kind: 'Pod',
+ apiVersion: 'v1',
+ gvr: 'core::v1::Pod',
+ load: () => import('./pod-v1.json'),
+ },
+ {
+ name: 'Deployment',
+ kind: 'Deployment',
+ apiVersion: 'apps/v1',
+ gvr: 'apps::v1::Deployment',
+ load: () => import('./deployment-apps-v1.json'),
+ },
+ {
+ name: 'Service',
+ kind: 'Service',
+ apiVersion: 'v1',
+ gvr: 'core::v1::Service',
+ load: () => import('./service-v1.json'),
+ },
+ {
+ name: 'ConfigMap',
+ kind: 'ConfigMap',
+ apiVersion: 'v1',
+ gvr: 'core::v1::ConfigMap',
+ load: () => import('./configmap-v1.json'),
+ },
+ {
+ name: 'Ingress',
+ kind: 'Ingress',
+ apiVersion: 'networking.k8s.io/v1',
+ gvr: 'networking.k8s.io::v1::Ingress',
+ load: () => import('./ingress-networking-v1.json'),
+ },
+];
+
+export { gvrFileMatch };
diff --git a/packages/editors/src/schemas/kubernetes/ingress-networking-v1.json b/packages/editors/src/schemas/kubernetes/ingress-networking-v1.json
new file mode 100644
index 0000000..a4d1021
--- /dev/null
+++ b/packages/editors/src/schemas/kubernetes/ingress-networking-v1.json
@@ -0,0 +1,656 @@
+{
+ "description": "Ingress is a collection of rules that allow inbound connections to reach the endpoints defined by a backend. An Ingress can be configured to give services externally-reachable urls, load balance traffic, terminate SSL, offer name based virtual hosting etc.",
+ "properties": {
+ "apiVersion": {
+ "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
+ "type": [
+ "string",
+ "null"
+ ],
+ "enum": [
+ "networking.k8s.io/v1"
+ ]
+ },
+ "kind": {
+ "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
+ "type": [
+ "string",
+ "null"
+ ],
+ "enum": [
+ "Ingress"
+ ]
+ },
+ "metadata": {
+ "description": "ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.",
+ "properties": {
+ "annotations": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations",
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "creationTimestamp": {
+ "description": "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.",
+ "format": "date-time",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "deletionGracePeriodSeconds": {
+ "description": "Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "deletionTimestamp": {
+ "description": "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.",
+ "format": "date-time",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "finalizers": {
+ "description": "Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed. Finalizers may be processed and removed in any order. Order is NOT enforced because it introduces significant risk of stuck finalizers. finalizers is a shared field, any actor with permission can reorder it. If the finalizer list is processed in order, then this can lead to a situation in which the component responsible for the first finalizer in the list is waiting for a signal (field value, external system, or other) produced by a component responsible for a finalizer later in the list, resulting in a deadlock. Without enforced ordering finalizers are free to order amongst themselves and are not vulnerable to ordering changes in the list.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "generateName": {
+ "description": "GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\n\nIf this field is specified and the generated name exists, the server will return a 409.\n\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "generation": {
+ "description": "A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "labels": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels",
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "managedFields": {
+ "description": "ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like \"ci-cd\". The set of fields is always in the version that the workflow used when modifying the object.",
+ "items": {
+ "description": "ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource that the fieldset applies to.",
+ "properties": {
+ "apiVersion": {
+ "description": "APIVersion defines the version of this resource that this field set applies to. The format is \"group/version\" just like the top-level APIVersion field. It is necessary to track the version of a field set because it cannot be automatically converted.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "fieldsType": {
+ "description": "FieldsType is the discriminator for the different fields format and version. There is currently only one possible value: \"FieldsV1\"",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "fieldsV1": {
+ "description": "FieldsV1 stores a set of fields in a data structure like a Trie, in JSON format.\n\nEach key is either a '.' representing the field itself, and will always map to an empty set, or a string representing a sub-field or item. The string will follow one of these four formats: 'f:', where is the name of a field in a struct, or key in a map 'v:', where is the exact json formatted value of a list item 'i:', where is position of a item in a list 'k:', where is a map of a list item's key fields to their unique values If a key maps to an empty Fields value, the field that key represents is part of the set.\n\nThe exact format is defined in sigs.k8s.io/structured-merge-diff",
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "manager": {
+ "description": "Manager is an identifier of the workflow managing these fields.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "operation": {
+ "description": "Operation is the type of operation which lead to this ManagedFieldsEntry being created. The only valid values for this field are 'Apply' and 'Update'.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "subresource": {
+ "description": "Subresource is the name of the subresource used to update that object, or empty string if the object was updated through the main resource. The value of this field is used to distinguish between managers, even if they share the same name. For example, a status update will be distinct from a regular update using the same manager name. Note that the APIVersion field is not related to the Subresource field and it always corresponds to the version of the main resource.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "time": {
+ "description": "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.",
+ "format": "date-time",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "name": {
+ "description": "Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "namespace": {
+ "description": "Namespace defines the space within which each name must be unique. An empty namespace is equivalent to the \"default\" namespace, but \"default\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\n\nMust be a DNS_LABEL. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "ownerReferences": {
+ "description": "List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.",
+ "items": {
+ "description": "OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.",
+ "properties": {
+ "apiVersion": {
+ "description": "API version of the referent.",
+ "type": "string"
+ },
+ "blockOwnerDeletion": {
+ "description": "If true, AND if the owner has the \"foregroundDeletion\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. See https://kubernetes.io/docs/concepts/architecture/garbage-collection/#foreground-deletion for how the garbage collector interacts with this field and enforces the foreground deletion. Defaults to false. To set this field, a user needs \"delete\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "controller": {
+ "description": "If true, this reference points to the managing controller.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "kind": {
+ "description": "Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
+ "type": "string"
+ },
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names",
+ "type": "string"
+ },
+ "uid": {
+ "description": "UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids",
+ "type": "string"
+ }
+ },
+ "required": [
+ "apiVersion",
+ "kind",
+ "name",
+ "uid"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-merge-key": "uid",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "resourceVersion": {
+ "description": "An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\n\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "selfLink": {
+ "description": "Deprecated: selfLink is a legacy read-only field that is no longer populated by the system.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "uid": {
+ "description": "UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\n\nPopulated by the system. Read-only. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "spec": {
+ "description": "IngressSpec describes the Ingress the user wishes to exist.",
+ "properties": {
+ "defaultBackend": {
+ "description": "IngressBackend describes all endpoints for a given service and port.",
+ "properties": {
+ "resource": {
+ "description": "TypedLocalObjectReference contains enough information to let you locate the typed referenced object inside the same namespace.",
+ "properties": {
+ "apiGroup": {
+ "description": "APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "kind": {
+ "description": "Kind is the type of resource being referenced",
+ "type": "string"
+ },
+ "name": {
+ "description": "Name is the name of resource being referenced",
+ "type": "string"
+ }
+ },
+ "required": [
+ "kind",
+ "name"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "service": {
+ "description": "IngressServiceBackend references a Kubernetes Service as a Backend.",
+ "properties": {
+ "name": {
+ "description": "name is the referenced service. The service must exist in the same namespace as the Ingress object.",
+ "type": "string"
+ },
+ "port": {
+ "description": "ServiceBackendPort is the service port being referenced.",
+ "properties": {
+ "name": {
+ "description": "name is the name of the port on the Service. This is a mutually exclusive setting with \"Number\".",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "number": {
+ "description": "number is the numerical port number (e.g. 80) on the Service. This is a mutually exclusive setting with \"Name\".",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "required": [
+ "name"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "ingressClassName": {
+ "description": "ingressClassName is the name of an IngressClass cluster resource. Ingress controller implementations use this field to know whether they should be serving this Ingress resource, by a transitive connection (controller -> IngressClass -> Ingress resource). Although the `kubernetes.io/ingress.class` annotation (simple constant name) was never formally defined, it was widely supported by Ingress controllers to create a direct binding between Ingress controller and Ingress resources. Newly created Ingress resources should prefer using the field. However, even though the annotation is officially deprecated, for backwards compatibility reasons, ingress controllers should still honor that annotation if present.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "rules": {
+ "description": "rules is a list of host rules used to configure the Ingress. If unspecified, or no rule matches, all traffic is sent to the default backend.",
+ "items": {
+ "description": "IngressRule represents the rules mapping the paths under a specified host to the related backend services. Incoming requests are first evaluated for a host match, then routed to the backend associated with the matching IngressRuleValue.",
+ "properties": {
+ "host": {
+ "description": "host is the fully qualified domain name of a network host, as defined by RFC 3986. Note the following deviations from the \"host\" part of the URI as defined in RFC 3986: 1. IPs are not allowed. Currently an IngressRuleValue can only apply to\n the IP in the Spec of the parent Ingress.\n2. The `:` delimiter is not respected because ports are not allowed.\n\t Currently the port of an Ingress is implicitly :80 for http and\n\t :443 for https.\nBoth these may change in the future. Incoming requests are matched against the host before the IngressRuleValue. If the host is unspecified, the Ingress routes all traffic based on the specified IngressRuleValue.\n\nhost can be \"precise\" which is a domain name without the terminating dot of a network host (e.g. \"foo.bar.com\") or \"wildcard\", which is a domain name prefixed with a single wildcard label (e.g. \"*.foo.com\"). The wildcard character '*' must appear by itself as the first DNS label and matches only a single label. You cannot have a wildcard label by itself (e.g. Host == \"*\"). Requests will be matched against the Host field in the following way: 1. If host is precise, the request matches this rule if the http host header is equal to Host. 2. If host is a wildcard, then the request matches this rule if the http host header is to equal to the suffix (removing the first label) of the wildcard rule.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "http": {
+ "description": "HTTPIngressRuleValue is a list of http selectors pointing to backends. In the example: http:///? -> backend where where parts of the url correspond to RFC 3986, this resource will be used to match against everything after the last '/' and before the first '?' or '#'.",
+ "properties": {
+ "paths": {
+ "description": "paths is a collection of paths that map requests to backends.",
+ "items": {
+ "description": "HTTPIngressPath associates a path with a backend. Incoming urls matching the path are forwarded to the backend.",
+ "properties": {
+ "backend": {
+ "description": "IngressBackend describes all endpoints for a given service and port.",
+ "properties": {
+ "resource": {
+ "description": "TypedLocalObjectReference contains enough information to let you locate the typed referenced object inside the same namespace.",
+ "properties": {
+ "apiGroup": {
+ "description": "APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "kind": {
+ "description": "Kind is the type of resource being referenced",
+ "type": "string"
+ },
+ "name": {
+ "description": "Name is the name of resource being referenced",
+ "type": "string"
+ }
+ },
+ "required": [
+ "kind",
+ "name"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "service": {
+ "description": "IngressServiceBackend references a Kubernetes Service as a Backend.",
+ "properties": {
+ "name": {
+ "description": "name is the referenced service. The service must exist in the same namespace as the Ingress object.",
+ "type": "string"
+ },
+ "port": {
+ "description": "ServiceBackendPort is the service port being referenced.",
+ "properties": {
+ "name": {
+ "description": "name is the name of the port on the Service. This is a mutually exclusive setting with \"Number\".",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "number": {
+ "description": "number is the numerical port number (e.g. 80) on the Service. This is a mutually exclusive setting with \"Name\".",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "required": [
+ "name"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": "object",
+ "additionalProperties": false
+ },
+ "path": {
+ "description": "path is matched against the path of an incoming request. Currently it can contain characters disallowed from the conventional \"path\" part of a URL as defined by RFC 3986. Paths must begin with a '/' and must be present when using PathType with value \"Exact\" or \"Prefix\".",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "pathType": {
+ "description": "pathType determines the interpretation of the path matching. PathType can be one of the following values: * Exact: Matches the URL path exactly. * Prefix: Matches based on a URL path prefix split by '/'. Matching is\n done on a path element by element basis. A path element refers is the\n list of labels in the path split by the '/' separator. A request is a\n match for path p if every p is an element-wise prefix of p of the\n request path. Note that if the last element of the path is a substring\n of the last element in request path, it is not a match (e.g. /foo/bar\n matches /foo/bar/baz, but does not match /foo/barbaz).\n* ImplementationSpecific: Interpretation of the Path matching is up to\n the IngressClass. Implementations can treat this as a separate PathType\n or treat it identically to Prefix or Exact path types.\nImplementations are required to support all path types.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "pathType",
+ "backend"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": "array",
+ "x-kubernetes-list-type": "atomic"
+ }
+ },
+ "required": [
+ "paths"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-list-type": "atomic"
+ },
+ "tls": {
+ "description": "tls represents the TLS configuration. Currently the Ingress only supports a single TLS port, 443. If multiple members of this list specify different hosts, they will be multiplexed on the same port according to the hostname specified through the SNI TLS extension, if the ingress controller fulfilling the ingress supports SNI.",
+ "items": {
+ "description": "IngressTLS describes the transport layer security associated with an ingress.",
+ "properties": {
+ "hosts": {
+ "description": "hosts is a list of hosts included in the TLS certificate. The values in this list must match the name/s used in the tlsSecret. Defaults to the wildcard host setting for the loadbalancer controller fulfilling this Ingress, if left unspecified.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-list-type": "atomic"
+ },
+ "secretName": {
+ "description": "secretName is the name of the secret used to terminate TLS traffic on port 443. Field is left optional to allow TLS routing based on SNI hostname alone. If the SNI host in a listener conflicts with the \"Host\" header field used by an IngressRule, the SNI host is used for termination and value of the \"Host\" header is used for routing.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-list-type": "atomic"
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "status": {
+ "description": "IngressStatus describe the current state of the Ingress.",
+ "properties": {
+ "loadBalancer": {
+ "description": "IngressLoadBalancerStatus represents the status of a load-balancer.",
+ "properties": {
+ "ingress": {
+ "description": "ingress is a list containing ingress points for the load-balancer.",
+ "items": {
+ "description": "IngressLoadBalancerIngress represents the status of a load-balancer ingress point.",
+ "properties": {
+ "hostname": {
+ "description": "hostname is set for load-balancer ingress points that are DNS based.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "ip": {
+ "description": "ip is set for load-balancer ingress points that are IP based.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "ports": {
+ "description": "ports provides information about the ports exposed by this LoadBalancer.",
+ "items": {
+ "description": "IngressPortStatus represents the error condition of a service port",
+ "properties": {
+ "error": {
+ "description": "error is to record the problem with the service port The format of the error shall comply with the following rules: - built-in error values shall be specified in this file and those shall use\n CamelCase names\n- cloud provider specific error values must have names that comply with the\n format foo.example.com/CamelCase.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "description": "port is the port number of the ingress port.",
+ "format": "int32",
+ "type": "integer"
+ },
+ "protocol": {
+ "description": "protocol is the protocol of the ingress port. The supported values are: \"TCP\", \"UDP\", \"SCTP\"",
+ "type": "string"
+ }
+ },
+ "required": [
+ "port",
+ "protocol"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-list-type": "atomic"
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": "object",
+ "x-kubernetes-group-version-kind": [
+ {
+ "group": "networking.k8s.io",
+ "kind": "Ingress",
+ "version": "v1"
+ }
+ ],
+ "additionalProperties": false,
+ "$schema": "http://json-schema.org/schema#"
+}
\ No newline at end of file
diff --git a/packages/editors/src/schemas/kubernetes/pod-v1.json b/packages/editors/src/schemas/kubernetes/pod-v1.json
new file mode 100644
index 0000000..8eb5efd
--- /dev/null
+++ b/packages/editors/src/schemas/kubernetes/pod-v1.json
@@ -0,0 +1,11148 @@
+{
+ "description": "Pod is a collection of containers that can run on a host. This resource is created by clients and scheduled onto hosts.",
+ "properties": {
+ "apiVersion": {
+ "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
+ "type": [
+ "string",
+ "null"
+ ],
+ "enum": [
+ "v1"
+ ]
+ },
+ "kind": {
+ "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
+ "type": [
+ "string",
+ "null"
+ ],
+ "enum": [
+ "Pod"
+ ]
+ },
+ "metadata": {
+ "description": "ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.",
+ "properties": {
+ "annotations": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations",
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "creationTimestamp": {
+ "description": "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.",
+ "format": "date-time",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "deletionGracePeriodSeconds": {
+ "description": "Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "deletionTimestamp": {
+ "description": "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.",
+ "format": "date-time",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "finalizers": {
+ "description": "Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed. Finalizers may be processed and removed in any order. Order is NOT enforced because it introduces significant risk of stuck finalizers. finalizers is a shared field, any actor with permission can reorder it. If the finalizer list is processed in order, then this can lead to a situation in which the component responsible for the first finalizer in the list is waiting for a signal (field value, external system, or other) produced by a component responsible for a finalizer later in the list, resulting in a deadlock. Without enforced ordering finalizers are free to order amongst themselves and are not vulnerable to ordering changes in the list.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "generateName": {
+ "description": "GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\n\nIf this field is specified and the generated name exists, the server will return a 409.\n\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "generation": {
+ "description": "A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "labels": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels",
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "managedFields": {
+ "description": "ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like \"ci-cd\". The set of fields is always in the version that the workflow used when modifying the object.",
+ "items": {
+ "description": "ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource that the fieldset applies to.",
+ "properties": {
+ "apiVersion": {
+ "description": "APIVersion defines the version of this resource that this field set applies to. The format is \"group/version\" just like the top-level APIVersion field. It is necessary to track the version of a field set because it cannot be automatically converted.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "fieldsType": {
+ "description": "FieldsType is the discriminator for the different fields format and version. There is currently only one possible value: \"FieldsV1\"",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "fieldsV1": {
+ "description": "FieldsV1 stores a set of fields in a data structure like a Trie, in JSON format.\n\nEach key is either a '.' representing the field itself, and will always map to an empty set, or a string representing a sub-field or item. The string will follow one of these four formats: 'f:', where is the name of a field in a struct, or key in a map 'v:', where is the exact json formatted value of a list item 'i:', where is position of a item in a list 'k:', where is a map of a list item's key fields to their unique values If a key maps to an empty Fields value, the field that key represents is part of the set.\n\nThe exact format is defined in sigs.k8s.io/structured-merge-diff",
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "manager": {
+ "description": "Manager is an identifier of the workflow managing these fields.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "operation": {
+ "description": "Operation is the type of operation which lead to this ManagedFieldsEntry being created. The only valid values for this field are 'Apply' and 'Update'.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "subresource": {
+ "description": "Subresource is the name of the subresource used to update that object, or empty string if the object was updated through the main resource. The value of this field is used to distinguish between managers, even if they share the same name. For example, a status update will be distinct from a regular update using the same manager name. Note that the APIVersion field is not related to the Subresource field and it always corresponds to the version of the main resource.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "time": {
+ "description": "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.",
+ "format": "date-time",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "name": {
+ "description": "Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "namespace": {
+ "description": "Namespace defines the space within which each name must be unique. An empty namespace is equivalent to the \"default\" namespace, but \"default\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\n\nMust be a DNS_LABEL. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "ownerReferences": {
+ "description": "List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.",
+ "items": {
+ "description": "OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.",
+ "properties": {
+ "apiVersion": {
+ "description": "API version of the referent.",
+ "type": "string"
+ },
+ "blockOwnerDeletion": {
+ "description": "If true, AND if the owner has the \"foregroundDeletion\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. See https://kubernetes.io/docs/concepts/architecture/garbage-collection/#foreground-deletion for how the garbage collector interacts with this field and enforces the foreground deletion. Defaults to false. To set this field, a user needs \"delete\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "controller": {
+ "description": "If true, this reference points to the managing controller.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "kind": {
+ "description": "Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
+ "type": "string"
+ },
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names",
+ "type": "string"
+ },
+ "uid": {
+ "description": "UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids",
+ "type": "string"
+ }
+ },
+ "required": [
+ "apiVersion",
+ "kind",
+ "name",
+ "uid"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-merge-key": "uid",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "resourceVersion": {
+ "description": "An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\n\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "selfLink": {
+ "description": "Deprecated: selfLink is a legacy read-only field that is no longer populated by the system.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "uid": {
+ "description": "UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\n\nPopulated by the system. Read-only. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "spec": {
+ "description": "PodSpec is a description of a pod.",
+ "properties": {
+ "activeDeadlineSeconds": {
+ "description": "Optional duration in seconds the pod may be active on the node relative to StartTime before the system will actively try to mark it failed and kill associated containers. Value must be a positive integer.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "affinity": {
+ "description": "Affinity is a group of affinity scheduling rules.",
+ "properties": {
+ "nodeAffinity": {
+ "description": "Node affinity is a group of node affinity scheduling rules.",
+ "properties": {
+ "preferredDuringSchedulingIgnoredDuringExecution": {
+ "description": "The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \"weight\" to the sum if the node matches the corresponding matchExpressions; the node(s) with the highest sum are the most preferred.",
+ "items": {
+ "description": "An empty preferred scheduling term matches all objects with implicit weight 0 (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op).",
+ "properties": {
+ "preference": {
+ "description": "A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.",
+ "properties": {
+ "matchExpressions": {
+ "description": "A list of node selector requirements by node's labels.",
+ "items": {
+ "description": "A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
+ "properties": {
+ "key": {
+ "description": "The label key that the selector applies to.",
+ "type": "string"
+ },
+ "operator": {
+ "description": "Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.",
+ "type": "string"
+ },
+ "values": {
+ "description": "An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key",
+ "operator"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "matchFields": {
+ "description": "A list of node selector requirements by node's fields.",
+ "items": {
+ "description": "A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
+ "properties": {
+ "key": {
+ "description": "The label key that the selector applies to.",
+ "type": "string"
+ },
+ "operator": {
+ "description": "Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.",
+ "type": "string"
+ },
+ "values": {
+ "description": "An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key",
+ "operator"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": "object",
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "weight": {
+ "description": "Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100.",
+ "format": "int32",
+ "type": "integer"
+ }
+ },
+ "required": [
+ "weight",
+ "preference"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "requiredDuringSchedulingIgnoredDuringExecution": {
+ "description": "A node selector represents the union of the results of one or more label queries over a set of nodes; that is, it represents the OR of the selectors represented by the node selector terms.",
+ "properties": {
+ "nodeSelectorTerms": {
+ "description": "Required. A list of node selector terms. The terms are ORed.",
+ "items": {
+ "description": "A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.",
+ "properties": {
+ "matchExpressions": {
+ "description": "A list of node selector requirements by node's labels.",
+ "items": {
+ "description": "A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
+ "properties": {
+ "key": {
+ "description": "The label key that the selector applies to.",
+ "type": "string"
+ },
+ "operator": {
+ "description": "Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.",
+ "type": "string"
+ },
+ "values": {
+ "description": "An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key",
+ "operator"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "matchFields": {
+ "description": "A list of node selector requirements by node's fields.",
+ "items": {
+ "description": "A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
+ "properties": {
+ "key": {
+ "description": "The label key that the selector applies to.",
+ "type": "string"
+ },
+ "operator": {
+ "description": "Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.",
+ "type": "string"
+ },
+ "values": {
+ "description": "An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key",
+ "operator"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "type": "array"
+ }
+ },
+ "required": [
+ "nodeSelectorTerms"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "podAffinity": {
+ "description": "Pod affinity is a group of inter pod affinity scheduling rules.",
+ "properties": {
+ "preferredDuringSchedulingIgnoredDuringExecution": {
+ "description": "The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \"weight\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.",
+ "items": {
+ "description": "The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s)",
+ "properties": {
+ "podAffinityTerm": {
+ "description": "Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key matches that of any node on which a pod of the set of pods is running",
+ "properties": {
+ "labelSelector": {
+ "description": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.",
+ "properties": {
+ "matchExpressions": {
+ "description": "matchExpressions is a list of label selector requirements. The requirements are ANDed.",
+ "items": {
+ "description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
+ "properties": {
+ "key": {
+ "description": "key is the label key that the selector applies to.",
+ "type": "string"
+ },
+ "operator": {
+ "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.",
+ "type": "string"
+ },
+ "values": {
+ "description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key",
+ "operator"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "matchLabels": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.",
+ "type": [
+ "object",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "namespaceSelector": {
+ "description": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.",
+ "properties": {
+ "matchExpressions": {
+ "description": "matchExpressions is a list of label selector requirements. The requirements are ANDed.",
+ "items": {
+ "description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
+ "properties": {
+ "key": {
+ "description": "key is the label key that the selector applies to.",
+ "type": "string"
+ },
+ "operator": {
+ "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.",
+ "type": "string"
+ },
+ "values": {
+ "description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key",
+ "operator"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "matchLabels": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.",
+ "type": [
+ "object",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "namespaces": {
+ "description": "namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector means \"this pod's namespace\".",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "topologyKey": {
+ "description": "This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "topologyKey"
+ ],
+ "type": "object",
+ "additionalProperties": false
+ },
+ "weight": {
+ "description": "weight associated with matching the corresponding podAffinityTerm, in the range 1-100.",
+ "format": "int32",
+ "type": "integer"
+ }
+ },
+ "required": [
+ "weight",
+ "podAffinityTerm"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "requiredDuringSchedulingIgnoredDuringExecution": {
+ "description": "If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied.",
+ "items": {
+ "description": "Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key matches that of any node on which a pod of the set of pods is running",
+ "properties": {
+ "labelSelector": {
+ "description": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.",
+ "properties": {
+ "matchExpressions": {
+ "description": "matchExpressions is a list of label selector requirements. The requirements are ANDed.",
+ "items": {
+ "description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
+ "properties": {
+ "key": {
+ "description": "key is the label key that the selector applies to.",
+ "type": "string"
+ },
+ "operator": {
+ "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.",
+ "type": "string"
+ },
+ "values": {
+ "description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key",
+ "operator"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "matchLabels": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.",
+ "type": [
+ "object",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "namespaceSelector": {
+ "description": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.",
+ "properties": {
+ "matchExpressions": {
+ "description": "matchExpressions is a list of label selector requirements. The requirements are ANDed.",
+ "items": {
+ "description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
+ "properties": {
+ "key": {
+ "description": "key is the label key that the selector applies to.",
+ "type": "string"
+ },
+ "operator": {
+ "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.",
+ "type": "string"
+ },
+ "values": {
+ "description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key",
+ "operator"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "matchLabels": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.",
+ "type": [
+ "object",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "namespaces": {
+ "description": "namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector means \"this pod's namespace\".",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "topologyKey": {
+ "description": "This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "topologyKey"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "podAntiAffinity": {
+ "description": "Pod anti affinity is a group of inter pod anti affinity scheduling rules.",
+ "properties": {
+ "preferredDuringSchedulingIgnoredDuringExecution": {
+ "description": "The scheduler will prefer to schedule pods to nodes that satisfy the anti-affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling anti-affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \"weight\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.",
+ "items": {
+ "description": "The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s)",
+ "properties": {
+ "podAffinityTerm": {
+ "description": "Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key matches that of any node on which a pod of the set of pods is running",
+ "properties": {
+ "labelSelector": {
+ "description": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.",
+ "properties": {
+ "matchExpressions": {
+ "description": "matchExpressions is a list of label selector requirements. The requirements are ANDed.",
+ "items": {
+ "description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
+ "properties": {
+ "key": {
+ "description": "key is the label key that the selector applies to.",
+ "type": "string"
+ },
+ "operator": {
+ "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.",
+ "type": "string"
+ },
+ "values": {
+ "description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key",
+ "operator"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "matchLabels": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.",
+ "type": [
+ "object",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "namespaceSelector": {
+ "description": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.",
+ "properties": {
+ "matchExpressions": {
+ "description": "matchExpressions is a list of label selector requirements. The requirements are ANDed.",
+ "items": {
+ "description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
+ "properties": {
+ "key": {
+ "description": "key is the label key that the selector applies to.",
+ "type": "string"
+ },
+ "operator": {
+ "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.",
+ "type": "string"
+ },
+ "values": {
+ "description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key",
+ "operator"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "matchLabels": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.",
+ "type": [
+ "object",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "namespaces": {
+ "description": "namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector means \"this pod's namespace\".",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "topologyKey": {
+ "description": "This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "topologyKey"
+ ],
+ "type": "object",
+ "additionalProperties": false
+ },
+ "weight": {
+ "description": "weight associated with matching the corresponding podAffinityTerm, in the range 1-100.",
+ "format": "int32",
+ "type": "integer"
+ }
+ },
+ "required": [
+ "weight",
+ "podAffinityTerm"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "requiredDuringSchedulingIgnoredDuringExecution": {
+ "description": "If the anti-affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the anti-affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied.",
+ "items": {
+ "description": "Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key matches that of any node on which a pod of the set of pods is running",
+ "properties": {
+ "labelSelector": {
+ "description": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.",
+ "properties": {
+ "matchExpressions": {
+ "description": "matchExpressions is a list of label selector requirements. The requirements are ANDed.",
+ "items": {
+ "description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
+ "properties": {
+ "key": {
+ "description": "key is the label key that the selector applies to.",
+ "type": "string"
+ },
+ "operator": {
+ "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.",
+ "type": "string"
+ },
+ "values": {
+ "description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key",
+ "operator"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "matchLabels": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.",
+ "type": [
+ "object",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "namespaceSelector": {
+ "description": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.",
+ "properties": {
+ "matchExpressions": {
+ "description": "matchExpressions is a list of label selector requirements. The requirements are ANDed.",
+ "items": {
+ "description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
+ "properties": {
+ "key": {
+ "description": "key is the label key that the selector applies to.",
+ "type": "string"
+ },
+ "operator": {
+ "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.",
+ "type": "string"
+ },
+ "values": {
+ "description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key",
+ "operator"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "matchLabels": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.",
+ "type": [
+ "object",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "namespaces": {
+ "description": "namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector means \"this pod's namespace\".",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "topologyKey": {
+ "description": "This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "topologyKey"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "automountServiceAccountToken": {
+ "description": "AutomountServiceAccountToken indicates whether a service account token should be automatically mounted.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "containers": {
+ "description": "List of containers belonging to the pod. Containers cannot currently be added or removed. There must be at least one container in a Pod. Cannot be updated.",
+ "items": {
+ "description": "A single application container that you want to run within a pod.",
+ "properties": {
+ "args": {
+ "description": "Arguments to the entrypoint. The container image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "command": {
+ "description": "Entrypoint array. Not executed within a shell. The container image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "env": {
+ "description": "List of environment variables to set in the container. Cannot be updated.",
+ "items": {
+ "description": "EnvVar represents an environment variable present in a Container.",
+ "properties": {
+ "name": {
+ "description": "Name of the environment variable. Must be a C_IDENTIFIER.",
+ "type": "string"
+ },
+ "value": {
+ "description": "Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to \"\".",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "valueFrom": {
+ "description": "EnvVarSource represents a source for the value of an EnvVar.",
+ "properties": {
+ "configMapKeyRef": {
+ "description": "Selects a key from a ConfigMap.",
+ "properties": {
+ "key": {
+ "description": "The key to select.",
+ "type": "string"
+ },
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "optional": {
+ "description": "Specify whether the ConfigMap or its key must be defined",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "fieldRef": {
+ "description": "ObjectFieldSelector selects an APIVersioned field of an object.",
+ "properties": {
+ "apiVersion": {
+ "description": "Version of the schema the FieldPath is written in terms of, defaults to \"v1\".",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "fieldPath": {
+ "description": "Path of the field to select in the specified API version.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "fieldPath"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "resourceFieldRef": {
+ "description": "ResourceFieldSelector represents container resources (cpu, memory) and their output format",
+ "properties": {
+ "containerName": {
+ "description": "Container name: required for volumes, optional for env vars",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "divisor": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ ]
+ },
+ "resource": {
+ "description": "Required: resource to select",
+ "type": "string"
+ }
+ },
+ "required": [
+ "resource"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "secretKeyRef": {
+ "description": "SecretKeySelector selects a key of a Secret.",
+ "properties": {
+ "key": {
+ "description": "The key of the secret to select from. Must be a valid secret key.",
+ "type": "string"
+ },
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "optional": {
+ "description": "Specify whether the Secret or its key must be defined",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "required": [
+ "name"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "envFrom": {
+ "description": "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.",
+ "items": {
+ "description": "EnvFromSource represents the source of a set of ConfigMaps",
+ "properties": {
+ "configMapRef": {
+ "description": "ConfigMapEnvSource selects a ConfigMap to populate the environment variables with.\n\nThe contents of the target ConfigMap's Data field will represent the key-value pairs as environment variables.",
+ "properties": {
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "optional": {
+ "description": "Specify whether the ConfigMap must be defined",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "prefix": {
+ "description": "An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "secretRef": {
+ "description": "SecretEnvSource selects a Secret to populate the environment variables with.\n\nThe contents of the target Secret's Data field will represent the key-value pairs as environment variables.",
+ "properties": {
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "optional": {
+ "description": "Specify whether the Secret must be defined",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "image": {
+ "description": "Container image name. More info: https://kubernetes.io/docs/concepts/containers/images This field is optional to allow higher level config management to default or override container images in workload controllers like Deployments and StatefulSets.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "imagePullPolicy": {
+ "description": "Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "lifecycle": {
+ "description": "Lifecycle describes actions that the management system should take in response to container lifecycle events. For the PostStart and PreStop lifecycle handlers, management of the container blocks until the action is complete, unless the container process fails, in which case the handler is aborted.",
+ "properties": {
+ "postStart": {
+ "description": "LifecycleHandler defines a specific action that should be taken in a lifecycle hook. One and only one of the fields, except TCPSocket must be specified.",
+ "properties": {
+ "exec": {
+ "description": "ExecAction describes a \"run in container\" action.",
+ "properties": {
+ "command": {
+ "description": "Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "httpGet": {
+ "description": "HTTPGetAction describes an action based on HTTP Get requests.",
+ "properties": {
+ "host": {
+ "description": "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "httpHeaders": {
+ "description": "Custom headers to set in the request. HTTP allows repeated headers.",
+ "items": {
+ "description": "HTTPHeader describes a custom header to be used in HTTP probes",
+ "properties": {
+ "name": {
+ "description": "The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.",
+ "type": "string"
+ },
+ "value": {
+ "description": "The header field value",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "Path to access on the HTTP server.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ },
+ "scheme": {
+ "description": "Scheme to use for connecting to the host. Defaults to HTTP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "tcpSocket": {
+ "description": "TCPSocketAction describes an action based on opening a socket",
+ "properties": {
+ "host": {
+ "description": "Optional: Host name to connect to, defaults to the pod IP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "preStop": {
+ "description": "LifecycleHandler defines a specific action that should be taken in a lifecycle hook. One and only one of the fields, except TCPSocket must be specified.",
+ "properties": {
+ "exec": {
+ "description": "ExecAction describes a \"run in container\" action.",
+ "properties": {
+ "command": {
+ "description": "Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "httpGet": {
+ "description": "HTTPGetAction describes an action based on HTTP Get requests.",
+ "properties": {
+ "host": {
+ "description": "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "httpHeaders": {
+ "description": "Custom headers to set in the request. HTTP allows repeated headers.",
+ "items": {
+ "description": "HTTPHeader describes a custom header to be used in HTTP probes",
+ "properties": {
+ "name": {
+ "description": "The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.",
+ "type": "string"
+ },
+ "value": {
+ "description": "The header field value",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "Path to access on the HTTP server.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ },
+ "scheme": {
+ "description": "Scheme to use for connecting to the host. Defaults to HTTP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "tcpSocket": {
+ "description": "TCPSocketAction describes an action based on opening a socket",
+ "properties": {
+ "host": {
+ "description": "Optional: Host name to connect to, defaults to the pod IP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "livenessProbe": {
+ "description": "Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.",
+ "properties": {
+ "exec": {
+ "description": "ExecAction describes a \"run in container\" action.",
+ "properties": {
+ "command": {
+ "description": "Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "failureThreshold": {
+ "description": "Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "grpc": {
+ "properties": {
+ "port": {
+ "description": "Port number of the gRPC service. Number must be in the range 1 to 65535.",
+ "format": "int32",
+ "type": "integer"
+ },
+ "service": {
+ "description": "Service is the name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md).\n\nIf this is not specified, the default behavior is defined by gRPC.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "httpGet": {
+ "description": "HTTPGetAction describes an action based on HTTP Get requests.",
+ "properties": {
+ "host": {
+ "description": "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "httpHeaders": {
+ "description": "Custom headers to set in the request. HTTP allows repeated headers.",
+ "items": {
+ "description": "HTTPHeader describes a custom header to be used in HTTP probes",
+ "properties": {
+ "name": {
+ "description": "The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.",
+ "type": "string"
+ },
+ "value": {
+ "description": "The header field value",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "Path to access on the HTTP server.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ },
+ "scheme": {
+ "description": "Scheme to use for connecting to the host. Defaults to HTTP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "initialDelaySeconds": {
+ "description": "Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "periodSeconds": {
+ "description": "How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "successThreshold": {
+ "description": "Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "tcpSocket": {
+ "description": "TCPSocketAction describes an action based on opening a socket",
+ "properties": {
+ "host": {
+ "description": "Optional: Host name to connect to, defaults to the pod IP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "terminationGracePeriodSeconds": {
+ "description": "Optional duration in seconds the pod needs to terminate gracefully upon probe failure. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this value overrides the value provided by the pod spec. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "timeoutSeconds": {
+ "description": "Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "name": {
+ "description": "Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated.",
+ "type": "string"
+ },
+ "ports": {
+ "description": "List of ports to expose from the container. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default \"0.0.0.0\" address inside a container will be accessible from the network. Modifying this array with strategic merge patch may corrupt the data. For more information See https://github.com/kubernetes/kubernetes/issues/108255. Cannot be updated.",
+ "items": {
+ "description": "ContainerPort represents a network port in a single container.",
+ "properties": {
+ "containerPort": {
+ "description": "Number of port to expose on the pod's IP address. This must be a valid port number, 0 < x < 65536.",
+ "format": "int32",
+ "type": "integer"
+ },
+ "hostIP": {
+ "description": "What host IP to bind the external port to.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "hostPort": {
+ "description": "Number of port to expose on the host. If specified, this must be a valid port number, 0 < x < 65536. If HostNetwork is specified, this must match ContainerPort. Most containers do not need this.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "name": {
+ "description": "If specified, this must be an IANA_SVC_NAME and unique within the pod. Each named port in a pod must have a unique name. Name for the port that can be referred to by services.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "protocol": {
+ "description": "Protocol for port. Must be UDP, TCP, or SCTP. Defaults to \"TCP\".",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "containerPort"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-list-map-keys": [
+ "containerPort",
+ "protocol"
+ ],
+ "x-kubernetes-list-type": "map",
+ "x-kubernetes-patch-merge-key": "containerPort",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "readinessProbe": {
+ "description": "Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.",
+ "properties": {
+ "exec": {
+ "description": "ExecAction describes a \"run in container\" action.",
+ "properties": {
+ "command": {
+ "description": "Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "failureThreshold": {
+ "description": "Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "grpc": {
+ "properties": {
+ "port": {
+ "description": "Port number of the gRPC service. Number must be in the range 1 to 65535.",
+ "format": "int32",
+ "type": "integer"
+ },
+ "service": {
+ "description": "Service is the name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md).\n\nIf this is not specified, the default behavior is defined by gRPC.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "httpGet": {
+ "description": "HTTPGetAction describes an action based on HTTP Get requests.",
+ "properties": {
+ "host": {
+ "description": "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "httpHeaders": {
+ "description": "Custom headers to set in the request. HTTP allows repeated headers.",
+ "items": {
+ "description": "HTTPHeader describes a custom header to be used in HTTP probes",
+ "properties": {
+ "name": {
+ "description": "The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.",
+ "type": "string"
+ },
+ "value": {
+ "description": "The header field value",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "Path to access on the HTTP server.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ },
+ "scheme": {
+ "description": "Scheme to use for connecting to the host. Defaults to HTTP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "initialDelaySeconds": {
+ "description": "Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "periodSeconds": {
+ "description": "How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "successThreshold": {
+ "description": "Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "tcpSocket": {
+ "description": "TCPSocketAction describes an action based on opening a socket",
+ "properties": {
+ "host": {
+ "description": "Optional: Host name to connect to, defaults to the pod IP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "terminationGracePeriodSeconds": {
+ "description": "Optional duration in seconds the pod needs to terminate gracefully upon probe failure. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this value overrides the value provided by the pod spec. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "timeoutSeconds": {
+ "description": "Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "resizePolicy": {
+ "description": "Resources resize policy for the container.",
+ "items": {
+ "description": "ContainerResizePolicy represents resource resize policy for the container.",
+ "properties": {
+ "resourceName": {
+ "description": "Name of the resource to which this resource resize policy applies. Supported values: cpu, memory.",
+ "type": "string"
+ },
+ "restartPolicy": {
+ "description": "Restart policy to apply when specified resource is resized. If not specified, it defaults to NotRequired.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "resourceName",
+ "restartPolicy"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-list-type": "atomic"
+ },
+ "resources": {
+ "description": "ResourceRequirements describes the compute resource requirements.",
+ "properties": {
+ "claims": {
+ "description": "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container.\n\nThis is an alpha field and requires enabling the DynamicResourceAllocation feature gate.\n\nThis field is immutable. It can only be set for containers.",
+ "items": {
+ "description": "ResourceClaim references one entry in PodSpec.ResourceClaims.",
+ "properties": {
+ "name": {
+ "description": "Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-list-map-keys": [
+ "name"
+ ],
+ "x-kubernetes-list-type": "map"
+ },
+ "limits": {
+ "additionalProperties": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ ]
+ },
+ "description": "Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/",
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "requests": {
+ "additionalProperties": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ ]
+ },
+ "description": "Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/",
+ "type": [
+ "object",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "restartPolicy": {
+ "description": "RestartPolicy defines the restart behavior of individual containers in a pod. This field may only be set for init containers, and the only allowed value is \"Always\". For non-init containers or when this field is not specified, the restart behavior is defined by the Pod's restart policy and the container type. Setting the RestartPolicy as \"Always\" for the init container will have the following effect: this init container will be continually restarted on exit until all regular containers have terminated. Once all regular containers have completed, all init containers with restartPolicy \"Always\" will be shut down. This lifecycle differs from normal init containers and is often referred to as a \"sidecar\" container. Although this init container still starts in the init container sequence, it does not wait for the container to complete before proceeding to the next init container. Instead, the next init container starts immediately after this init container is started, or after any startupProbe has successfully completed.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "securityContext": {
+ "description": "SecurityContext holds security configuration that will be applied to a container. Some fields are present in both SecurityContext and PodSecurityContext. When both are set, the values in SecurityContext take precedence.",
+ "properties": {
+ "allowPrivilegeEscalation": {
+ "description": "AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process. This bool directly controls if the no_new_privs flag will be set on the container process. AllowPrivilegeEscalation is true always when the container is: 1) run as Privileged 2) has CAP_SYS_ADMIN Note that this field cannot be set when spec.os.name is windows.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "capabilities": {
+ "description": "Adds and removes POSIX capabilities from running containers.",
+ "properties": {
+ "add": {
+ "description": "Added capabilities",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "drop": {
+ "description": "Removed capabilities",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "privileged": {
+ "description": "Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false. Note that this field cannot be set when spec.os.name is windows.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "procMount": {
+ "description": "procMount denotes the type of proc mount to use for the containers. The default is DefaultProcMount which uses the container runtime defaults for readonly paths and masked paths. This requires the ProcMountType feature flag to be enabled. Note that this field cannot be set when spec.os.name is windows.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "readOnlyRootFilesystem": {
+ "description": "Whether this container has a read-only root filesystem. Default is false. Note that this field cannot be set when spec.os.name is windows.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "runAsGroup": {
+ "description": "The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is windows.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "runAsNonRoot": {
+ "description": "Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "runAsUser": {
+ "description": "The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is windows.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "seLinuxOptions": {
+ "description": "SELinuxOptions are the labels to be applied to the container",
+ "properties": {
+ "level": {
+ "description": "Level is SELinux level label that applies to the container.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "role": {
+ "description": "Role is a SELinux role label that applies to the container.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": {
+ "description": "Type is a SELinux type label that applies to the container.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "user": {
+ "description": "User is a SELinux user label that applies to the container.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "seccompProfile": {
+ "description": "SeccompProfile defines a pod/container's seccomp profile settings. Only one profile source may be set.",
+ "properties": {
+ "localhostProfile": {
+ "description": "localhostProfile indicates a profile defined in a file on the node should be used. The profile must be preconfigured on the node to work. Must be a descending path, relative to the kubelet's configured seccomp profile location. Must be set if type is \"Localhost\". Must NOT be set for any other type.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": {
+ "description": "type indicates which kind of seccomp profile will be applied. Valid options are:\n\nLocalhost - a profile defined in a file on the node should be used. RuntimeDefault - the container runtime default profile should be used. Unconfined - no profile should be applied.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-unions": [
+ {
+ "discriminator": "type",
+ "fields-to-discriminateBy": {
+ "localhostProfile": "LocalhostProfile"
+ }
+ }
+ ],
+ "additionalProperties": false
+ },
+ "windowsOptions": {
+ "description": "WindowsSecurityContextOptions contain Windows-specific options and credentials.",
+ "properties": {
+ "gmsaCredentialSpec": {
+ "description": "GMSACredentialSpec is where the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the GMSA credential spec named by the GMSACredentialSpecName field.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "gmsaCredentialSpecName": {
+ "description": "GMSACredentialSpecName is the name of the GMSA credential spec to use.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "hostProcess": {
+ "description": "HostProcess determines if a container should be run as a 'Host Process' container. All of a Pod's containers must have the same effective HostProcess value (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). In addition, if HostProcess is true then HostNetwork must also be set to true.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "runAsUserName": {
+ "description": "The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "startupProbe": {
+ "description": "Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.",
+ "properties": {
+ "exec": {
+ "description": "ExecAction describes a \"run in container\" action.",
+ "properties": {
+ "command": {
+ "description": "Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "failureThreshold": {
+ "description": "Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "grpc": {
+ "properties": {
+ "port": {
+ "description": "Port number of the gRPC service. Number must be in the range 1 to 65535.",
+ "format": "int32",
+ "type": "integer"
+ },
+ "service": {
+ "description": "Service is the name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md).\n\nIf this is not specified, the default behavior is defined by gRPC.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "httpGet": {
+ "description": "HTTPGetAction describes an action based on HTTP Get requests.",
+ "properties": {
+ "host": {
+ "description": "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "httpHeaders": {
+ "description": "Custom headers to set in the request. HTTP allows repeated headers.",
+ "items": {
+ "description": "HTTPHeader describes a custom header to be used in HTTP probes",
+ "properties": {
+ "name": {
+ "description": "The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.",
+ "type": "string"
+ },
+ "value": {
+ "description": "The header field value",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "Path to access on the HTTP server.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ },
+ "scheme": {
+ "description": "Scheme to use for connecting to the host. Defaults to HTTP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "initialDelaySeconds": {
+ "description": "Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "periodSeconds": {
+ "description": "How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "successThreshold": {
+ "description": "Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "tcpSocket": {
+ "description": "TCPSocketAction describes an action based on opening a socket",
+ "properties": {
+ "host": {
+ "description": "Optional: Host name to connect to, defaults to the pod IP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "terminationGracePeriodSeconds": {
+ "description": "Optional duration in seconds the pod needs to terminate gracefully upon probe failure. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this value overrides the value provided by the pod spec. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "timeoutSeconds": {
+ "description": "Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "stdin": {
+ "description": "Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "stdinOnce": {
+ "description": "Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "terminationMessagePath": {
+ "description": "Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "terminationMessagePolicy": {
+ "description": "Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "tty": {
+ "description": "Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "volumeDevices": {
+ "description": "volumeDevices is the list of block devices to be used by the container.",
+ "items": {
+ "description": "volumeDevice describes a mapping of a raw block device within a container.",
+ "properties": {
+ "devicePath": {
+ "description": "devicePath is the path inside of the container that the device will be mapped to.",
+ "type": "string"
+ },
+ "name": {
+ "description": "name must match the name of a persistentVolumeClaim in the pod",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "devicePath"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-merge-key": "devicePath",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "volumeMounts": {
+ "description": "Pod volumes to mount into the container's filesystem. Cannot be updated.",
+ "items": {
+ "description": "VolumeMount describes a mounting of a Volume within a container.",
+ "properties": {
+ "mountPath": {
+ "description": "Path within the container at which the volume should be mounted. Must not contain ':'.",
+ "type": "string"
+ },
+ "mountPropagation": {
+ "description": "mountPropagation determines how mounts are propagated from the host to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "name": {
+ "description": "This must match the Name of a Volume.",
+ "type": "string"
+ },
+ "readOnly": {
+ "description": "Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "subPath": {
+ "description": "Path within the volume from which the container's volume should be mounted. Defaults to \"\" (volume's root).",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "subPathExpr": {
+ "description": "Expanded path within the volume from which the container's volume should be mounted. Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. Defaults to \"\" (volume's root). SubPathExpr and SubPath are mutually exclusive.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "name",
+ "mountPath"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-merge-key": "mountPath",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "workingDir": {
+ "description": "Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "name"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": "array",
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "dnsConfig": {
+ "description": "PodDNSConfig defines the DNS parameters of a pod in addition to those generated from DNSPolicy.",
+ "properties": {
+ "nameservers": {
+ "description": "A list of DNS name server IP addresses. This will be appended to the base nameservers generated from DNSPolicy. Duplicated nameservers will be removed.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "options": {
+ "description": "A list of DNS resolver options. This will be merged with the base options generated from DNSPolicy. Duplicated entries will be removed. Resolution options given in Options will override those that appear in the base DNSPolicy.",
+ "items": {
+ "description": "PodDNSConfigOption defines DNS resolver options of a pod.",
+ "properties": {
+ "name": {
+ "description": "Required.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "value": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "searches": {
+ "description": "A list of DNS search domains for host-name lookup. This will be appended to the base search paths generated from DNSPolicy. Duplicated search paths will be removed.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "dnsPolicy": {
+ "description": "Set DNS policy for the pod. Defaults to \"ClusterFirst\". Valid values are 'ClusterFirstWithHostNet', 'ClusterFirst', 'Default' or 'None'. DNS parameters given in DNSConfig will be merged with the policy selected with DNSPolicy. To have DNS options set along with hostNetwork, you have to specify DNS policy explicitly to 'ClusterFirstWithHostNet'.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "enableServiceLinks": {
+ "description": "EnableServiceLinks indicates whether information about services should be injected into pod's environment variables, matching the syntax of Docker links. Optional: Defaults to true.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "ephemeralContainers": {
+ "description": "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource.",
+ "items": {
+ "description": "An EphemeralContainer is a temporary container that you may add to an existing Pod for user-initiated activities such as debugging. Ephemeral containers have no resource or scheduling guarantees, and they will not be restarted when they exit or when a Pod is removed or restarted. The kubelet may evict a Pod if an ephemeral container causes the Pod to exceed its resource allocation.\n\nTo add an ephemeral container, use the ephemeralcontainers subresource of an existing Pod. Ephemeral containers may not be removed or restarted.",
+ "properties": {
+ "args": {
+ "description": "Arguments to the entrypoint. The image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "command": {
+ "description": "Entrypoint array. Not executed within a shell. The image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "env": {
+ "description": "List of environment variables to set in the container. Cannot be updated.",
+ "items": {
+ "description": "EnvVar represents an environment variable present in a Container.",
+ "properties": {
+ "name": {
+ "description": "Name of the environment variable. Must be a C_IDENTIFIER.",
+ "type": "string"
+ },
+ "value": {
+ "description": "Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to \"\".",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "valueFrom": {
+ "description": "EnvVarSource represents a source for the value of an EnvVar.",
+ "properties": {
+ "configMapKeyRef": {
+ "description": "Selects a key from a ConfigMap.",
+ "properties": {
+ "key": {
+ "description": "The key to select.",
+ "type": "string"
+ },
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "optional": {
+ "description": "Specify whether the ConfigMap or its key must be defined",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "fieldRef": {
+ "description": "ObjectFieldSelector selects an APIVersioned field of an object.",
+ "properties": {
+ "apiVersion": {
+ "description": "Version of the schema the FieldPath is written in terms of, defaults to \"v1\".",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "fieldPath": {
+ "description": "Path of the field to select in the specified API version.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "fieldPath"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "resourceFieldRef": {
+ "description": "ResourceFieldSelector represents container resources (cpu, memory) and their output format",
+ "properties": {
+ "containerName": {
+ "description": "Container name: required for volumes, optional for env vars",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "divisor": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ ]
+ },
+ "resource": {
+ "description": "Required: resource to select",
+ "type": "string"
+ }
+ },
+ "required": [
+ "resource"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "secretKeyRef": {
+ "description": "SecretKeySelector selects a key of a Secret.",
+ "properties": {
+ "key": {
+ "description": "The key of the secret to select from. Must be a valid secret key.",
+ "type": "string"
+ },
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "optional": {
+ "description": "Specify whether the Secret or its key must be defined",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "required": [
+ "name"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "envFrom": {
+ "description": "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.",
+ "items": {
+ "description": "EnvFromSource represents the source of a set of ConfigMaps",
+ "properties": {
+ "configMapRef": {
+ "description": "ConfigMapEnvSource selects a ConfigMap to populate the environment variables with.\n\nThe contents of the target ConfigMap's Data field will represent the key-value pairs as environment variables.",
+ "properties": {
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "optional": {
+ "description": "Specify whether the ConfigMap must be defined",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "prefix": {
+ "description": "An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "secretRef": {
+ "description": "SecretEnvSource selects a Secret to populate the environment variables with.\n\nThe contents of the target Secret's Data field will represent the key-value pairs as environment variables.",
+ "properties": {
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "optional": {
+ "description": "Specify whether the Secret must be defined",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "image": {
+ "description": "Container image name. More info: https://kubernetes.io/docs/concepts/containers/images",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "imagePullPolicy": {
+ "description": "Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "lifecycle": {
+ "description": "Lifecycle describes actions that the management system should take in response to container lifecycle events. For the PostStart and PreStop lifecycle handlers, management of the container blocks until the action is complete, unless the container process fails, in which case the handler is aborted.",
+ "properties": {
+ "postStart": {
+ "description": "LifecycleHandler defines a specific action that should be taken in a lifecycle hook. One and only one of the fields, except TCPSocket must be specified.",
+ "properties": {
+ "exec": {
+ "description": "ExecAction describes a \"run in container\" action.",
+ "properties": {
+ "command": {
+ "description": "Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "httpGet": {
+ "description": "HTTPGetAction describes an action based on HTTP Get requests.",
+ "properties": {
+ "host": {
+ "description": "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "httpHeaders": {
+ "description": "Custom headers to set in the request. HTTP allows repeated headers.",
+ "items": {
+ "description": "HTTPHeader describes a custom header to be used in HTTP probes",
+ "properties": {
+ "name": {
+ "description": "The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.",
+ "type": "string"
+ },
+ "value": {
+ "description": "The header field value",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "Path to access on the HTTP server.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ },
+ "scheme": {
+ "description": "Scheme to use for connecting to the host. Defaults to HTTP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "tcpSocket": {
+ "description": "TCPSocketAction describes an action based on opening a socket",
+ "properties": {
+ "host": {
+ "description": "Optional: Host name to connect to, defaults to the pod IP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "preStop": {
+ "description": "LifecycleHandler defines a specific action that should be taken in a lifecycle hook. One and only one of the fields, except TCPSocket must be specified.",
+ "properties": {
+ "exec": {
+ "description": "ExecAction describes a \"run in container\" action.",
+ "properties": {
+ "command": {
+ "description": "Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "httpGet": {
+ "description": "HTTPGetAction describes an action based on HTTP Get requests.",
+ "properties": {
+ "host": {
+ "description": "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "httpHeaders": {
+ "description": "Custom headers to set in the request. HTTP allows repeated headers.",
+ "items": {
+ "description": "HTTPHeader describes a custom header to be used in HTTP probes",
+ "properties": {
+ "name": {
+ "description": "The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.",
+ "type": "string"
+ },
+ "value": {
+ "description": "The header field value",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "Path to access on the HTTP server.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ },
+ "scheme": {
+ "description": "Scheme to use for connecting to the host. Defaults to HTTP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "tcpSocket": {
+ "description": "TCPSocketAction describes an action based on opening a socket",
+ "properties": {
+ "host": {
+ "description": "Optional: Host name to connect to, defaults to the pod IP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "livenessProbe": {
+ "description": "Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.",
+ "properties": {
+ "exec": {
+ "description": "ExecAction describes a \"run in container\" action.",
+ "properties": {
+ "command": {
+ "description": "Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "failureThreshold": {
+ "description": "Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "grpc": {
+ "properties": {
+ "port": {
+ "description": "Port number of the gRPC service. Number must be in the range 1 to 65535.",
+ "format": "int32",
+ "type": "integer"
+ },
+ "service": {
+ "description": "Service is the name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md).\n\nIf this is not specified, the default behavior is defined by gRPC.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "httpGet": {
+ "description": "HTTPGetAction describes an action based on HTTP Get requests.",
+ "properties": {
+ "host": {
+ "description": "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "httpHeaders": {
+ "description": "Custom headers to set in the request. HTTP allows repeated headers.",
+ "items": {
+ "description": "HTTPHeader describes a custom header to be used in HTTP probes",
+ "properties": {
+ "name": {
+ "description": "The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.",
+ "type": "string"
+ },
+ "value": {
+ "description": "The header field value",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "Path to access on the HTTP server.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ },
+ "scheme": {
+ "description": "Scheme to use for connecting to the host. Defaults to HTTP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "initialDelaySeconds": {
+ "description": "Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "periodSeconds": {
+ "description": "How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "successThreshold": {
+ "description": "Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "tcpSocket": {
+ "description": "TCPSocketAction describes an action based on opening a socket",
+ "properties": {
+ "host": {
+ "description": "Optional: Host name to connect to, defaults to the pod IP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "terminationGracePeriodSeconds": {
+ "description": "Optional duration in seconds the pod needs to terminate gracefully upon probe failure. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this value overrides the value provided by the pod spec. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "timeoutSeconds": {
+ "description": "Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "name": {
+ "description": "Name of the ephemeral container specified as a DNS_LABEL. This name must be unique among all containers, init containers and ephemeral containers.",
+ "type": "string"
+ },
+ "ports": {
+ "description": "Ports are not allowed for ephemeral containers.",
+ "items": {
+ "description": "ContainerPort represents a network port in a single container.",
+ "properties": {
+ "containerPort": {
+ "description": "Number of port to expose on the pod's IP address. This must be a valid port number, 0 < x < 65536.",
+ "format": "int32",
+ "type": "integer"
+ },
+ "hostIP": {
+ "description": "What host IP to bind the external port to.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "hostPort": {
+ "description": "Number of port to expose on the host. If specified, this must be a valid port number, 0 < x < 65536. If HostNetwork is specified, this must match ContainerPort. Most containers do not need this.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "name": {
+ "description": "If specified, this must be an IANA_SVC_NAME and unique within the pod. Each named port in a pod must have a unique name. Name for the port that can be referred to by services.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "protocol": {
+ "description": "Protocol for port. Must be UDP, TCP, or SCTP. Defaults to \"TCP\".",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "containerPort"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-list-map-keys": [
+ "containerPort",
+ "protocol"
+ ],
+ "x-kubernetes-list-type": "map",
+ "x-kubernetes-patch-merge-key": "containerPort",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "readinessProbe": {
+ "description": "Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.",
+ "properties": {
+ "exec": {
+ "description": "ExecAction describes a \"run in container\" action.",
+ "properties": {
+ "command": {
+ "description": "Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "failureThreshold": {
+ "description": "Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "grpc": {
+ "properties": {
+ "port": {
+ "description": "Port number of the gRPC service. Number must be in the range 1 to 65535.",
+ "format": "int32",
+ "type": "integer"
+ },
+ "service": {
+ "description": "Service is the name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md).\n\nIf this is not specified, the default behavior is defined by gRPC.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "httpGet": {
+ "description": "HTTPGetAction describes an action based on HTTP Get requests.",
+ "properties": {
+ "host": {
+ "description": "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "httpHeaders": {
+ "description": "Custom headers to set in the request. HTTP allows repeated headers.",
+ "items": {
+ "description": "HTTPHeader describes a custom header to be used in HTTP probes",
+ "properties": {
+ "name": {
+ "description": "The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.",
+ "type": "string"
+ },
+ "value": {
+ "description": "The header field value",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "Path to access on the HTTP server.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ },
+ "scheme": {
+ "description": "Scheme to use for connecting to the host. Defaults to HTTP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "initialDelaySeconds": {
+ "description": "Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "periodSeconds": {
+ "description": "How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "successThreshold": {
+ "description": "Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "tcpSocket": {
+ "description": "TCPSocketAction describes an action based on opening a socket",
+ "properties": {
+ "host": {
+ "description": "Optional: Host name to connect to, defaults to the pod IP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "terminationGracePeriodSeconds": {
+ "description": "Optional duration in seconds the pod needs to terminate gracefully upon probe failure. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this value overrides the value provided by the pod spec. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "timeoutSeconds": {
+ "description": "Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "resizePolicy": {
+ "description": "Resources resize policy for the container.",
+ "items": {
+ "description": "ContainerResizePolicy represents resource resize policy for the container.",
+ "properties": {
+ "resourceName": {
+ "description": "Name of the resource to which this resource resize policy applies. Supported values: cpu, memory.",
+ "type": "string"
+ },
+ "restartPolicy": {
+ "description": "Restart policy to apply when specified resource is resized. If not specified, it defaults to NotRequired.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "resourceName",
+ "restartPolicy"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-list-type": "atomic"
+ },
+ "resources": {
+ "description": "ResourceRequirements describes the compute resource requirements.",
+ "properties": {
+ "claims": {
+ "description": "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container.\n\nThis is an alpha field and requires enabling the DynamicResourceAllocation feature gate.\n\nThis field is immutable. It can only be set for containers.",
+ "items": {
+ "description": "ResourceClaim references one entry in PodSpec.ResourceClaims.",
+ "properties": {
+ "name": {
+ "description": "Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-list-map-keys": [
+ "name"
+ ],
+ "x-kubernetes-list-type": "map"
+ },
+ "limits": {
+ "additionalProperties": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ ]
+ },
+ "description": "Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/",
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "requests": {
+ "additionalProperties": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ ]
+ },
+ "description": "Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/",
+ "type": [
+ "object",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "restartPolicy": {
+ "description": "Restart policy for the container to manage the restart behavior of each container within a pod. This may only be set for init containers. You cannot set this field on ephemeral containers.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "securityContext": {
+ "description": "SecurityContext holds security configuration that will be applied to a container. Some fields are present in both SecurityContext and PodSecurityContext. When both are set, the values in SecurityContext take precedence.",
+ "properties": {
+ "allowPrivilegeEscalation": {
+ "description": "AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process. This bool directly controls if the no_new_privs flag will be set on the container process. AllowPrivilegeEscalation is true always when the container is: 1) run as Privileged 2) has CAP_SYS_ADMIN Note that this field cannot be set when spec.os.name is windows.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "capabilities": {
+ "description": "Adds and removes POSIX capabilities from running containers.",
+ "properties": {
+ "add": {
+ "description": "Added capabilities",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "drop": {
+ "description": "Removed capabilities",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "privileged": {
+ "description": "Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false. Note that this field cannot be set when spec.os.name is windows.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "procMount": {
+ "description": "procMount denotes the type of proc mount to use for the containers. The default is DefaultProcMount which uses the container runtime defaults for readonly paths and masked paths. This requires the ProcMountType feature flag to be enabled. Note that this field cannot be set when spec.os.name is windows.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "readOnlyRootFilesystem": {
+ "description": "Whether this container has a read-only root filesystem. Default is false. Note that this field cannot be set when spec.os.name is windows.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "runAsGroup": {
+ "description": "The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is windows.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "runAsNonRoot": {
+ "description": "Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "runAsUser": {
+ "description": "The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is windows.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "seLinuxOptions": {
+ "description": "SELinuxOptions are the labels to be applied to the container",
+ "properties": {
+ "level": {
+ "description": "Level is SELinux level label that applies to the container.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "role": {
+ "description": "Role is a SELinux role label that applies to the container.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": {
+ "description": "Type is a SELinux type label that applies to the container.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "user": {
+ "description": "User is a SELinux user label that applies to the container.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "seccompProfile": {
+ "description": "SeccompProfile defines a pod/container's seccomp profile settings. Only one profile source may be set.",
+ "properties": {
+ "localhostProfile": {
+ "description": "localhostProfile indicates a profile defined in a file on the node should be used. The profile must be preconfigured on the node to work. Must be a descending path, relative to the kubelet's configured seccomp profile location. Must be set if type is \"Localhost\". Must NOT be set for any other type.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": {
+ "description": "type indicates which kind of seccomp profile will be applied. Valid options are:\n\nLocalhost - a profile defined in a file on the node should be used. RuntimeDefault - the container runtime default profile should be used. Unconfined - no profile should be applied.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-unions": [
+ {
+ "discriminator": "type",
+ "fields-to-discriminateBy": {
+ "localhostProfile": "LocalhostProfile"
+ }
+ }
+ ],
+ "additionalProperties": false
+ },
+ "windowsOptions": {
+ "description": "WindowsSecurityContextOptions contain Windows-specific options and credentials.",
+ "properties": {
+ "gmsaCredentialSpec": {
+ "description": "GMSACredentialSpec is where the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the GMSA credential spec named by the GMSACredentialSpecName field.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "gmsaCredentialSpecName": {
+ "description": "GMSACredentialSpecName is the name of the GMSA credential spec to use.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "hostProcess": {
+ "description": "HostProcess determines if a container should be run as a 'Host Process' container. All of a Pod's containers must have the same effective HostProcess value (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). In addition, if HostProcess is true then HostNetwork must also be set to true.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "runAsUserName": {
+ "description": "The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "startupProbe": {
+ "description": "Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.",
+ "properties": {
+ "exec": {
+ "description": "ExecAction describes a \"run in container\" action.",
+ "properties": {
+ "command": {
+ "description": "Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "failureThreshold": {
+ "description": "Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "grpc": {
+ "properties": {
+ "port": {
+ "description": "Port number of the gRPC service. Number must be in the range 1 to 65535.",
+ "format": "int32",
+ "type": "integer"
+ },
+ "service": {
+ "description": "Service is the name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md).\n\nIf this is not specified, the default behavior is defined by gRPC.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "httpGet": {
+ "description": "HTTPGetAction describes an action based on HTTP Get requests.",
+ "properties": {
+ "host": {
+ "description": "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "httpHeaders": {
+ "description": "Custom headers to set in the request. HTTP allows repeated headers.",
+ "items": {
+ "description": "HTTPHeader describes a custom header to be used in HTTP probes",
+ "properties": {
+ "name": {
+ "description": "The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.",
+ "type": "string"
+ },
+ "value": {
+ "description": "The header field value",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "Path to access on the HTTP server.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ },
+ "scheme": {
+ "description": "Scheme to use for connecting to the host. Defaults to HTTP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "initialDelaySeconds": {
+ "description": "Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "periodSeconds": {
+ "description": "How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "successThreshold": {
+ "description": "Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "tcpSocket": {
+ "description": "TCPSocketAction describes an action based on opening a socket",
+ "properties": {
+ "host": {
+ "description": "Optional: Host name to connect to, defaults to the pod IP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "terminationGracePeriodSeconds": {
+ "description": "Optional duration in seconds the pod needs to terminate gracefully upon probe failure. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this value overrides the value provided by the pod spec. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "timeoutSeconds": {
+ "description": "Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "stdin": {
+ "description": "Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "stdinOnce": {
+ "description": "Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "targetContainerName": {
+ "description": "If set, the name of the container from PodSpec that this ephemeral container targets. The ephemeral container will be run in the namespaces (IPC, PID, etc) of this container. If not set then the ephemeral container uses the namespaces configured in the Pod spec.\n\nThe container runtime must implement support for this feature. If the runtime does not support namespace targeting then the result of setting this field is undefined.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "terminationMessagePath": {
+ "description": "Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "terminationMessagePolicy": {
+ "description": "Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "tty": {
+ "description": "Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "volumeDevices": {
+ "description": "volumeDevices is the list of block devices to be used by the container.",
+ "items": {
+ "description": "volumeDevice describes a mapping of a raw block device within a container.",
+ "properties": {
+ "devicePath": {
+ "description": "devicePath is the path inside of the container that the device will be mapped to.",
+ "type": "string"
+ },
+ "name": {
+ "description": "name must match the name of a persistentVolumeClaim in the pod",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "devicePath"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-merge-key": "devicePath",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "volumeMounts": {
+ "description": "Pod volumes to mount into the container's filesystem. Subpath mounts are not allowed for ephemeral containers. Cannot be updated.",
+ "items": {
+ "description": "VolumeMount describes a mounting of a Volume within a container.",
+ "properties": {
+ "mountPath": {
+ "description": "Path within the container at which the volume should be mounted. Must not contain ':'.",
+ "type": "string"
+ },
+ "mountPropagation": {
+ "description": "mountPropagation determines how mounts are propagated from the host to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "name": {
+ "description": "This must match the Name of a Volume.",
+ "type": "string"
+ },
+ "readOnly": {
+ "description": "Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "subPath": {
+ "description": "Path within the volume from which the container's volume should be mounted. Defaults to \"\" (volume's root).",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "subPathExpr": {
+ "description": "Expanded path within the volume from which the container's volume should be mounted. Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. Defaults to \"\" (volume's root). SubPathExpr and SubPath are mutually exclusive.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "name",
+ "mountPath"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-merge-key": "mountPath",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "workingDir": {
+ "description": "Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "name"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "hostAliases": {
+ "description": "HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods.",
+ "items": {
+ "description": "HostAlias holds the mapping between IP and hostnames that will be injected as an entry in the pod's hosts file.",
+ "properties": {
+ "hostnames": {
+ "description": "Hostnames for the above IP address.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "ip": {
+ "description": "IP address of the host file entry.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-merge-key": "ip",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "hostIPC": {
+ "description": "Use the host's ipc namespace. Optional: Default to false.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "hostNetwork": {
+ "description": "Host networking requested for this pod. Use the host's network namespace. If this option is set, the ports that will be used must be specified. Default to false.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "hostPID": {
+ "description": "Use the host's pid namespace. Optional: Default to false.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "hostUsers": {
+ "description": "Use the host's user namespace. Optional: Default to true. If set to true or not present, the pod will be run in the host user namespace, useful for when the pod needs a feature only available to the host user namespace, such as loading a kernel module with CAP_SYS_MODULE. When set to false, a new userns is created for the pod. Setting false is useful for mitigating container breakout vulnerabilities even allowing users to run their containers as root without actually having root privileges on the host. This field is alpha-level and is only honored by servers that enable the UserNamespacesSupport feature.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "hostname": {
+ "description": "Specifies the hostname of the Pod If not specified, the pod's hostname will be set to a system-defined value.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "imagePullSecrets": {
+ "description": "ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod",
+ "items": {
+ "description": "LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace.",
+ "properties": {
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "initContainers": {
+ "description": "List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/",
+ "items": {
+ "description": "A single application container that you want to run within a pod.",
+ "properties": {
+ "args": {
+ "description": "Arguments to the entrypoint. The container image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "command": {
+ "description": "Entrypoint array. Not executed within a shell. The container image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "env": {
+ "description": "List of environment variables to set in the container. Cannot be updated.",
+ "items": {
+ "description": "EnvVar represents an environment variable present in a Container.",
+ "properties": {
+ "name": {
+ "description": "Name of the environment variable. Must be a C_IDENTIFIER.",
+ "type": "string"
+ },
+ "value": {
+ "description": "Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to \"\".",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "valueFrom": {
+ "description": "EnvVarSource represents a source for the value of an EnvVar.",
+ "properties": {
+ "configMapKeyRef": {
+ "description": "Selects a key from a ConfigMap.",
+ "properties": {
+ "key": {
+ "description": "The key to select.",
+ "type": "string"
+ },
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "optional": {
+ "description": "Specify whether the ConfigMap or its key must be defined",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "fieldRef": {
+ "description": "ObjectFieldSelector selects an APIVersioned field of an object.",
+ "properties": {
+ "apiVersion": {
+ "description": "Version of the schema the FieldPath is written in terms of, defaults to \"v1\".",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "fieldPath": {
+ "description": "Path of the field to select in the specified API version.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "fieldPath"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "resourceFieldRef": {
+ "description": "ResourceFieldSelector represents container resources (cpu, memory) and their output format",
+ "properties": {
+ "containerName": {
+ "description": "Container name: required for volumes, optional for env vars",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "divisor": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ ]
+ },
+ "resource": {
+ "description": "Required: resource to select",
+ "type": "string"
+ }
+ },
+ "required": [
+ "resource"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "secretKeyRef": {
+ "description": "SecretKeySelector selects a key of a Secret.",
+ "properties": {
+ "key": {
+ "description": "The key of the secret to select from. Must be a valid secret key.",
+ "type": "string"
+ },
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "optional": {
+ "description": "Specify whether the Secret or its key must be defined",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "required": [
+ "name"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "envFrom": {
+ "description": "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.",
+ "items": {
+ "description": "EnvFromSource represents the source of a set of ConfigMaps",
+ "properties": {
+ "configMapRef": {
+ "description": "ConfigMapEnvSource selects a ConfigMap to populate the environment variables with.\n\nThe contents of the target ConfigMap's Data field will represent the key-value pairs as environment variables.",
+ "properties": {
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "optional": {
+ "description": "Specify whether the ConfigMap must be defined",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "prefix": {
+ "description": "An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "secretRef": {
+ "description": "SecretEnvSource selects a Secret to populate the environment variables with.\n\nThe contents of the target Secret's Data field will represent the key-value pairs as environment variables.",
+ "properties": {
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "optional": {
+ "description": "Specify whether the Secret must be defined",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "image": {
+ "description": "Container image name. More info: https://kubernetes.io/docs/concepts/containers/images This field is optional to allow higher level config management to default or override container images in workload controllers like Deployments and StatefulSets.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "imagePullPolicy": {
+ "description": "Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "lifecycle": {
+ "description": "Lifecycle describes actions that the management system should take in response to container lifecycle events. For the PostStart and PreStop lifecycle handlers, management of the container blocks until the action is complete, unless the container process fails, in which case the handler is aborted.",
+ "properties": {
+ "postStart": {
+ "description": "LifecycleHandler defines a specific action that should be taken in a lifecycle hook. One and only one of the fields, except TCPSocket must be specified.",
+ "properties": {
+ "exec": {
+ "description": "ExecAction describes a \"run in container\" action.",
+ "properties": {
+ "command": {
+ "description": "Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "httpGet": {
+ "description": "HTTPGetAction describes an action based on HTTP Get requests.",
+ "properties": {
+ "host": {
+ "description": "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "httpHeaders": {
+ "description": "Custom headers to set in the request. HTTP allows repeated headers.",
+ "items": {
+ "description": "HTTPHeader describes a custom header to be used in HTTP probes",
+ "properties": {
+ "name": {
+ "description": "The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.",
+ "type": "string"
+ },
+ "value": {
+ "description": "The header field value",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "Path to access on the HTTP server.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ },
+ "scheme": {
+ "description": "Scheme to use for connecting to the host. Defaults to HTTP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "tcpSocket": {
+ "description": "TCPSocketAction describes an action based on opening a socket",
+ "properties": {
+ "host": {
+ "description": "Optional: Host name to connect to, defaults to the pod IP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "preStop": {
+ "description": "LifecycleHandler defines a specific action that should be taken in a lifecycle hook. One and only one of the fields, except TCPSocket must be specified.",
+ "properties": {
+ "exec": {
+ "description": "ExecAction describes a \"run in container\" action.",
+ "properties": {
+ "command": {
+ "description": "Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "httpGet": {
+ "description": "HTTPGetAction describes an action based on HTTP Get requests.",
+ "properties": {
+ "host": {
+ "description": "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "httpHeaders": {
+ "description": "Custom headers to set in the request. HTTP allows repeated headers.",
+ "items": {
+ "description": "HTTPHeader describes a custom header to be used in HTTP probes",
+ "properties": {
+ "name": {
+ "description": "The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.",
+ "type": "string"
+ },
+ "value": {
+ "description": "The header field value",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "Path to access on the HTTP server.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ },
+ "scheme": {
+ "description": "Scheme to use for connecting to the host. Defaults to HTTP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "tcpSocket": {
+ "description": "TCPSocketAction describes an action based on opening a socket",
+ "properties": {
+ "host": {
+ "description": "Optional: Host name to connect to, defaults to the pod IP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "livenessProbe": {
+ "description": "Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.",
+ "properties": {
+ "exec": {
+ "description": "ExecAction describes a \"run in container\" action.",
+ "properties": {
+ "command": {
+ "description": "Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "failureThreshold": {
+ "description": "Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "grpc": {
+ "properties": {
+ "port": {
+ "description": "Port number of the gRPC service. Number must be in the range 1 to 65535.",
+ "format": "int32",
+ "type": "integer"
+ },
+ "service": {
+ "description": "Service is the name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md).\n\nIf this is not specified, the default behavior is defined by gRPC.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "httpGet": {
+ "description": "HTTPGetAction describes an action based on HTTP Get requests.",
+ "properties": {
+ "host": {
+ "description": "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "httpHeaders": {
+ "description": "Custom headers to set in the request. HTTP allows repeated headers.",
+ "items": {
+ "description": "HTTPHeader describes a custom header to be used in HTTP probes",
+ "properties": {
+ "name": {
+ "description": "The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.",
+ "type": "string"
+ },
+ "value": {
+ "description": "The header field value",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "Path to access on the HTTP server.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ },
+ "scheme": {
+ "description": "Scheme to use for connecting to the host. Defaults to HTTP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "initialDelaySeconds": {
+ "description": "Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "periodSeconds": {
+ "description": "How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "successThreshold": {
+ "description": "Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "tcpSocket": {
+ "description": "TCPSocketAction describes an action based on opening a socket",
+ "properties": {
+ "host": {
+ "description": "Optional: Host name to connect to, defaults to the pod IP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "terminationGracePeriodSeconds": {
+ "description": "Optional duration in seconds the pod needs to terminate gracefully upon probe failure. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this value overrides the value provided by the pod spec. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "timeoutSeconds": {
+ "description": "Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "name": {
+ "description": "Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated.",
+ "type": "string"
+ },
+ "ports": {
+ "description": "List of ports to expose from the container. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default \"0.0.0.0\" address inside a container will be accessible from the network. Modifying this array with strategic merge patch may corrupt the data. For more information See https://github.com/kubernetes/kubernetes/issues/108255. Cannot be updated.",
+ "items": {
+ "description": "ContainerPort represents a network port in a single container.",
+ "properties": {
+ "containerPort": {
+ "description": "Number of port to expose on the pod's IP address. This must be a valid port number, 0 < x < 65536.",
+ "format": "int32",
+ "type": "integer"
+ },
+ "hostIP": {
+ "description": "What host IP to bind the external port to.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "hostPort": {
+ "description": "Number of port to expose on the host. If specified, this must be a valid port number, 0 < x < 65536. If HostNetwork is specified, this must match ContainerPort. Most containers do not need this.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "name": {
+ "description": "If specified, this must be an IANA_SVC_NAME and unique within the pod. Each named port in a pod must have a unique name. Name for the port that can be referred to by services.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "protocol": {
+ "description": "Protocol for port. Must be UDP, TCP, or SCTP. Defaults to \"TCP\".",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "containerPort"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-list-map-keys": [
+ "containerPort",
+ "protocol"
+ ],
+ "x-kubernetes-list-type": "map",
+ "x-kubernetes-patch-merge-key": "containerPort",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "readinessProbe": {
+ "description": "Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.",
+ "properties": {
+ "exec": {
+ "description": "ExecAction describes a \"run in container\" action.",
+ "properties": {
+ "command": {
+ "description": "Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "failureThreshold": {
+ "description": "Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "grpc": {
+ "properties": {
+ "port": {
+ "description": "Port number of the gRPC service. Number must be in the range 1 to 65535.",
+ "format": "int32",
+ "type": "integer"
+ },
+ "service": {
+ "description": "Service is the name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md).\n\nIf this is not specified, the default behavior is defined by gRPC.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "httpGet": {
+ "description": "HTTPGetAction describes an action based on HTTP Get requests.",
+ "properties": {
+ "host": {
+ "description": "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "httpHeaders": {
+ "description": "Custom headers to set in the request. HTTP allows repeated headers.",
+ "items": {
+ "description": "HTTPHeader describes a custom header to be used in HTTP probes",
+ "properties": {
+ "name": {
+ "description": "The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.",
+ "type": "string"
+ },
+ "value": {
+ "description": "The header field value",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "Path to access on the HTTP server.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ },
+ "scheme": {
+ "description": "Scheme to use for connecting to the host. Defaults to HTTP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "initialDelaySeconds": {
+ "description": "Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "periodSeconds": {
+ "description": "How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "successThreshold": {
+ "description": "Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "tcpSocket": {
+ "description": "TCPSocketAction describes an action based on opening a socket",
+ "properties": {
+ "host": {
+ "description": "Optional: Host name to connect to, defaults to the pod IP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "terminationGracePeriodSeconds": {
+ "description": "Optional duration in seconds the pod needs to terminate gracefully upon probe failure. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this value overrides the value provided by the pod spec. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "timeoutSeconds": {
+ "description": "Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "resizePolicy": {
+ "description": "Resources resize policy for the container.",
+ "items": {
+ "description": "ContainerResizePolicy represents resource resize policy for the container.",
+ "properties": {
+ "resourceName": {
+ "description": "Name of the resource to which this resource resize policy applies. Supported values: cpu, memory.",
+ "type": "string"
+ },
+ "restartPolicy": {
+ "description": "Restart policy to apply when specified resource is resized. If not specified, it defaults to NotRequired.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "resourceName",
+ "restartPolicy"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-list-type": "atomic"
+ },
+ "resources": {
+ "description": "ResourceRequirements describes the compute resource requirements.",
+ "properties": {
+ "claims": {
+ "description": "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container.\n\nThis is an alpha field and requires enabling the DynamicResourceAllocation feature gate.\n\nThis field is immutable. It can only be set for containers.",
+ "items": {
+ "description": "ResourceClaim references one entry in PodSpec.ResourceClaims.",
+ "properties": {
+ "name": {
+ "description": "Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-list-map-keys": [
+ "name"
+ ],
+ "x-kubernetes-list-type": "map"
+ },
+ "limits": {
+ "additionalProperties": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ ]
+ },
+ "description": "Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/",
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "requests": {
+ "additionalProperties": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ ]
+ },
+ "description": "Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/",
+ "type": [
+ "object",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "restartPolicy": {
+ "description": "RestartPolicy defines the restart behavior of individual containers in a pod. This field may only be set for init containers, and the only allowed value is \"Always\". For non-init containers or when this field is not specified, the restart behavior is defined by the Pod's restart policy and the container type. Setting the RestartPolicy as \"Always\" for the init container will have the following effect: this init container will be continually restarted on exit until all regular containers have terminated. Once all regular containers have completed, all init containers with restartPolicy \"Always\" will be shut down. This lifecycle differs from normal init containers and is often referred to as a \"sidecar\" container. Although this init container still starts in the init container sequence, it does not wait for the container to complete before proceeding to the next init container. Instead, the next init container starts immediately after this init container is started, or after any startupProbe has successfully completed.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "securityContext": {
+ "description": "SecurityContext holds security configuration that will be applied to a container. Some fields are present in both SecurityContext and PodSecurityContext. When both are set, the values in SecurityContext take precedence.",
+ "properties": {
+ "allowPrivilegeEscalation": {
+ "description": "AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process. This bool directly controls if the no_new_privs flag will be set on the container process. AllowPrivilegeEscalation is true always when the container is: 1) run as Privileged 2) has CAP_SYS_ADMIN Note that this field cannot be set when spec.os.name is windows.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "capabilities": {
+ "description": "Adds and removes POSIX capabilities from running containers.",
+ "properties": {
+ "add": {
+ "description": "Added capabilities",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "drop": {
+ "description": "Removed capabilities",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "privileged": {
+ "description": "Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false. Note that this field cannot be set when spec.os.name is windows.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "procMount": {
+ "description": "procMount denotes the type of proc mount to use for the containers. The default is DefaultProcMount which uses the container runtime defaults for readonly paths and masked paths. This requires the ProcMountType feature flag to be enabled. Note that this field cannot be set when spec.os.name is windows.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "readOnlyRootFilesystem": {
+ "description": "Whether this container has a read-only root filesystem. Default is false. Note that this field cannot be set when spec.os.name is windows.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "runAsGroup": {
+ "description": "The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is windows.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "runAsNonRoot": {
+ "description": "Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "runAsUser": {
+ "description": "The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is windows.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "seLinuxOptions": {
+ "description": "SELinuxOptions are the labels to be applied to the container",
+ "properties": {
+ "level": {
+ "description": "Level is SELinux level label that applies to the container.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "role": {
+ "description": "Role is a SELinux role label that applies to the container.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": {
+ "description": "Type is a SELinux type label that applies to the container.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "user": {
+ "description": "User is a SELinux user label that applies to the container.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "seccompProfile": {
+ "description": "SeccompProfile defines a pod/container's seccomp profile settings. Only one profile source may be set.",
+ "properties": {
+ "localhostProfile": {
+ "description": "localhostProfile indicates a profile defined in a file on the node should be used. The profile must be preconfigured on the node to work. Must be a descending path, relative to the kubelet's configured seccomp profile location. Must be set if type is \"Localhost\". Must NOT be set for any other type.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": {
+ "description": "type indicates which kind of seccomp profile will be applied. Valid options are:\n\nLocalhost - a profile defined in a file on the node should be used. RuntimeDefault - the container runtime default profile should be used. Unconfined - no profile should be applied.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-unions": [
+ {
+ "discriminator": "type",
+ "fields-to-discriminateBy": {
+ "localhostProfile": "LocalhostProfile"
+ }
+ }
+ ],
+ "additionalProperties": false
+ },
+ "windowsOptions": {
+ "description": "WindowsSecurityContextOptions contain Windows-specific options and credentials.",
+ "properties": {
+ "gmsaCredentialSpec": {
+ "description": "GMSACredentialSpec is where the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the GMSA credential spec named by the GMSACredentialSpecName field.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "gmsaCredentialSpecName": {
+ "description": "GMSACredentialSpecName is the name of the GMSA credential spec to use.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "hostProcess": {
+ "description": "HostProcess determines if a container should be run as a 'Host Process' container. All of a Pod's containers must have the same effective HostProcess value (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). In addition, if HostProcess is true then HostNetwork must also be set to true.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "runAsUserName": {
+ "description": "The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "startupProbe": {
+ "description": "Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.",
+ "properties": {
+ "exec": {
+ "description": "ExecAction describes a \"run in container\" action.",
+ "properties": {
+ "command": {
+ "description": "Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "failureThreshold": {
+ "description": "Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "grpc": {
+ "properties": {
+ "port": {
+ "description": "Port number of the gRPC service. Number must be in the range 1 to 65535.",
+ "format": "int32",
+ "type": "integer"
+ },
+ "service": {
+ "description": "Service is the name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md).\n\nIf this is not specified, the default behavior is defined by gRPC.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "httpGet": {
+ "description": "HTTPGetAction describes an action based on HTTP Get requests.",
+ "properties": {
+ "host": {
+ "description": "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "httpHeaders": {
+ "description": "Custom headers to set in the request. HTTP allows repeated headers.",
+ "items": {
+ "description": "HTTPHeader describes a custom header to be used in HTTP probes",
+ "properties": {
+ "name": {
+ "description": "The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.",
+ "type": "string"
+ },
+ "value": {
+ "description": "The header field value",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "Path to access on the HTTP server.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ },
+ "scheme": {
+ "description": "Scheme to use for connecting to the host. Defaults to HTTP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "initialDelaySeconds": {
+ "description": "Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "periodSeconds": {
+ "description": "How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "successThreshold": {
+ "description": "Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "tcpSocket": {
+ "description": "TCPSocketAction describes an action based on opening a socket",
+ "properties": {
+ "host": {
+ "description": "Optional: Host name to connect to, defaults to the pod IP.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "port": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ ]
+ }
+ },
+ "required": [
+ "port"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "terminationGracePeriodSeconds": {
+ "description": "Optional duration in seconds the pod needs to terminate gracefully upon probe failure. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this value overrides the value provided by the pod spec. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "timeoutSeconds": {
+ "description": "Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "stdin": {
+ "description": "Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "stdinOnce": {
+ "description": "Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "terminationMessagePath": {
+ "description": "Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "terminationMessagePolicy": {
+ "description": "Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "tty": {
+ "description": "Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "volumeDevices": {
+ "description": "volumeDevices is the list of block devices to be used by the container.",
+ "items": {
+ "description": "volumeDevice describes a mapping of a raw block device within a container.",
+ "properties": {
+ "devicePath": {
+ "description": "devicePath is the path inside of the container that the device will be mapped to.",
+ "type": "string"
+ },
+ "name": {
+ "description": "name must match the name of a persistentVolumeClaim in the pod",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "devicePath"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-merge-key": "devicePath",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "volumeMounts": {
+ "description": "Pod volumes to mount into the container's filesystem. Cannot be updated.",
+ "items": {
+ "description": "VolumeMount describes a mounting of a Volume within a container.",
+ "properties": {
+ "mountPath": {
+ "description": "Path within the container at which the volume should be mounted. Must not contain ':'.",
+ "type": "string"
+ },
+ "mountPropagation": {
+ "description": "mountPropagation determines how mounts are propagated from the host to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "name": {
+ "description": "This must match the Name of a Volume.",
+ "type": "string"
+ },
+ "readOnly": {
+ "description": "Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "subPath": {
+ "description": "Path within the volume from which the container's volume should be mounted. Defaults to \"\" (volume's root).",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "subPathExpr": {
+ "description": "Expanded path within the volume from which the container's volume should be mounted. Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. Defaults to \"\" (volume's root). SubPathExpr and SubPath are mutually exclusive.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "name",
+ "mountPath"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-merge-key": "mountPath",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "workingDir": {
+ "description": "Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "name"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "nodeName": {
+ "description": "NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "nodeSelector": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "NodeSelector is a selector which must be true for the pod to fit on a node. Selector which must match a node's labels for the pod to be scheduled on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/",
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic"
+ },
+ "os": {
+ "description": "PodOS defines the OS parameters of a pod.",
+ "properties": {
+ "name": {
+ "description": "Name is the name of the operating system. The currently supported values are linux and windows. Additional value may be defined in future and can be one of: https://github.com/opencontainers/runtime-spec/blob/master/config.md#platform-specific-configuration Clients should expect to handle additional values and treat unrecognized values in this field as os: null",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "overhead": {
+ "additionalProperties": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ ]
+ },
+ "description": "Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md",
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "preemptionPolicy": {
+ "description": "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "priority": {
+ "description": "The priority value. Various system components use this field to find the priority of the pod. When Priority Admission Controller is enabled, it prevents users from setting this field. The admission controller populates this field from PriorityClassName. The higher the value, the higher the priority.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "priorityClassName": {
+ "description": "If specified, indicates the pod's priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "readinessGates": {
+ "description": "If specified, all readiness gates will be evaluated for pod readiness. A pod is ready when all its containers are ready AND all conditions specified in the readiness gates have status equal to \"True\" More info: https://git.k8s.io/enhancements/keps/sig-network/580-pod-readiness-gates",
+ "items": {
+ "description": "PodReadinessGate contains the reference to a pod condition",
+ "properties": {
+ "conditionType": {
+ "description": "ConditionType refers to a condition in the pod's condition list with matching type.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "conditionType"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "resourceClaims": {
+ "description": "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.\n\nThis is an alpha field and requires enabling the DynamicResourceAllocation feature gate.\n\nThis field is immutable.",
+ "items": {
+ "description": "PodResourceClaim references exactly one ResourceClaim through a ClaimSource. It adds a name to it that uniquely identifies the ResourceClaim inside the Pod. Containers that need access to the ResourceClaim reference it with this name.",
+ "properties": {
+ "name": {
+ "description": "Name uniquely identifies this resource claim inside the pod. This must be a DNS_LABEL.",
+ "type": "string"
+ },
+ "source": {
+ "description": "ClaimSource describes a reference to a ResourceClaim.\n\nExactly one of these fields should be set. Consumers of this type must treat an empty object as if it has an unknown value.",
+ "properties": {
+ "resourceClaimName": {
+ "description": "ResourceClaimName is the name of a ResourceClaim object in the same namespace as this pod.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "resourceClaimTemplateName": {
+ "description": "ResourceClaimTemplateName is the name of a ResourceClaimTemplate object in the same namespace as this pod.\n\nThe template will be used to create a new ResourceClaim, which will be bound to this pod. When this pod is deleted, the ResourceClaim will also be deleted. The pod name and resource name, along with a generated component, will be used to form a unique name for the ResourceClaim, which will be recorded in pod.status.resourceClaimStatuses.\n\nThis field is immutable and no changes will be made to the corresponding ResourceClaim by the control plane after creating the ResourceClaim.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "required": [
+ "name"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-list-map-keys": [
+ "name"
+ ],
+ "x-kubernetes-list-type": "map",
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge,retainKeys"
+ },
+ "restartPolicy": {
+ "description": "Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "runtimeClassName": {
+ "description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "schedulerName": {
+ "description": "If specified, the pod will be dispatched by specified scheduler. If not specified, the pod will be dispatched by default scheduler.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "schedulingGates": {
+ "description": "SchedulingGates is an opaque list of values that if specified will block scheduling the pod. If schedulingGates is not empty, the pod will stay in the SchedulingGated state and the scheduler will not attempt to schedule the pod.\n\nSchedulingGates can only be set at pod creation time, and be removed only afterwards.\n\nThis is a beta feature enabled by the PodSchedulingReadiness feature gate.",
+ "items": {
+ "description": "PodSchedulingGate is associated to a Pod to guard its scheduling.",
+ "properties": {
+ "name": {
+ "description": "Name of the scheduling gate. Each scheduling gate must have a unique name field.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-list-map-keys": [
+ "name"
+ ],
+ "x-kubernetes-list-type": "map",
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "securityContext": {
+ "description": "PodSecurityContext holds pod-level security attributes and common container settings. Some fields are also present in container.securityContext. Field values of container.securityContext take precedence over field values of PodSecurityContext.",
+ "properties": {
+ "fsGroup": {
+ "description": "A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to change the ownership of that volume to be owned by the pod:\n\n1. The owning GID will be the FSGroup 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) 3. The permission bits are OR'd with rw-rw----\n\nIf unset, the Kubelet will not modify the ownership and permissions of any volume. Note that this field cannot be set when spec.os.name is windows.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "fsGroupChangePolicy": {
+ "description": "fsGroupChangePolicy defines behavior of changing ownership and permission of the volume before being exposed inside Pod. This field will only apply to volume types which support fsGroup based ownership(and permissions). It will have no effect on ephemeral volume types such as: secret, configmaps and emptydir. Valid values are \"OnRootMismatch\" and \"Always\". If not specified, \"Always\" is used. Note that this field cannot be set when spec.os.name is windows.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "runAsGroup": {
+ "description": "The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container. Note that this field cannot be set when spec.os.name is windows.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "runAsNonRoot": {
+ "description": "Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "runAsUser": {
+ "description": "The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container. Note that this field cannot be set when spec.os.name is windows.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "seLinuxOptions": {
+ "description": "SELinuxOptions are the labels to be applied to the container",
+ "properties": {
+ "level": {
+ "description": "Level is SELinux level label that applies to the container.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "role": {
+ "description": "Role is a SELinux role label that applies to the container.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": {
+ "description": "Type is a SELinux type label that applies to the container.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "user": {
+ "description": "User is a SELinux user label that applies to the container.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "seccompProfile": {
+ "description": "SeccompProfile defines a pod/container's seccomp profile settings. Only one profile source may be set.",
+ "properties": {
+ "localhostProfile": {
+ "description": "localhostProfile indicates a profile defined in a file on the node should be used. The profile must be preconfigured on the node to work. Must be a descending path, relative to the kubelet's configured seccomp profile location. Must be set if type is \"Localhost\". Must NOT be set for any other type.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": {
+ "description": "type indicates which kind of seccomp profile will be applied. Valid options are:\n\nLocalhost - a profile defined in a file on the node should be used. RuntimeDefault - the container runtime default profile should be used. Unconfined - no profile should be applied.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "type"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-unions": [
+ {
+ "discriminator": "type",
+ "fields-to-discriminateBy": {
+ "localhostProfile": "LocalhostProfile"
+ }
+ }
+ ],
+ "additionalProperties": false
+ },
+ "supplementalGroups": {
+ "description": "A list of groups applied to the first process run in each container, in addition to the container's primary GID, the fsGroup (if specified), and group memberships defined in the container image for the uid of the container process. If unspecified, no additional groups are added to any container. Note that group memberships defined in the container image for the uid of the container process are still effective, even if they are not included in this list. Note that this field cannot be set when spec.os.name is windows.",
+ "items": {
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "sysctls": {
+ "description": "Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported sysctls (by the container runtime) might fail to launch. Note that this field cannot be set when spec.os.name is windows.",
+ "items": {
+ "description": "Sysctl defines a kernel parameter to be set",
+ "properties": {
+ "name": {
+ "description": "Name of a property to set",
+ "type": "string"
+ },
+ "value": {
+ "description": "Value of a property to set",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "windowsOptions": {
+ "description": "WindowsSecurityContextOptions contain Windows-specific options and credentials.",
+ "properties": {
+ "gmsaCredentialSpec": {
+ "description": "GMSACredentialSpec is where the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the GMSA credential spec named by the GMSACredentialSpecName field.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "gmsaCredentialSpecName": {
+ "description": "GMSACredentialSpecName is the name of the GMSA credential spec to use.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "hostProcess": {
+ "description": "HostProcess determines if a container should be run as a 'Host Process' container. All of a Pod's containers must have the same effective HostProcess value (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). In addition, if HostProcess is true then HostNetwork must also be set to true.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "runAsUserName": {
+ "description": "The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "serviceAccount": {
+ "description": "DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "serviceAccountName": {
+ "description": "ServiceAccountName is the name of the ServiceAccount to use to run this pod. More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "setHostnameAsFQDN": {
+ "description": "If true the pod's hostname will be configured as the pod's FQDN, rather than the leaf name (the default). In Linux containers, this means setting the FQDN in the hostname field of the kernel (the nodename field of struct utsname). In Windows containers, this means setting the registry value of hostname for the registry key HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters to FQDN. If a pod does not have FQDN, this has no effect. Default to false.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "shareProcessNamespace": {
+ "description": "Share a single process namespace between all of the containers in a pod. When this is set containers will be able to view and signal processes from other containers in the same pod, and the first process in each container will not be assigned PID 1. HostPID and ShareProcessNamespace cannot both be set. Optional: Default to false.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "subdomain": {
+ "description": "If specified, the fully qualified Pod hostname will be \"...svc.\". If not specified, the pod will not have a domainname at all.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "terminationGracePeriodSeconds": {
+ "description": "Optional duration in seconds the pod needs to terminate gracefully. May be decreased in delete request. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). If this value is nil, the default grace period will be used instead. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. Defaults to 30 seconds.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "tolerations": {
+ "description": "If specified, the pod's tolerations.",
+ "items": {
+ "description": "The pod this Toleration is attached to tolerates any taint that matches the triple using the matching operator .",
+ "properties": {
+ "effect": {
+ "description": "Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "key": {
+ "description": "Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "operator": {
+ "description": "Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "tolerationSeconds": {
+ "description": "TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "value": {
+ "description": "Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "topologySpreadConstraints": {
+ "description": "TopologySpreadConstraints describes how a group of pods ought to spread across topology domains. Scheduler will schedule pods in a way which abides by the constraints. All topologySpreadConstraints are ANDed.",
+ "items": {
+ "description": "TopologySpreadConstraint specifies how to spread matching pods among the given topology.",
+ "properties": {
+ "labelSelector": {
+ "description": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.",
+ "properties": {
+ "matchExpressions": {
+ "description": "matchExpressions is a list of label selector requirements. The requirements are ANDed.",
+ "items": {
+ "description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
+ "properties": {
+ "key": {
+ "description": "key is the label key that the selector applies to.",
+ "type": "string"
+ },
+ "operator": {
+ "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.",
+ "type": "string"
+ },
+ "values": {
+ "description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "key",
+ "operator"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "matchLabels": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.",
+ "type": [
+ "object",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "matchLabelKeys": {
+ "description": "MatchLabelKeys is a set of pod label keys to select the pods over which spreading will be calculated. The keys are used to lookup values from the incoming pod labels, those key-value labels are ANDed with labelSelector to select the group of existing pods over which spreading will be calculated for the incoming pod. The same key is forbidden to exist in both MatchLabelKeys and LabelSelector. MatchLabelKeys cannot be set when LabelSelector isn't set. Keys that don't exist in the incoming pod labels will be ignored. A null or empty list means only match against labelSelector.\n\nThis is a beta field and requires the MatchLabelKeysInPodTopologySpread feature gate to be enabled (enabled by default).",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-list-type": "atomic"
+ },
+ "maxSkew": {
+ "description": "MaxSkew describes the degree to which pods may be unevenly distributed. When `whenUnsatisfiable=DoNotSchedule`, it is the maximum permitted difference between the number of matching pods in the target topology and the global minimum. The global minimum is the minimum number of matching pods in an eligible domain or zero if the number of eligible domains is less than MinDomains. For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same labelSelector spread as 2/2/1: In this case, the global minimum is 1. | zone1 | zone2 | zone3 | | P P | P P | P | - if MaxSkew is 1, incoming pod can only be scheduled to zone3 to become 2/2/2; scheduling it onto zone1(zone2) would make the ActualSkew(3-1) on zone1(zone2) violate MaxSkew(1). - if MaxSkew is 2, incoming pod can be scheduled onto any zone. When `whenUnsatisfiable=ScheduleAnyway`, it is used to give higher precedence to topologies that satisfy it. It's a required field. Default value is 1 and 0 is not allowed.",
+ "format": "int32",
+ "type": "integer"
+ },
+ "minDomains": {
+ "description": "MinDomains indicates a minimum number of eligible domains. When the number of eligible domains with matching topology keys is less than minDomains, Pod Topology Spread treats \"global minimum\" as 0, and then the calculation of Skew is performed. And when the number of eligible domains with matching topology keys equals or greater than minDomains, this value has no effect on scheduling. As a result, when the number of eligible domains is less than minDomains, scheduler won't schedule more than maxSkew Pods to those domains. If value is nil, the constraint behaves as if MinDomains is equal to 1. Valid values are integers greater than 0. When value is not nil, WhenUnsatisfiable must be DoNotSchedule.\n\nFor example, in a 3-zone cluster, MaxSkew is set to 2, MinDomains is set to 5 and pods with the same labelSelector spread as 2/2/2: | zone1 | zone2 | zone3 | | P P | P P | P P | The number of domains is less than 5(MinDomains), so \"global minimum\" is treated as 0. In this situation, new pod with the same labelSelector cannot be scheduled, because computed skew will be 3(3 - 0) if new Pod is scheduled to any of the three zones, it will violate MaxSkew.\n\nThis is a beta field and requires the MinDomainsInPodTopologySpread feature gate to be enabled (enabled by default).",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "nodeAffinityPolicy": {
+ "description": "NodeAffinityPolicy indicates how we will treat Pod's nodeAffinity/nodeSelector when calculating pod topology spread skew. Options are: - Honor: only nodes matching nodeAffinity/nodeSelector are included in the calculations. - Ignore: nodeAffinity/nodeSelector are ignored. All nodes are included in the calculations.\n\nIf this value is nil, the behavior is equivalent to the Honor policy. This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "nodeTaintsPolicy": {
+ "description": "NodeTaintsPolicy indicates how we will treat node taints when calculating pod topology spread skew. Options are: - Honor: nodes without taints, along with tainted nodes for which the incoming pod has a toleration, are included. - Ignore: node taints are ignored. All nodes are included.\n\nIf this value is nil, the behavior is equivalent to the Ignore policy. This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "topologyKey": {
+ "description": "TopologyKey is the key of node labels. Nodes that have a label with this key and identical values are considered to be in the same topology. We consider each as a \"bucket\", and try to put balanced number of pods into each bucket. We define a domain as a particular instance of a topology. Also, we define an eligible domain as a domain whose nodes meet the requirements of nodeAffinityPolicy and nodeTaintsPolicy. e.g. If TopologyKey is \"kubernetes.io/hostname\", each Node is a domain of that topology. And, if TopologyKey is \"topology.kubernetes.io/zone\", each zone is a domain of that topology. It's a required field.",
+ "type": "string"
+ },
+ "whenUnsatisfiable": {
+ "description": "WhenUnsatisfiable indicates how to deal with a pod if it doesn't satisfy the spread constraint. - DoNotSchedule (default) tells the scheduler not to schedule it. - ScheduleAnyway tells the scheduler to schedule the pod in any location,\n but giving higher precedence to topologies that would help reduce the\n skew.\nA constraint is considered \"Unsatisfiable\" for an incoming pod if and only if every possible node assignment for that pod would violate \"MaxSkew\" on some topology. For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same labelSelector spread as 3/1/1: | zone1 | zone2 | zone3 | | P P P | P | P | If WhenUnsatisfiable is set to DoNotSchedule, incoming pod can only be scheduled to zone2(zone3) to become 3/2/1(3/1/2) as ActualSkew(2-1) on zone2(zone3) satisfies MaxSkew(1). In other words, the cluster can still be imbalanced, but scheduler won't make it *more* imbalanced. It's a required field.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "maxSkew",
+ "topologyKey",
+ "whenUnsatisfiable"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-list-map-keys": [
+ "topologyKey",
+ "whenUnsatisfiable"
+ ],
+ "x-kubernetes-list-type": "map",
+ "x-kubernetes-patch-merge-key": "topologyKey",
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "volumes": {
+ "description": "List of volumes that can be mounted by containers belonging to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes",
+ "items": {
+ "description": "Volume represents a named volume in a pod that may be accessed by any container in the pod.",
+ "properties": {
+ "awsElasticBlockStore": {
+ "description": "Represents a Persistent Disk resource in AWS.\n\nAn AWS EBS disk must exist before mounting to a container. The disk must also be in the same AWS zone as the kubelet. An AWS EBS disk can only be mounted as read/write once. AWS EBS volumes support ownership management and SELinux relabeling.",
+ "properties": {
+ "fsType": {
+ "description": "fsType is the filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "partition": {
+ "description": "partition is the partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify the partition as \"1\". Similarly, the volume partition for /dev/sda is \"0\" (or you can leave the property empty).",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "readOnly": {
+ "description": "readOnly value true will force the readOnly setting in VolumeMounts. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "volumeID": {
+ "description": "volumeID is unique ID of the persistent disk resource in AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore",
+ "type": "string"
+ }
+ },
+ "required": [
+ "volumeID"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "azureDisk": {
+ "description": "AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.",
+ "properties": {
+ "cachingMode": {
+ "description": "cachingMode is the Host Caching mode: None, Read Only, Read Write.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "diskName": {
+ "description": "diskName is the Name of the data disk in the blob storage",
+ "type": "string"
+ },
+ "diskURI": {
+ "description": "diskURI is the URI of data disk in the blob storage",
+ "type": "string"
+ },
+ "fsType": {
+ "description": "fsType is Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "kind": {
+ "description": "kind expected values are Shared: multiple blob disks per storage account Dedicated: single blob disk per storage account Managed: azure managed data disk (only in managed availability set). defaults to shared",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "readOnly": {
+ "description": "readOnly Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "diskName",
+ "diskURI"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "azureFile": {
+ "description": "AzureFile represents an Azure File Service mount on the host and bind mount to the pod.",
+ "properties": {
+ "readOnly": {
+ "description": "readOnly defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "secretName": {
+ "description": "secretName is the name of secret that contains Azure Storage Account Name and Key",
+ "type": "string"
+ },
+ "shareName": {
+ "description": "shareName is the azure share Name",
+ "type": "string"
+ }
+ },
+ "required": [
+ "secretName",
+ "shareName"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "cephfs": {
+ "description": "Represents a Ceph Filesystem mount that lasts the lifetime of a pod Cephfs volumes do not support ownership management or SELinux relabeling.",
+ "properties": {
+ "monitors": {
+ "description": "monitors is Required: Monitors is a collection of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": "array"
+ },
+ "path": {
+ "description": "path is Optional: Used as the mounted root, rather than the full Ceph tree, default is /",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "readOnly": {
+ "description": "readOnly is Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "secretFile": {
+ "description": "secretFile is Optional: SecretFile is the path to key ring for User, default is /etc/ceph/user.secret More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "secretRef": {
+ "description": "LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace.",
+ "properties": {
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "user": {
+ "description": "user is optional: User is the rados user name, default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "monitors"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "cinder": {
+ "description": "Represents a cinder volume resource in Openstack. A Cinder volume must exist before mounting to a container. The volume must also be in the same region as the kubelet. Cinder volumes support ownership management and SELinux relabeling.",
+ "properties": {
+ "fsType": {
+ "description": "fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "readOnly": {
+ "description": "readOnly defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/mysql-cinder-pd/README.md",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "secretRef": {
+ "description": "LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace.",
+ "properties": {
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "volumeID": {
+ "description": "volumeID used to identify the volume in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md",
+ "type": "string"
+ }
+ },
+ "required": [
+ "volumeID"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "configMap": {
+ "description": "Adapts a ConfigMap into a volume.\n\nThe contents of the target ConfigMap's Data field will be presented in a volume as files using the keys in the Data field as the file names, unless the items element is populated with specific mappings of keys to paths. ConfigMap volumes support ownership management and SELinux relabeling.",
+ "properties": {
+ "defaultMode": {
+ "description": "defaultMode is optional: mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "items": {
+ "description": "items if unspecified, each key-value pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the ConfigMap, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.",
+ "items": {
+ "description": "Maps a string key to a path within a volume.",
+ "properties": {
+ "key": {
+ "description": "key is the key to project.",
+ "type": "string"
+ },
+ "mode": {
+ "description": "mode is Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "path is the relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "key",
+ "path"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "optional": {
+ "description": "optional specify whether the ConfigMap or its keys must be defined",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "csi": {
+ "description": "Represents a source location of a volume to mount, managed by an external CSI driver",
+ "properties": {
+ "driver": {
+ "description": "driver is the name of the CSI driver that handles this volume. Consult with your admin for the correct name as registered in the cluster.",
+ "type": "string"
+ },
+ "fsType": {
+ "description": "fsType to mount. Ex. \"ext4\", \"xfs\", \"ntfs\". If not provided, the empty value is passed to the associated CSI driver which will determine the default filesystem to apply.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "nodePublishSecretRef": {
+ "description": "LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace.",
+ "properties": {
+ "name": {
+ "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "readOnly": {
+ "description": "readOnly specifies a read-only configuration for the volume. Defaults to false (read/write).",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ },
+ "volumeAttributes": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "volumeAttributes stores driver-specific properties that are passed to the CSI driver. Consult your driver's documentation for supported values.",
+ "type": [
+ "object",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "driver"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "downwardAPI": {
+ "description": "DownwardAPIVolumeSource represents a volume containing downward API info. Downward API volumes support ownership management and SELinux relabeling.",
+ "properties": {
+ "defaultMode": {
+ "description": "Optional: mode bits to use on created files by default. Must be a Optional: mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "items": {
+ "description": "Items is a list of downward API volume file",
+ "items": {
+ "description": "DownwardAPIVolumeFile represents information to create the file containing the pod field",
+ "properties": {
+ "fieldRef": {
+ "description": "ObjectFieldSelector selects an APIVersioned field of an object.",
+ "properties": {
+ "apiVersion": {
+ "description": "Version of the schema the FieldPath is written in terms of, defaults to \"v1\".",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "fieldPath": {
+ "description": "Path of the field to select in the specified API version.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "fieldPath"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ },
+ "mode": {
+ "description": "Optional: mode bits used to set permissions on this file, must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.",
+ "format": "int32",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "path": {
+ "description": "Required: Path is the relative path name of the file to be created. Must not be absolute or contain the '..' path. Must be utf-8 encoded. The first item of the relative path must not start with '..'",
+ "type": "string"
+ },
+ "resourceFieldRef": {
+ "description": "ResourceFieldSelector represents container resources (cpu, memory) and their output format",
+ "properties": {
+ "containerName": {
+ "description": "Container name: required for volumes, optional for env vars",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "divisor": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ ]
+ },
+ "resource": {
+ "description": "Required: resource to select",
+ "type": "string"
+ }
+ },
+ "required": [
+ "resource"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "x-kubernetes-map-type": "atomic",
+ "additionalProperties": false
+ }
+ },
+ "required": [
+ "path"
+ ],
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "emptyDir": {
+ "description": "Represents an empty directory for a pod. Empty directory volumes support ownership management and SELinux relabeling.",
+ "properties": {
+ "medium": {
+ "description": "medium represents what type of storage medium should back this directory. The default is \"\" which means to use the node's default medium. Must be an empty string (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "sizeLimit": {
+ "oneOf": [
+ {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ {
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ ]
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false
+ },
+ "ephemeral": {
+ "description": "Represents an ephemeral volume that is handled by a normal storage driver.",
+ "properties": {
+ "volumeClaimTemplate": {
+ "description": "PersistentVolumeClaimTemplate is used to produce PersistentVolumeClaim objects as part of an EphemeralVolumeSource.",
+ "properties": {
+ "metadata": {
+ "description": "ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.",
+ "properties": {
+ "annotations": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations",
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "creationTimestamp": {
+ "description": "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.",
+ "format": "date-time",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "deletionGracePeriodSeconds": {
+ "description": "Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "deletionTimestamp": {
+ "description": "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.",
+ "format": "date-time",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "finalizers": {
+ "description": "Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed. Finalizers may be processed and removed in any order. Order is NOT enforced because it introduces significant risk of stuck finalizers. finalizers is a shared field, any actor with permission can reorder it. If the finalizer list is processed in order, then this can lead to a situation in which the component responsible for the first finalizer in the list is waiting for a signal (field value, external system, or other) produced by a component responsible for a finalizer later in the list, resulting in a deadlock. Without enforced ordering finalizers are free to order amongst themselves and are not vulnerable to ordering changes in the list.",
+ "items": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "type": [
+ "array",
+ "null"
+ ],
+ "x-kubernetes-patch-strategy": "merge"
+ },
+ "generateName": {
+ "description": "GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\n\nIf this field is specified and the generated name exists, the server will return a 409.\n\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "generation": {
+ "description": "A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.",
+ "format": "int64",
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "labels": {
+ "additionalProperties": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "description": "Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels",
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "managedFields": {
+ "description": "ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like \"ci-cd\". The set of fields is always in the version that the workflow used when modifying the object.",
+ "items": {
+ "description": "ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource that the fieldset applies to.",
+ "properties": {
+ "apiVersion": {
+ "description": "APIVersion defines the version of this resource that this field set applies to. The format is \"group/version\" just like the top-level APIVersion field. It is necessary to track the version of a field set because it cannot be automatically converted.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "fieldsType": {
+ "description": "FieldsType is the discriminator for the different fields format and version. There is currently only one possible value: \"FieldsV1\"",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "fieldsV1": {
+ "description": "FieldsV1 stores a set of fields in a data structure like a Trie, in JSON format.\n\nEach key is either a '.' representing the field itself, and will always map to an empty set, or a string representing a sub-field or item. The string will follow one of these four formats: 'f:', where is the name of a field in a struct, or key in a map 'v:', where is the exact json formatted value of a list item 'i:', where