Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added missing JDL deployment options #14945

Merged
merged 8 commits into from
Jun 9, 2021
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(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont remmber why did omitBy here originally, so hope its still works

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The omit here is useful if you have undefined values, and as the previous code generated them, the omit's role was to clean things up.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And thanks for the review :)

{
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 @@ -1721,4 +1721,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