Skip to content

WIP feat(docker): Docker build and deploy #885

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

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions addon/docker/blueprints/docker/files/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.git
.gitignore
.env*
node_modules
docker-compose*.yml
8 changes: 8 additions & 0 deletions addon/docker/blueprints/docker/files/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# You are free to change the contents of this file
FROM nginx

# Configure for angular fallback routes
COPY nginx.conf /etc/nginx/conf.d/default.conf

# Copy built app to wwwroot
COPY dist /usr/share/nginx/html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
version: "2"
services:
<%= serviceName %>:
<% if (useImage) {
%>image: ${NG_APP_IMAGE}<%
} else {
%>build: .
image: <%= imageName %><% } %>
ports:
- "<%= servicePort %>:<%= containerPort %>"
13 changes: 13 additions & 0 deletions addon/docker/blueprints/docker/files/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
server {
listen 80;
index index.html;
root /usr/share/nginx/html;

# These log paths are forwarded to the docker log collector
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;

location / {
try_files $uri$args $uri$args/ $uri/ /index.html =404;
}
}
41 changes: 41 additions & 0 deletions addon/docker/blueprints/docker/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
'use strict';

const stringUtils = require('ember-cli-string-utils');

module.exports = {
description: '',

availableOptions: [
{ name: 'environment', type: String, aliases: ['env'] },
{ name: 'service-name', type: String, default: 'ngapp', aliases: ['s'] },
{ name: 'service-port', type: Number, default: 8000, aliases: ['sp'] },
{ name: 'container-port', type: Number, default: 80, aliases: ['cp'] },
{ name: 'use-image', type: Boolean, default: false, aliases: ['i'] },
{ name: 'registry', type: String, aliases: ['r'] },
{ name: 'image-org', type: String, aliases: ['o'] },
{ name: 'image-name', type: String, aliases: ['in'] }
],

locals: function (options) {
return {
environment: options.environment,
serviceName: options.serviceName,
servicePort: options.servicePort,
containerPort: options.containerPort,
useImage: options.useImage,
registry: options.registry,
imageOrg: options.imageOrg,
imageName: options.imageName
};
},

fileMapTokens: function (options) {
// Return custom template variables here.
return {
__environment__: () => {
return (options.locals.environment) ?
'-' + stringUtils.dasherize(options.locals.environment) : '';
}
};
}
};
111 changes: 111 additions & 0 deletions addon/docker/commands/deploy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
'use strict';

const Command = require('ember-cli/lib/models/command');
const ValidateDockerCli = require('../tasks/validate-docker-cli');
const BuildTask = require('ember-cli/lib/tasks/build');
const Promise = require('ember-cli/lib/ext/promise');

module.exports = Command.extend({
name: 'docker:deploy',
description: 'Builds and deploys to a Docker environment.',
aliases: ['d:d'],
works: 'insideProject',

availableOptions: [
{ name: 'dry-run', type: Boolean, default: false, aliases: ['d'] },
{ name: 'verbose', type: Boolean, default: false, aliases: ['v'] },
{
name: 'tag', type: String, aliases: ['t'],
description: 'The Docker tag to use for deploying images.'
},
{
name: 'machine', type: String, aliases: ['m'],
description: 'The Docker Machine name to use as the deploy destination.'
},
{
name: 'services', type: Array, aliases: ['s'],
description: 'The specific service name(s) to deploy from the compose file.'
},
{
name: 'config-env', type: String, default: 'prod', aliases: ['ce', 'cfg'],
description: 'The Angular configuration environment file to include in the build.'
},
{
name: 'skip-build', type: Boolean, default: false, aliases: ['sb'],
description: 'Do not build the Angular application. Use current contents of "dist/".'
},
{
name: 'no-cache', type: Boolean, default: false, aliases: ['nc'],
description: 'Do not use cache when building the image.'
},
{
name: 'force-rm', type: Boolean, default: false, aliases: ['rm'],
description: 'Always remove intermediate containers.'
},
{
name: 'pull', type: Boolean, default: false,
description: 'Always attempt to pull a newer version of the image.'
},
{
name: 'force-recreate', type: Boolean, default: false, aliases: ['fr'],
description: 'Recreate containers even if their configuration and image haven\'t changed.'
},
{
name: 'no-recreate', type: Boolean, default: false, aliases: ['nr'],
description: 'If containers already exist, don\'t recreate them.'
}
],

anonymousOptions: ['<environment>'],

run: function(commandOptions, rawArgs) {
var environment = (rawArgs.length) ? rawArgs[0] : null;

// Validate Docker CLI options
var validateDockerCli = new ValidateDockerCli({
ui: this.ui,
project: this.project
});
var validateDockerOpts = {
verbose: commandOptions.verbose
};

// Build task options
var buildTask = new BuildTask({
ui: this.ui,
analytics: this.analytics,
project: this.project
});

var buildOptions = {
environment: commandOptions.configEnv,
outputPath: 'dist/'
};

return validateDockerCli.run(validateDockerOpts)
.then(buildApp)
.then(buildImage)
.then(deploy);

function buildApp() {
// TODO: If environment useImage == true, skip the build.
if (commandOptions.skipBuild) return Promise.resolve();
return buildTask.run(buildOptions);
}

function buildImage() {
// TODO: If environment useImage == true, skip the build.
// TODO: Use a reusable task for image builds
// TODO: Validate docker build env
// TODO: docker-compose build {serviceName}
return Promise.resolve();
}

// TODO: Move to a task
function deploy() {
// TODO: Validate docker deploy env
// TODO: docker-compose up -d [services]
return Promise.resolve();
}
}
});
124 changes: 124 additions & 0 deletions addon/docker/commands/init.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
'use strict';

