Add user dashboard-side validation for deployments/configmaps in recipes
Add validation and processing on the dashboard side for recipes containing deployments and/or configmaps. This allows users to create new stacks using these items in recipes. Signed-off-by: Angel Misevski <amisevsk@redhat.com>6.19.x
parent
129788ba31
commit
e0f774c456
|
|
@ -13,8 +13,8 @@
|
|||
|
||||
import {KubernetesEnvironmentManager} from './kubernetes-environment-manager';
|
||||
import {IEnvironmentManagerMachine, IEnvironmentManagerMachineServer} from './environment-manager-machine';
|
||||
import {IPodList} from './kubernetes-environment-recipe-parser';
|
||||
import {IPodItem, IPodItemContainer} from './kubernetes-machine-recipe-parser';
|
||||
import {ISupportedItemList} from './kubernetes-environment-recipe-parser';
|
||||
import {IPodItem, IPodItemContainer, getPodItemOrNull, ISupportedListItem} from './kubernetes-machine-recipe-parser';
|
||||
import {CheRecipeTypes} from '../recipe/che-recipe-types';
|
||||
|
||||
/**
|
||||
|
|
@ -104,11 +104,15 @@ describe('KubernetesEnvironmentManager', () => {
|
|||
|
||||
let getEnvironmentSource = (environment: che.IWorkspaceEnvironment, machine: IEnvironmentManagerMachine): string => {
|
||||
const [podName, containerName] = machine.name.split(/\//);
|
||||
const recipe: IPodList = jsyaml.load(environment.recipe.content);
|
||||
const machinePodItem = recipe.items.find((machinePodItem: IPodItem) => {
|
||||
const recipe: ISupportedItemList = jsyaml.load(environment.recipe.content);
|
||||
const machinePodItem = getPodItemOrNull(recipe.items.find((item: ISupportedListItem) => {
|
||||
const machinePodItem = getPodItemOrNull(item);
|
||||
if (!machinePodItem) {
|
||||
return false;
|
||||
}
|
||||
const podItemName = machinePodItem.metadata.name ? machinePodItem.metadata.name : machinePodItem.metadata.generateName;
|
||||
return podItemName === podName;
|
||||
});
|
||||
}));
|
||||
const podContainer = machinePodItem.spec.containers.find((podContainer: IPodItemContainer) => {
|
||||
return podContainer.name === containerName;
|
||||
});
|
||||
|
|
@ -187,11 +191,16 @@ describe('KubernetesEnvironmentManager', () => {
|
|||
let getEnvironmentSource = (environment: che.IWorkspaceEnvironment, machine: IEnvironmentManagerMachine): string => {
|
||||
const podName = machine.recipe.metadata.name;
|
||||
const containerName = machine.recipe.spec.containers[0].name;
|
||||
const recipe: IPodList = jsyaml.load(environment.recipe.content);
|
||||
const machinePodItem = recipe.items.find((machinePodItem: IPodItem) => {
|
||||
const recipe: ISupportedItemList = jsyaml.load(environment.recipe.content);
|
||||
const machinePodItem = getPodItemOrNull(recipe.items.find((item: ISupportedListItem) => {
|
||||
const machinePodItem = getPodItemOrNull(item);
|
||||
if (!machinePodItem) {
|
||||
return false;
|
||||
}
|
||||
const podItemName = machinePodItem.metadata.name ? machinePodItem.metadata.name : machinePodItem.metadata.generateName;
|
||||
return podItemName === podName;
|
||||
});
|
||||
}));
|
||||
|
||||
const podContainer = machinePodItem.spec.containers.find((podContainer: IPodItemContainer) => {
|
||||
return podContainer.name === containerName;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@
|
|||
import {EnvironmentManager} from './environment-manager';
|
||||
import {IEnvironmentManagerMachine} from './environment-manager-machine';
|
||||
import {CheRecipeTypes} from '../recipe/che-recipe-types';
|
||||
import {IPodList, KubernetesEnvironmentRecipeParser} from './kubernetes-environment-recipe-parser';
|
||||
import {IPodItem, IPodItemContainer, KubernetesMachineRecipeParser} from './kubernetes-machine-recipe-parser';
|
||||
import {ISupportedItemList, KubernetesEnvironmentRecipeParser} from './kubernetes-environment-recipe-parser';
|
||||
import {ISupportedListItem, IPodItem, IPodItemContainer, KubernetesMachineRecipeParser, getPodItemOrNull} from './kubernetes-machine-recipe-parser';
|
||||
|
||||
enum MemoryUnit { 'B', 'Ki', 'Mi', 'Gi' }
|
||||
|
||||
|
|
@ -73,19 +73,19 @@ export class KubernetesEnvironmentManager extends EnvironmentManager {
|
|||
/**
|
||||
* Parses machine recipe content.
|
||||
* @param content {string} recipe content
|
||||
* @returns {IPodItem} recipe object
|
||||
* @returns {ISupportedListItem} recipe object
|
||||
*/
|
||||
parseMachineRecipe(content: string): IPodItem {
|
||||
parseMachineRecipe(content: string): ISupportedListItem {
|
||||
return this.machineParser.parse(content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses recipe content.
|
||||
* @param content {string} recipe content
|
||||
* @returns {IPodList} recipe object
|
||||
* @returns {ISupportedItemList} recipe object
|
||||
*/
|
||||
parseRecipe(content: string): IPodList {
|
||||
let recipe: IPodList;
|
||||
parseRecipe(content: string): ISupportedItemList {
|
||||
let recipe: ISupportedItemList;
|
||||
try {
|
||||
recipe = this.parser.parse(content);
|
||||
} catch (e) {
|
||||
|
|
@ -96,10 +96,10 @@ export class KubernetesEnvironmentManager extends EnvironmentManager {
|
|||
|
||||
/**
|
||||
* Dumps recipe object.
|
||||
* @param recipe {IPodList} recipe object
|
||||
* @param recipe {ISupportedItemList} recipe object
|
||||
* @returns {string} recipe content
|
||||
*/
|
||||
stringifyRecipe(recipe: IPodList): string {
|
||||
stringifyRecipe(recipe: ISupportedItemList): string {
|
||||
return this.parser.dump(recipe);
|
||||
}
|
||||
|
||||
|
|
@ -115,16 +115,18 @@ export class KubernetesEnvironmentManager extends EnvironmentManager {
|
|||
return machines;
|
||||
}
|
||||
|
||||
const recipe: IPodList = this.parseRecipe(environment.recipe.content);
|
||||
const recipe: ISupportedItemList = this.parseRecipe(environment.recipe.content);
|
||||
if (!recipe) {
|
||||
this.$log.error('EnvironmentManager: cannot parse recipe.');
|
||||
return machines;
|
||||
}
|
||||
|
||||
recipe.items.forEach((podItem: IPodItem) => {
|
||||
if (!podItem || podItem.kind !== POD || !podItem.metadata.name && podItem.spec || !angular.isArray(podItem.spec.containers)) {
|
||||
recipe.items.forEach((item: ISupportedListItem) => {
|
||||
const podItem = getPodItemOrNull(item);
|
||||
if (!podItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
const annotations = podItem.metadata.annotations;
|
||||
podItem.spec.containers.forEach((container: IPodItemContainer) => {
|
||||
if (!container || !container.name) {
|
||||
|
|
@ -208,7 +210,8 @@ export class KubernetesEnvironmentManager extends EnvironmentManager {
|
|||
}
|
||||
machines.forEach((machine: IEnvironmentManagerMachine) => {
|
||||
let containerName: string;
|
||||
let pod = recipe.items.find((podItem: IPodItem) => {
|
||||
let podOrDeployment = recipe.items.find((item: ISupportedListItem) => {
|
||||
const podItem = getPodItemOrNull(item);
|
||||
if (!podItem || !podItem.metadata) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -231,6 +234,7 @@ export class KubernetesEnvironmentManager extends EnvironmentManager {
|
|||
return podItemName === podName;
|
||||
});
|
||||
|
||||
const pod = getPodItemOrNull(podOrDeployment);
|
||||
if (pod && pod.kind === POD && pod.metadata.name && pod.spec && angular.isArray(pod.spec.containers)) {
|
||||
const containerIndex = pod.spec.containers.findIndex((container: IPodItemContainer) => {
|
||||
return container.name === containerName;
|
||||
|
|
@ -324,7 +328,8 @@ export class KubernetesEnvironmentManager extends EnvironmentManager {
|
|||
let pod;
|
||||
let containerName: string;
|
||||
if (recipe && recipe.kind === LIST && angular.isArray(recipe.items)) {
|
||||
pod = recipe.items.find((podItem: IPodItem) => {
|
||||
pod = getPodItemOrNull(recipe.items.find((item: ISupportedListItem) => {
|
||||
const podItem = getPodItemOrNull(item);
|
||||
if (!podItem || podItem.kind !== POD || !podItem.metadata.name || !podItem.spec || !angular.isArray(podItem.spec.containers)) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -347,7 +352,7 @@ export class KubernetesEnvironmentManager extends EnvironmentManager {
|
|||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}));
|
||||
}
|
||||
if (!pod) {
|
||||
this.$log.error('EnvironmentManager: cannot rename machine.');
|
||||
|
|
@ -439,8 +444,9 @@ export class KubernetesEnvironmentManager extends EnvironmentManager {
|
|||
const nameAnnotation = `${NAME_ANNOTATION_PREFIX}.${machineRecipe.spec.containers[0].name}.${MACHINE_NAME}`;
|
||||
if (angular.isArray(recipe.items)) {
|
||||
const machineRecipePod = this.getMachinePod(machineRecipe);
|
||||
const usedPodIndex = recipe.items.findIndex((pod: IPodItem) => {
|
||||
if (!pod.kind || pod.kind !== POD || pod.metadata.name !== machineRecipe.metadata.name) {
|
||||
const usedPodIndex = recipe.items.findIndex((item: ISupportedListItem) => {
|
||||
const pod = getPodItemOrNull(item);
|
||||
if (!pod) {
|
||||
return false;
|
||||
}
|
||||
return machineRecipePod.metadata.name === this.getMachinePod(pod).metadata.name;
|
||||
|
|
@ -448,7 +454,7 @@ export class KubernetesEnvironmentManager extends EnvironmentManager {
|
|||
if (usedPodIndex === -1) {
|
||||
recipe.items.push(machineRecipe);
|
||||
} else {
|
||||
recipe.items[usedPodIndex].spec.containers.push(machineRecipe.spec.containers[0]);
|
||||
getPodItemOrNull(recipe.items[usedPodIndex]).spec.containers.push(machineRecipe.spec.containers[0]);
|
||||
}
|
||||
|
||||
// update machine name
|
||||
|
|
@ -506,8 +512,9 @@ export class KubernetesEnvironmentManager extends EnvironmentManager {
|
|||
let podIndex: number;
|
||||
let containerName: string;
|
||||
if (envRecipe && envRecipe.kind === LIST && angular.isArray(envRecipe.items)) {
|
||||
podIndex = envRecipe.items.findIndex((podItem: IPodItem) => {
|
||||
if (!podItem || podItem.kind !== POD || !podItem.metadata.name || !podItem.spec || !angular.isArray(podItem.spec.containers)) {
|
||||
podIndex = envRecipe.items.findIndex((item: ISupportedListItem) => {
|
||||
const podItem = getPodItemOrNull(item);
|
||||
if (!podItem || !podItem.metadata.name || !podItem.spec || !angular.isArray(podItem.spec.containers)) {
|
||||
return false;
|
||||
}
|
||||
const containerIndex = podItem.spec.containers.findIndex((container: IPodItemContainer) => {
|
||||
|
|
@ -531,7 +538,7 @@ export class KubernetesEnvironmentManager extends EnvironmentManager {
|
|||
});
|
||||
}
|
||||
if (podIndex > -1) {
|
||||
const podItem = envRecipe.items[podIndex];
|
||||
const podItem = getPodItemOrNull(envRecipe.items[podIndex]);
|
||||
if (podItem && podItem.kind === POD && podItem.metadata.name && podItem.spec && angular.isArray(podItem.spec.containers)) {
|
||||
if (podItem.spec.containers.length) {
|
||||
const containerIndex = podItem.spec.containers.findIndex((podItemContainer: IPodItemContainer) => {
|
||||
|
|
|
|||
|
|
@ -10,12 +10,12 @@
|
|||
* Red Hat, Inc. - initial API and implementation
|
||||
*/
|
||||
'use strict';
|
||||
import {IPodItem, KubernetesMachineRecipeParser} from './kubernetes-machine-recipe-parser';
|
||||
import {ISupportedListItem, KubernetesMachineRecipeParser} from './kubernetes-machine-recipe-parser';
|
||||
import {IParser} from './parser';
|
||||
|
||||
export interface IPodList {
|
||||
export interface ISupportedItemList {
|
||||
kind: string;
|
||||
items: Array<IPodItem>;
|
||||
items: Array<ISupportedListItem>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -25,16 +25,16 @@ export interface IPodList {
|
|||
*/
|
||||
export class KubernetesEnvironmentRecipeParser implements IParser {
|
||||
private machineRecipeParser = new KubernetesMachineRecipeParser();
|
||||
private recipeByContent: Map<string, IPodList> = new Map();
|
||||
private recipeByContent: Map<string, ISupportedItemList> = new Map();
|
||||
private recipeKeys: Array<string> = [];
|
||||
|
||||
/**
|
||||
* Parses recipe content
|
||||
* @param content {string} recipe content
|
||||
* @returns {IPodList} recipe object
|
||||
* @returns {ISupportedItemList} recipe object
|
||||
*/
|
||||
parse(content: string): IPodList {
|
||||
let recipe: IPodList;
|
||||
parse(content: string): ISupportedItemList {
|
||||
let recipe: ISupportedItemList;
|
||||
if (this.recipeByContent.has(content)) {
|
||||
recipe = angular.copy(this.recipeByContent.get(content));
|
||||
this.validate(recipe);
|
||||
|
|
@ -54,40 +54,40 @@ export class KubernetesEnvironmentRecipeParser implements IParser {
|
|||
|
||||
/**
|
||||
* Dumps recipe object.
|
||||
* @param recipe {IPodList} recipe object
|
||||
* @param recipe {ISupportedItemList} recipe object
|
||||
* @returns {string} recipe content
|
||||
*/
|
||||
dump(recipe: IPodList): string {
|
||||
dump(recipe: ISupportedItemList): string {
|
||||
return jsyaml.safeDump(recipe, {'indent': 1});
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple validation of recipe.
|
||||
* @param recipe {IPodList}
|
||||
* @param recipe {ISupportedItemList}
|
||||
*/
|
||||
private validate(recipe: IPodList): void {
|
||||
private validate(recipe: ISupportedItemList): void {
|
||||
if (!recipe || !recipe.kind) {
|
||||
throw new TypeError(`Recipe should contain a 'kind' section.`);
|
||||
}
|
||||
if (recipe.kind.toLowerCase() !== 'list') {
|
||||
throw new TypeError(`Recipe 'kind' section should be equals 'list'.`);
|
||||
}
|
||||
const podItems = recipe.items;
|
||||
if (!podItems) {
|
||||
throw new TypeError(`Recipe pod list should contain an 'items' section.`);
|
||||
const items = recipe.items;
|
||||
if (!items) {
|
||||
throw new TypeError(`Recipe kubernetes list should contain an 'items' section.`);
|
||||
}
|
||||
if (!angular.isArray(podItems) || podItems.length === 0) {
|
||||
throw new TypeError(`Recipe pod list should contain at least one 'item'.`);
|
||||
if (!angular.isArray(items) || items.length === 0) {
|
||||
throw new TypeError(`Recipe kubernetes list should contain at least one 'item'.`);
|
||||
} else {
|
||||
podItems.forEach((podItem: IPodItem) => {
|
||||
if (!podItem) {
|
||||
items.forEach((item: ISupportedListItem) => {
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
// skip services
|
||||
if (podItem.kind && podItem.kind.toLowerCase() === 'service') {
|
||||
if (item.kind && item.kind.toLowerCase() === 'service') {
|
||||
return;
|
||||
}
|
||||
this.machineRecipeParser.validate(podItem);
|
||||
this.machineRecipeParser.validate(item);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,15 +13,55 @@
|
|||
|
||||
import {IParser} from './parser';
|
||||
|
||||
export type ISupportedListItem = IPodItem | IDeploymentItem | IConfigMapItem;
|
||||
|
||||
export function isDeploymentItem(item: ISupportedListItem): item is IDeploymentItem {
|
||||
return (item.kind && item.kind.toLowerCase() === 'deployment');
|
||||
}
|
||||
|
||||
export function isPodItem(item: ISupportedListItem): item is IPodItem {
|
||||
return (item.kind && item.kind.toLowerCase() === 'pod');
|
||||
}
|
||||
|
||||
export function isConfigMapItem(item: ISupportedListItem): item is IConfigMapItem {
|
||||
return (item.kind && item.kind.toLowerCase() === 'configmap');
|
||||
}
|
||||
|
||||
export function getPodItemOrNull(item: ISupportedListItem): IPodItem {
|
||||
if (isDeploymentItem(item)) {
|
||||
return item.spec.template;
|
||||
} else if (isPodItem(item)) {
|
||||
return item;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IObjectMetadata {
|
||||
name?: string;
|
||||
generateName?: string;
|
||||
annotations?: { [propName: string]: string };
|
||||
labels?: { [propName: string ]: string };
|
||||
[propName: string]: string | Object;
|
||||
}
|
||||
|
||||
export interface IDeploymentItem {
|
||||
apiVersion: string;
|
||||
kind: string;
|
||||
metadata: IObjectMetadata;
|
||||
spec: {
|
||||
replicas: number;
|
||||
selector: {
|
||||
matchLabels: { [propName: string]: string | Object };
|
||||
}
|
||||
template: IPodItem
|
||||
};
|
||||
}
|
||||
|
||||
export interface IPodItem {
|
||||
apiVersion: string;
|
||||
kind: string;
|
||||
metadata: {
|
||||
name?: string;
|
||||
generateName?: string;
|
||||
annotations?: { [propName: string]: string };
|
||||
[propName: string]: string | Object;
|
||||
};
|
||||
metadata: IObjectMetadata;
|
||||
spec: { containers: Array<IPodItemContainer> };
|
||||
[propName: string]: string | Object;
|
||||
}
|
||||
|
|
@ -37,23 +77,30 @@ export interface IPodItemContainer {
|
|||
[propName: string]: string | Object;
|
||||
}
|
||||
|
||||
export interface IConfigMapItem {
|
||||
apiVersion: string;
|
||||
kind: string;
|
||||
metadata: IObjectMetadata;
|
||||
data: { [propName: string]: string | Object };
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for jsyaml and simple validator for kubernetes machine recipe.
|
||||
*
|
||||
* @author Oleksii Orel
|
||||
*/
|
||||
export class KubernetesMachineRecipeParser implements IParser {
|
||||
private recipeByContent: Map<string, IPodItem> = new Map();
|
||||
private recipeByContent: Map<string, ISupportedListItem> = new Map();
|
||||
private recipeKeys: Array<string> = [];
|
||||
|
||||
/**
|
||||
* Parses recipe content
|
||||
*
|
||||
* @param content {string} recipe content
|
||||
* @returns {IPodItem} recipe object
|
||||
* @returns {ISupportedListItem} recipe object
|
||||
*/
|
||||
parse(content: string): IPodItem {
|
||||
let recipe: IPodItem;
|
||||
parse(content: string): ISupportedListItem {
|
||||
let recipe: ISupportedListItem;
|
||||
if (this.recipeByContent.has(content)) {
|
||||
recipe = angular.copy(this.recipeByContent.get(content));
|
||||
this.validate(recipe);
|
||||
|
|
@ -74,47 +121,87 @@ export class KubernetesMachineRecipeParser implements IParser {
|
|||
/**
|
||||
* Dumps recipe object.
|
||||
*
|
||||
* @param recipe {IPodItem} recipe object
|
||||
* @param recipe {IPodItem | IDeploymentItem | IConfigMapItem} recipe object
|
||||
* @returns {string} recipe content
|
||||
*/
|
||||
dump(recipe: IPodItem): string {
|
||||
dump(recipe: ISupportedListItem): string {
|
||||
return jsyaml.safeDump(recipe, {'indent': 1});
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple validation of machine recipe.
|
||||
*
|
||||
* @param recipe {IPodItem}
|
||||
*/
|
||||
validate(recipe: IPodItem): void {
|
||||
validate(recipe: ISupportedListItem ): void {
|
||||
if (!recipe || !recipe.kind) {
|
||||
throw new TypeError(`Recipe should contain a 'kind' section.`);
|
||||
}
|
||||
if (recipe.kind.toLowerCase() !== 'pod') {
|
||||
throw new TypeError(`Recipe 'kind' section should be equals 'pod'.`);
|
||||
throw new TypeError(`Recipe item should contain a 'kind' section.`);
|
||||
}
|
||||
if (!recipe.apiVersion) {
|
||||
throw new TypeError(`Recipe pod item should contain 'apiVersion' section.`);
|
||||
throw new TypeError(`Recipe item should contain 'apiVersion' section`);
|
||||
}
|
||||
if (!recipe.metadata) {
|
||||
throw new TypeError(`Recipe pod item should contain 'metadata' section.`);
|
||||
if (isDeploymentItem(recipe)) {
|
||||
this.validateDeployment(<IDeploymentItem>recipe);
|
||||
} else if (isPodItem(recipe)) {
|
||||
this.validatePod(<IPodItem>recipe);
|
||||
} else if (isConfigMapItem(recipe)) {
|
||||
this.validateConfigMap(<IConfigMapItem>recipe);
|
||||
}
|
||||
if (!recipe.metadata.name && !recipe.metadata.generateName) {
|
||||
throw new TypeError(`Recipe pod item metadata should contain 'name' section.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple validation of Deployment recipe.
|
||||
*
|
||||
* @param deployment
|
||||
*/
|
||||
validateDeployment(deployment: IDeploymentItem): void {
|
||||
this.validateMetadata(deployment.metadata);
|
||||
if (!deployment.spec) {
|
||||
throw new TypeError(`Recipe deployment item should contain a 'spec' section.`);
|
||||
}
|
||||
if (recipe.metadata.name && !this.testName(recipe.metadata.name)) {
|
||||
throw new TypeError(`Recipe pod item container name should not contain special characters like dollar, etc.`);
|
||||
const spec = deployment.spec;
|
||||
if (!spec.replicas || spec.replicas !== 1) {
|
||||
throw new TypeError(`Recipe deployment spec should contain replicas value equal to 1.`);
|
||||
}
|
||||
if (!recipe.spec) {
|
||||
if (!spec.template) {
|
||||
throw new TypeError(`Recipe deployment spec should contain template section.`);
|
||||
}
|
||||
if (!spec.selector || !spec.selector.matchLabels) {
|
||||
throw new TypeError(`Recipe deployment spec should contain selector section.`);
|
||||
}
|
||||
this.validatePod(spec.template);
|
||||
// for the deployment to work, the matchlabels section needs to match the labels
|
||||
// applied to the Pod.
|
||||
const matchLabels = spec.selector.matchLabels;
|
||||
const podLabels = spec.template.metadata.labels;
|
||||
if (!podLabels) {
|
||||
throw new TypeError(`Recipe deployment spec matchLabels must match pod labels.`);
|
||||
}
|
||||
for (const key of Object.keys(matchLabels)) {
|
||||
if (matchLabels[key] !== podLabels[key]) {
|
||||
throw new TypeError(`Recipe deployment matchLabels must match pod labels.`);
|
||||
}
|
||||
}
|
||||
// since match labels are ANDed, we also need the same labels
|
||||
for (const key of Object.keys(podLabels)) {
|
||||
if (podLabels[key] !== matchLabels[key]) {
|
||||
throw new TypeError(`Recipe deployment matchLabels must match pod labels.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple validation of Pod recipe.
|
||||
*
|
||||
* @param pod {IPodItem}
|
||||
*/
|
||||
validatePod(pod: IPodItem): void {
|
||||
this.validateMetadata(pod.metadata);
|
||||
if (!pod.spec) {
|
||||
throw new TypeError(`Recipe pod item should contain 'spec' section.`);
|
||||
}
|
||||
if (!recipe.spec.containers) {
|
||||
if (!pod.spec.containers) {
|
||||
throw new TypeError(`Recipe pod item spec should contain 'containers' section.`);
|
||||
}
|
||||
if (!angular.isArray(recipe.spec.containers) || recipe.spec.containers.length === 0) {
|
||||
if (!angular.isArray(pod.spec.containers) || pod.spec.containers.length === 0) {
|
||||
throw new TypeError(`Recipe pod item spec containers should contain at least one 'container'.`);
|
||||
}
|
||||
recipe.spec.containers.forEach((podItemContainer: IPodItemContainer) => {
|
||||
pod.spec.containers.forEach((podItemContainer: IPodItemContainer) => {
|
||||
if (!podItemContainer) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -128,7 +215,25 @@ export class KubernetesMachineRecipeParser implements IParser {
|
|||
throw new TypeError(`Recipe pod item container should contain 'image' section.`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
validateConfigMap(configMap: IConfigMapItem) {
|
||||
this.validateMetadata(configMap.metadata);
|
||||
if (!configMap.data) {
|
||||
throw new TypeError(`Recipe config map item should contain data section.`);
|
||||
}
|
||||
}
|
||||
|
||||
validateMetadata(metadata: IObjectMetadata) {
|
||||
if (!metadata) {
|
||||
throw new TypeError(`Recipe item should contain 'metadata' section.`);
|
||||
}
|
||||
if (!metadata.name && !metadata.generateName) {
|
||||
throw new TypeError(`Recipe item metadata should contain 'name' section.`);
|
||||
}
|
||||
if (metadata.name && !this.testName(metadata.name)) {
|
||||
throw new TypeError(`Recipe item container name should not contain special characters like dollar, etc.`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in New Issue