Skip to content

Commit

Permalink
Added missing JDL deployment options (#14945)
Browse files Browse the repository at this point in the history
* Removed unused deployment option

* Added missing jdl deployment options

fixes #14817

* Added missing jdl deployment options

fixes #14817

* Added validations for docker-compose-related deployments

* Added validations for kubernetes-related deployments

* Added validations for openshift-related deployments

* Fixed the kubernetesStorageClassName option
  • Loading branch information
MathieuAA authored Jun 9, 2021
1 parent e5d3b0d commit 4bffad3
Show file tree
Hide file tree
Showing 12 changed files with 505 additions and 89 deletions.
135 changes: 89 additions & 46 deletions jdl/jhipster/deployment-options.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,82 +16,125 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const _ = require('lodash');

const DeploymentTypes = {
DOCKERCOMPOSE: 'docker-compose',
KUBERNETES: 'kubernetes',
OPENSHIFT: 'openshift',
exists: deploymentType => !!deploymentType && !!DeploymentTypes[deploymentType.toUpperCase().replace('-', '')],
};

const Options = {
deploymentType: {
dockerCompose: DeploymentTypes.DOCKERCOMPOSE,
rancherCompose: DeploymentTypes.RANCHERCOMPOSE,
kubernetes: DeploymentTypes.KUBERNETES,
openshift: DeploymentTypes.OPENSHIFT,
},
gatewayType: {
springCloudGateway: 'SpringCloudGateway',
},
monitoring: {
no: 'no',
prometheus: 'prometheus',
},
directoryPath: '../',
appsFolders: [],
clusteredDbApps: [],
// adminPassword: 'admin',
serviceDiscoveryType: {
eureka: 'eureka',
consul: 'consul',
no: 'no',
},
dockerRepositoryName: '',
dockerPushCommand: 'docker push',
// Kubernetes specific
const kubernetesRelatedOptions = {
kubernetesNamespace: 'default',
kubernetesServiceType: {
loadBalancer: 'LoadBalancer',
nodePort: 'NodePort',
ingress: 'Ingress',
},
kubernetesStorageClassName: '',
kubernetesUseDynamicStorage: {
false: false,
true: true,
},
ingressDomain: '',
ingressType: {
nginx: 'nginx',
gke: 'gke',
},
istio: {
false: false,
true: true,
},
// openshift specific
};

const openshiftRelatedOptions = {
openshiftNamespace: 'default',
registryReplicas: {
two: 2,
},
storageType: {
ephemeral: 'ephemeral',
persistent: 'persistent',
},
};

Options.defaults = (deploymentType = Options.deploymentType.dockerCompose) =>
_.omitBy(
{
deploymentType,
gatewayType: Options.gatewayType.springCloudGateway,
monitoring: Options.monitoring.no,
directoryPath: Options.directoryPath,
const dockerComposeRelatedOptions = {
gatewayType: {
springCloudGateway: 'SpringCloudGateway',
},
};

const baseOptions = {
appsFolders: [],
clusteredDbApps: [],
directoryPath: '../',
monitoring: {
no: 'no',
prometheus: 'prometheus',
},
serviceDiscoveryType: {
eureka: 'eureka',
consul: 'consul',
no: 'no',
},
};

const Options = {
...baseOptions,
deploymentType: {
dockerCompose: DeploymentTypes.DOCKERCOMPOSE,
kubernetes: DeploymentTypes.KUBERNETES,
openshift: DeploymentTypes.OPENSHIFT,
},
dockerPushCommand: 'docker push',
dockerRepositoryName: '',
...dockerComposeRelatedOptions,
...kubernetesRelatedOptions,
...openshiftRelatedOptions,
};

Options.defaults = (deploymentType = Options.deploymentType.dockerCompose) => {
if (deploymentType === Options.deploymentType.kubernetes) {
return {
appsFolders: new Set(),
directoryPath: Options.directoryPath,
clusteredDbApps: new Set(),
adminPassword: Options.adminPassword,
serviceDiscoveryType: Options.serviceDiscoveryType.eureka,
dockerRepositoryName: Options.dockerRepositoryName,
dockerPushCommand: Options.dockerPushCommand,
kubernetesNamespace: deploymentType === Options.deploymentType.kubernetes ? Options.kubernetesNamespace : undefined,
kubernetesServiceType: deploymentType === Options.deploymentType.kubernetes ? Options.kubernetesServiceType.loadBalancer : undefined,
ingressDomain: deploymentType === Options.deploymentType.kubernetes ? Options.ingressDomain : undefined,
istio: deploymentType === Options.deploymentType.kubernetes ? Options.istio.false : undefined,
openshiftNamespace: deploymentType === Options.deploymentType.openshift ? Options.openshiftNamespace : undefined,
storageType: deploymentType === Options.deploymentType.openshift ? Options.storageType.ephemeral : undefined,
},
_.isUndefined
);
kubernetesNamespace: Options.kubernetesNamespace,
kubernetesServiceType: Options.kubernetesServiceType.loadBalancer,
kubernetesUseDynamicStorage: Options.kubernetesUseDynamicStorage.false,
kubernetesStorageClassName: Options.kubernetesStorageClassName,
ingressDomain: Options.ingressDomain,
monitoring: Options.monitoring.no,
istio: Options.istio.false,
};
}

if (deploymentType === Options.deploymentType.dockerCompose) {
return {
appsFolders: new Set(),
directoryPath: Options.directoryPath,
gatewayType: Options.gatewayType.springCloudGateway,
clusteredDbApps: new Set(),
monitoring: Options.monitoring.no,
serviceDiscoveryType: Options.serviceDiscoveryType.eureka,
};
}

return {
appsFolders: new Set(),
directoryPath: Options.directoryPath,
clusteredDbApps: new Set(),
serviceDiscoveryType: Options.serviceDiscoveryType.eureka,
monitoring: Options.monitoring.no,
dockerRepositoryName: Options.dockerRepositoryName,
dockerPushCommand: Options.dockerPushCommand,
openshiftNamespace: Options.openshiftNamespace,
storageType: Options.storageType.ephemeral,
registryReplicas: Options.registryReplicas.two,
};
};

module.exports = {
Options,
Expand Down
18 changes: 11 additions & 7 deletions jdl/parsing/lexer/deployment-tokens.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,23 @@ const deploymentCategoryToken = createTokenFromConfig({
});

const deploymentTokens = [
{ name: 'DEPLOYMENT_TYPE', pattern: 'deploymentType' },
{ name: 'GATEWAY_TYPE', pattern: 'gatewayType' },
{ name: 'MONITORING', pattern: 'monitoring' },
{ name: 'DIRECTORY_PATH', pattern: 'directoryPath' },
{ name: 'APPS_FOLDERS', pattern: 'appsFolders' },
{ name: 'CLUSTERED_DB_APPS', pattern: 'clusteredDbApps' },
{ name: 'DOCKER_REPOSITORY_NAME', pattern: 'dockerRepositoryName' },
{ name: 'DEPLOYMENT_TYPE', pattern: 'deploymentType' },
{ name: 'DIRECTORY_PATH', pattern: 'directoryPath' },
{ name: 'DOCKER_PUSH_COMMAND', pattern: 'dockerPushCommand' },
{ name: 'KUBERNETES_NAMESPACE', pattern: 'kubernetesNamespace' },
{ name: 'KUBERNETES_SERVICE_TYPE', pattern: 'kubernetesServiceType' },
{ name: 'DOCKER_REPOSITORY_NAME', pattern: 'dockerRepositoryName' },
{ name: 'GATEWAY_TYPE', pattern: 'gatewayType' },
{ name: 'INGRESS_DOMAIN', pattern: 'ingressDomain' },
{ name: 'INGRESS_TYPE', pattern: 'ingressType' },
{ name: 'ISTIO', pattern: 'istio' },
{ name: 'KUBERNETES_NAMESPACE', pattern: 'kubernetesNamespace' },
{ name: 'KUBERNETES_SERVICE_TYPE', pattern: 'kubernetesServiceType' },
{ name: 'KUBERNETES_STORAGE_CLASS_NAME', pattern: 'kubernetesStorageClassName' },
{ name: 'KUBERNETES_USE_DYNAMIC_STORAGE', pattern: 'kubernetesUseDynamicStorage' },
{ name: 'MONITORING', pattern: 'monitoring' },
{ name: 'OPENSHIFT_NAMESPACE', pattern: 'openshiftNamespace' },
{ name: 'REGISTRY_REPLICAS', pattern: 'registryReplicas' },
{ name: 'STORAGE_TYPE', pattern: 'storageType' },
].map(tokenConfig => {
tokenConfig.categories = [deploymentCategoryToken];
Expand Down
13 changes: 13 additions & 0 deletions jdl/parsing/validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const LANGUAGE_PATTERN = /^[a-z]+(-[A-Za-z0-9]+)*$/;
const PATH_PATTERN = /^"([^\/]+).*"$/;
// const PASSWORD_PATTERN = /^(.+)$/;
const REPONAME_PATTERN = /^"((?:http(s)?:\/\/)?[\w.-]+(?:\.[\w.-]+)+[\w\-._~:\/?#[\]@!$&'()*+,;=]+|[a-zA-Z0-9]+)"$/;
const KUBERNETES_STORAGE_CLASS_NAME = /^"[A-Za-z]*"$/;
const JWT_SECRET_KEY_PATTERN = /^\S+$/;
const REMEMBER_ME_KEY_PATTERN = /^\S+$/;
const NUMERIC = /^\d$/;
Expand Down Expand Up @@ -275,11 +276,22 @@ const deploymentConfigPropsValidations = {
pattern: ALPHABETIC,
msg: 'kubernetesServiceType property',
},
KUBERNETES_STORAGE_CLASS_NAME: {
type: 'STRING',
pattern: KUBERNETES_STORAGE_CLASS_NAME,
msg: 'kubernetesStorageClassName property',
},
KUBERNETES_USE_DYNAMIC_STORAGE: { type: 'BOOLEAN' },
INGRESS_DOMAIN: {
type: 'STRING',
pattern: REPONAME_PATTERN,
msg: 'ingressDomain property',
},
INGRESS_TYPE: {
type: 'STRING',
pattern: ALPHABETIC,
msg: 'ingressType property',
},
ISTIO: {
type: 'BOOLEAN',
msg: 'istio property',
Expand All @@ -289,6 +301,7 @@ const deploymentConfigPropsValidations = {
pattern: ALPHANUMERIC_DASH,
msg: 'openshiftNamespace property',
},
REGISTRY_REPLICAS: { type: 'INTEGER' },
STORAGE_TYPE: {
type: 'NAME',
pattern: ALPHABETIC_LOWER,
Expand Down
60 changes: 56 additions & 4 deletions jdl/validators/deployment-validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,68 @@
* limitations under the License.
*/

const { MICROSERVICE } = require('../jhipster/application-types');
const { NO } = require('../jhipster/database-types');
const { ELASTICSEARCH } = require('../jhipster/search-engine-types');
const { Options } = require('../jhipster/deployment-options');
const Validator = require('./validator');

class DeploymentValidator extends Validator {
module.exports = class DeploymentValidator extends Validator {
constructor() {
super('deployment', ['deploymentType', 'appsFolders', 'dockerRepositoryName']);
super('deployment', ['deploymentType', 'appsFolders', 'directoryPath']);
}

validate(jdlDeployment) {
validate(jdlDeployment, options = {}) {
super.validate(jdlDeployment);

switch (jdlDeployment.deploymentType) {
case Options.deploymentType.dockerCompose:
validateDockerComposeRelatedDeployment(jdlDeployment, options);
break;
case Options.deploymentType.kubernetes:
validateKubernetesRelatedDeployment(jdlDeployment);
break;
case Options.deploymentType.openshift:
validateOpenshiftRelatedDeployment(jdlDeployment, options);
break;
default:
throw new Error(`The deployment type ${jdlDeployment.deploymentType} isn't supported.`);
}
}
};

function validateDockerComposeRelatedDeployment(jdlDeployment, options = {}) {
if (jdlDeployment.gatewayType !== Options.gatewayType.springCloudGateway && options.applicationType === MICROSERVICE) {
throw new Error('A gateway type must be provided when dealing with microservices and the deployment type is docker-compose.');
}
}

function validateKubernetesRelatedDeployment(jdlDeployment) {
if (!jdlDeployment.kubernetesServiceType) {
throw new Error('A kubernetes service type must be provided when dealing with kubernetes-related deployments.');
}
if (jdlDeployment.istio && !jdlDeployment.ingressDomain) {
throw new Error(
'An ingress domain must be provided when dealing with kubernetes-related deployments, with istio and when the service type is ingress.'
);
}
if (jdlDeployment.kubernetesServiceType === Options.kubernetesServiceType.ingress && !jdlDeployment.ingressType) {
throw new Error('An ingress type is required when dealing with kubernetes-related deployments and when the service type is ingress.');
}
}

module.exports = DeploymentValidator;
function validateOpenshiftRelatedDeployment(jdlDeployment, options) {
if (jdlDeployment.storageType) {
if (options.prodDatabaseType === NO) {
throw new Error("Can't have the storageType option set when there is no prodDatabaseType.");
}

if (options.searchEngine === ELASTICSEARCH) {
throw new Error("Can't have the storageType option set when elasticsearch is the search engine.");
}

if (jdlDeployment.monitoring === Options.monitoring.prometheus) {
throw new Error("Can't have the storageType option set when the monitoring is done with prometheus.");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,6 @@ describe('ParsedJDLToJDLObjectConverter', () => {
expect(deployment).to.deep.equal({
deploymentType: 'docker-compose',
directoryPath: '../',
dockerPushCommand: 'docker push',
dockerRepositoryName: 'test',
gatewayType: 'SpringCloudGateway',
monitoring: 'no',
Expand Down
4 changes: 2 additions & 2 deletions test/jdl/exporters/jhipster-deployment-exporter.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ describe('JHipsterDeploymentExporter', () => {
appsFolders: ['tata', 'titi'],
clusteredDbApps: [],
directoryPath: '../',
dockerPushCommand: 'docker push',
dockerRepositoryName: 'test',
gatewayType: 'SpringCloudGateway',
monitoring: 'no',
Expand Down Expand Up @@ -128,12 +127,13 @@ describe('JHipsterDeploymentExporter', () => {
directoryPath: '../',
dockerPushCommand: 'docker push',
dockerRepositoryName: 'test',
gatewayType: 'SpringCloudGateway',
ingressDomain: '',
istio: false,
kubernetesNamespace: 'default',
kubernetesServiceType: 'LoadBalancer',
monitoring: 'no',
kubernetesUseDynamicStorage: false,
kubernetesStorageClassName: '',
serviceDiscoveryType: 'eureka',
});
});
Expand Down
42 changes: 42 additions & 0 deletions test/jdl/grammar/grammar.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1861,4 +1861,46 @@ entity A {
});
});
});
context('when parsing deployments', () => {
context('with kubernetesStorageClassName', () => {
context('being empty', () => {
let parsedDeployment;

before(() => {
const content = parseFromContent(
`deployment {
kubernetesStorageClassName ""
}
`
);
parsedDeployment = content.deployments[0];
});

it('should parse it', () => {
expect(parsedDeployment).to.deep.equal({
kubernetesStorageClassName: '',
});
});
});
context('being set', () => {
let parsedDeployment;

before(() => {
const content = parseFromContent(
`deployment {
kubernetesStorageClassName "SetValue"
}
`
);
parsedDeployment = content.deployments[0];
});

it('should parse it', () => {
expect(parsedDeployment).to.deep.equal({
kubernetesStorageClassName: 'SetValue',
});
});
});
});
});
});
Loading

0 comments on commit 4bffad3

Please sign in to comment.