const Command = require('ember-cli/lib/models/command');
const normalizeBlueprint = require('ember-cli/lib/utilities/normalize-blueprint-option');
const ValidateDockerCli = require('../tasks/validate-docker-cli');
const SetDockerConfig = require('../tasks/set-docker-config');

module.exports = Command.extend({
name: 'docker:init',
description: 'Initializes settings and files for building and deploying to a Docker environment.',
aliases: ['d:i'],
works: 'insideProject',

availableOptions: [
{ name: 'dry-run', type: Boolean, default: false, aliases: ['d'] },
{ name: 'verbose', type: Boolean, default: false, aliases: ['v'] },
{ name: 'blueprint', type: String, aliases: ['b'] },
{
name: 'machine', type: String, aliases: ['m'], default: 'native',
description: 'The Docker Machine name of the deploy machine for this environment.'
},
{
name: 'service-name', type: String, default: 'ngapp', aliases: ['s', 'service'],
description: 'The service name of the Angular app for use in the compose file.'
},
{
name: 'service-port', type: Number, default: 8000, aliases: ['sp'],
description: 'The external port of the Angular service exposed on the host machine.'
},
{
name: 'container-port', type: Number, default: 80, aliases: ['cp'],
description: 'The internal port of the Angular service within the container.'
},
{
name: 'use-image', type: Boolean, default: false, aliases: ['ui'],
description: 'Use an image URI when deploying, instead of building.' +
' Requires the image pushed to an external registry.'
},
{
name: 'registry', type: String, aliases: ['r', 'reg'],
description: 'The default Docker registry address to use for the "docker:push" command.'
},
{
name: 'image-org', type: String, aliases: ['o', 'org'],
description: 'The organization name for the image when pushing to a Docker registry.'
},
{
name: 'image-name', type: String, aliases: ['im', 'image'],
description: 'The image name to use when building or pulling from a Docker registry.'
}
],

anonymousOptions: ['<environment>'],

_defaultBlueprint: function () {
return (this.project.isEmberCLIAddon()) ? 'addon' : 'docker';
},

run: function(commandOptions, rawArgs) {
var environment = (rawArgs.length) ? rawArgs[0] : null;
var imageName = commandOptions.imageName || commandOptions.serviceName;

// Validate Docker CLI options
var validateDockerCli = new ValidateDockerCli({
ui: this.ui,
project: this.project
});
var validateDockerOpts = {
verbose: commandOptions.verbose
};

// Set Docker configuration options
var setDockerConfig = new SetDockerConfig({
ui: this.ui,
project: this.project
});

var configOpts = {
dryRun: commandOptions.dryRun
};

var dockerProjectCfg = {
imageName: imageName,
imageOrg: commandOptions.imageOrg,
registry: commandOptions.registry
};
var dockerEnvCfg = {
name: environment || 'default',
useImage: commandOptions.useImage,
serviceName: commandOptions.serviceName,
machine: commandOptions.machine
};

// Install Docker blueprints options
var installBlueprint = new this.tasks.InstallBlueprint({
ui: this.ui,
analytics: this.analytics,
project: this.project
});

var blueprintOpts = {
dryRun: commandOptions.dryRun,
blueprint: commandOptions.blueprint || this._defaultBlueprint(),
rawName: this.project.root,
targetFiles: '',
rawArgs: rawArgs.toString(),
environment: environment,
machine: commandOptions.machine,
serviceName: commandOptions.serviceName,
servicePort: commandOptions.servicePort,
containerPort: commandOptions.containerPort,
useImage: commandOptions.useImage,
registry: commandOptions.registry,
imageOrg: commandOptions.imageOrg,
imageName: imageName
};

blueprintOpts.blueprint = normalizeBlueprint(blueprintOpts.blueprint);

return validateDockerCli.run(validateDockerOpts)
.then(() => setDockerConfig.run(dockerProjectCfg, dockerEnvCfg, configOpts))
.then(() => installBlueprint.run(blueprintOpts));
}
});
Loading