Skip to content

Commit

Permalink
Merge pull request #13 from cxcloud/deployment-improvements
Browse files Browse the repository at this point in the history
Deployment improvements
  • Loading branch information
sallar authored Jan 30, 2019
2 parents fc81dd7 + 91d2746 commit b277710
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 34 deletions.
10 changes: 10 additions & 0 deletions lib/cxcloud-deploy.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ async function deploy(cmd) {

const config = await env.readDeploymentManifest();

// Purge action
if (cmd.purge || cmd.purgeAll) {
logOperation('Purging deployment...');
await kube.deleteService(config, cmd.env, cmd.purgeAll);
return;
}

// Deployment action
if (config.deployment) {
await env.ensureDeployEnvironment();
await docker.dockerLogin();
Expand Down Expand Up @@ -46,5 +54,7 @@ program
'Comma separated list of environment variables',
input => input.split(',')
)
.option('-p, --purge', 'Purge the deployment')
.option('-x, --purge-all', 'Purge the deployment and namespace')
.action(deploy)
.parse(process.argv);
44 changes: 30 additions & 14 deletions lib/utils/deployment.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
const YAML = require('js-yaml');
const { showError } = require('./');

const buildSSLManifest = domain => {
const buildSSLManifest = (domain, namespace) => {
return {
apiVersion: 'certmanager.k8s.io/v1alpha1',
kind: 'Certificate',
metadata: {
namespace,
name: domain
},
spec: {
Expand All @@ -32,12 +33,13 @@ const buildSSLManifest = domain => {
const buildDeploymentRoutingManifest = config => {
const { deployment } = config;
const { routing } = deployment;
const namespace = config.namespace || 'applications';
return {
apiVersion: 'extensions/v1beta1',
kind: 'Ingress',
metadata: {
name: `${deployment.name}-routing`,
namespace: 'applications',
namespace,
annotations: {
'kubernetes.io/ingress.class': 'nginx',
...(routing.ssl
Expand Down Expand Up @@ -78,12 +80,13 @@ const buildDeploymentRoutingManifest = config => {

const buildGlobalRoutingManifest = config => {
const { routing } = config;
const namespace = config.namespace || 'applications';
return {
apiVersion: 'extensions/v1beta1',
kind: 'Ingress',
metadata: {
name: `${routing.domain}-routing`,
namespace: 'applications',
namespace,
annotations: {
'kubernetes.io/ingress.class': 'nginx',
...(routing.ssl
Expand Down Expand Up @@ -122,6 +125,7 @@ const buildGlobalRoutingManifest = config => {

const buildDeploymentManifests = (config, envList = []) => {
const data = config.deployment;
const namespace = config.namespace || 'applications';
const passedEnvs = envList
.filter(item => /^([A-Z_0-9]+)=([\w\W]+)$/.test(item))
.map(item => item.split('='))
Expand All @@ -148,7 +152,7 @@ const buildDeploymentManifests = (config, envList = []) => {
metadata: {
labels: { app: data.name, role: 'service' },
name: data.name,
namespace: 'applications'
namespace
},
spec: {
replicas: Number(data.replicas) || 1,
Expand Down Expand Up @@ -180,7 +184,7 @@ const buildDeploymentManifests = (config, envList = []) => {
metadata: {
labels: { app: data.name },
name: data.name,
namespace: 'applications'
namespace
},
spec: {
ports: [{ name: 'http', port: data.containerPort }],
Expand All @@ -190,8 +194,20 @@ const buildDeploymentManifests = (config, envList = []) => {
];
};

const buildNamespaceManifest = namespace => ({
apiVersion: 'v1',
kind: 'Namespace',
metadata: {
name: namespace
}
});

exports.getKubernetesManifest = (config, envList = []) => {
const manifests = [];
const namespace = config.namespace || 'applications';

// Push namespace, so if it's not there should be created
manifests.push(buildNamespaceManifest(namespace));

// Single deployment
if (config.deployment) {
Expand All @@ -201,24 +217,24 @@ exports.getKubernetesManifest = (config, envList = []) => {
// Deployment specific routing
if (config.deployment && config.deployment.routing) {
if (config.deployment.routing.ssl) {
manifests.push(buildSSLManifest(config.deployment.routing.domain));
manifests.push(
buildSSLManifest(config.deployment.routing.domain, namespace)
);
}
manifests.push(buildDeploymentRoutingManifest(config));
}

// Global routing
if (config.routing) {
if (config.routing.ssl) {
manifests.push(buildSSLManifest(config.routing.domain));
manifests.push(buildSSLManifest(config.routing.domain, namespace));
}
manifests.push(buildGlobalRoutingManifest(config));
}

return manifests
.map(json =>
YAML.safeDump(json, {
lineWidth: 200
})
)
.join('\n---\n');
return manifests.map(json =>
YAML.safeDump(json, {
lineWidth: 200
})
);
};
57 changes: 40 additions & 17 deletions lib/utils/env.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const getBinPath = bin => path.join(__dirname, '../../.env/bin', bin);
const envAWSKeysExist = function() {
const { AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY } = process.env;
return AWS_ACCESS_KEY_ID && AWS_SECRET_ACCESS_KEY;
}
};

const installEnvironment = async () => {
if (await fs.pathExists(path.join(__dirname, '../../.env'))) {
Expand All @@ -30,20 +30,44 @@ const installEnvironment = async () => {

const installRequirements = async () => {
if (envAWSKeysExist()) {
logOperation('AWS API key environment variables is set, `awsudo` not needed.');
return;
}
if (await fs.pathExists(getBinPath('awsudo'))) {
logOperation(
'AWS API key environment variables is set, `awsudo` not needed.'
);
} else if (await fs.pathExists(getBinPath('awsudo'))) {
logOperation('`awsudo` exists, skipping installation.');
return;
} else {
logOperation('Installing `awsudo`...');
await runCommand(getBinPath('pip'), [
'-q',
'install',
'--upgrade',
'git+https://github.com/makethunder/awsudo.git'
]);
}

if (await fs.pathExists(getBinPath('aws'))) {
logOperation('`aws` exists, skipping installation.');
} else {
logOperation('Installing AWS CLI...');
await runCommand(getBinPath('pip'), [
'-q',
'install',
'--upgrade',
'awscli'
]);
}
logOperation('Installing `awsudo`...');
await runCommand(getBinPath('pip'), [
'-q',
'install',
'--upgrade',
'git+https://github.com/makethunder/awsudo.git'
]);
};

const getEnvironmentMacros = async () => {
return {
GIT_BRANCH: await execa.stdout('git', [
'rev-parse',
'--abbrev-ref',
'HEAD'
]),
GIT_COMMIT_ID: await execa.stdout('git', ['rev-parse', 'HEAD']),
GIT_SHORT_COMMIT_ID: await execa.stdout('git', ['describe', '--always'])
};
};

exports.checkEnvironment = async () => {
Expand All @@ -60,9 +84,7 @@ exports.checkEnvironment = async () => {

exports.runCommandWithAWSCredentials = (cmd, args) => {
if (envAWSKeysExist()) {
return execa(getBinPath(cmd), [
...args
]);
return execa(getBinPath(cmd), [...args]);
} else {
return execa(getBinPath('awsudo'), [
'-u',
Expand All @@ -71,7 +93,7 @@ exports.runCommandWithAWSCredentials = (cmd, args) => {
...args
]);
}
}
};

exports.getBinPath = getBinPath;

Expand Down Expand Up @@ -104,6 +126,7 @@ exports.readDeploymentManifest = async () => {
const macros = {
APP_VERSION: pkg.version,
APP_NAME: pkg.name,
...(await getEnvironmentMacros()),
...process.env
};
Object.keys(macros).forEach(key => {
Expand Down
12 changes: 11 additions & 1 deletion lib/utils/kube.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,17 @@ exports.deployService = async (config, envList) => {
logOperation('Deploying to cluster...');
const manifest = getKubernetesManifest(config, envList);
return runCommand('kubectl', ['apply', '-f', '-'], {
input: manifest
input: manifest.join('\n---\n')
});
};

exports.deleteService = async (config, envList, purgeNamespace = false) => {
logOperation('Deploying to cluster...');
const manifest = getKubernetesManifest(config, envList);
const [_, ...rest] = manifest;

return runCommand('kubectl', ['delete', '-f', '-'], {
input: (purgeNamespace === true ? manifest : rest).join('\n---\n')
});
};

Expand Down
1 change: 1 addition & 0 deletions lib/utils/schemas.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ const rootRoutingSchema = Joi.object().keys({
});

exports.deploymentManifestSchema = Joi.object().keys({
namespace: Joi.string().default('applications'),
deployment: deploymentSchema,
routing: rootRoutingSchema
});
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cxcloud",
"version": "0.10.0",
"version": "0.11.0",
"description": "CXCloud command line tools",
"license": "MIT",
"repository": "cxcloud/cxcloud-cli",
Expand Down

0 comments on commit b277710

Please sign in to comment.