Skip to content

Commit b96da3f

Browse files
Greg Ziebold and Nathan Barefieldgregz67
Greg Ziebold and Nathan Barefield
authored andcommitted
feat(gen:cloudfoundry): add working tests, and support for mongolab services
1 parent 9ee9b4b commit b96da3f

File tree

4 files changed

+230
-25
lines changed

4 files changed

+230
-25
lines changed

app/templates/server/config/environment/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ var all = {
1919
root: path.normalize(__dirname + '/../../..'),
2020

2121
// Server port
22-
port: process.env.PORT || 9000,
22+
port: process.env.PORT || process.env.VCAP_APP_PORT || 9000,
2323

2424
// Should we populate the DB with sample data?
2525
seedDB: false,

app/templates/server/config/environment/production.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@
22

33
// Production specific configuration
44
// =================================
5+
6+
// cloud foundry
7+
var getCfMongo = function() {
8+
var vcap_services = JSON.parse(process.env.VCAP_SERVICES),
9+
mongoUri;
10+
if (vcap_services.mongolab && vcap_services.mongolab.length > 0) {
11+
mongoUri = vcap_services.mongolab[0].credentials.uri;
12+
}
13+
return mongoUri;
14+
};
15+
516
module.exports = {
617
// Server IP
718
ip: process.env.OPENSHIFT_NODEJS_IP ||
@@ -11,6 +22,7 @@ module.exports = {
1122
// Server port
1223
port: process.env.OPENSHIFT_NODEJS_PORT ||
1324
process.env.PORT ||
25+
process.env.VCAP_APP_PORT ||
1426
8080,
1527

1628
// MongoDB connection options
@@ -19,6 +31,7 @@ module.exports = {
1931
process.env.MONGOHQ_URL ||
2032
process.env.OPENSHIFT_MONGODB_DB_URL +
2133
process.env.OPENSHIFT_APP_NAME ||
34+
getCfMongo() ||
2235
'mongodb://localhost/<%= _.slugify(appname) %>'
2336
}
24-
};
37+
};

cloudfoundry/index.js

Lines changed: 58 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ Generator.prototype.askForRoute = function askForRoute() {
3131
}
3232
];
3333

34-
this.prompt(prompts, function (props) {
34+
this.prompt(prompts, function(props) {
3535
this.routeName = this._.slugify(props.routeName);
3636
done();
3737
}.bind(this));
@@ -41,10 +41,10 @@ Generator.prototype.checkInstallation = function checkInstallation() {
4141
if (this.abort) return;
4242
var done = this.async();
4343

44-
exec('cf --version', function (err) {
44+
exec('cf --version', function(err) {
4545
if (err) {
4646
this.log.error('You don\'t have the Cloud Foundry CLI installed. ' +
47-
'Grab it from https://github.com/cloudfoundry/cli');
47+
'Grab it from https://github.com/cloudfoundry/cli');
4848
this.abort = true;
4949
}
5050
done();
@@ -63,7 +63,7 @@ Generator.prototype.askForApiEndpoint = function askForApiEndpoint() {
6363
}
6464
];
6565

66-
this.prompt(prompts, function (props) {
66+
this.prompt(prompts, function(props) {
6767
this.apiEndpoint = props.apiEndpoint;
6868
done();
6969
}.bind(this));
@@ -75,7 +75,7 @@ Generator.prototype.cfInit = function cfInit() {
7575

7676
this.log(chalk.bold('Setting Cloud Foundry api endpoint'));
7777
this.mkdir('dist');
78-
var child = exec('cf api ' + this.apiEndpoint, { cwd: 'dist' }, function (err, stdout, stderr) {
78+
var child = exec('cf api ' + this.apiEndpoint, { cwd: 'dist' }, function(err, stdout, stderr) {
7979
if (err) {
8080
this.abort = true;
8181
this.log.error(err);
@@ -91,7 +91,7 @@ Generator.prototype.cfInit = function cfInit() {
9191
done();
9292
}.bind(this));
9393

94-
child.stdout.on('data', function (data) {
94+
child.stdout.on('data', function(data) {
9595
this.log(this._.trim(data.toString(), "\n\r"));
9696
}.bind(this));
9797
}
@@ -101,7 +101,7 @@ Generator.prototype.copyProcfile = function copyProcfile() {
101101
var done = this.async();
102102
this.log(chalk.bold('Creating Procfile and manifest.yml'));
103103
genUtils.processDirectory(this, '.', './dist');
104-
this.conflicter.resolve(function (err) {
104+
this.conflicter.resolve(function(err) {
105105
done();
106106
});
107107
};
@@ -111,10 +111,10 @@ Generator.prototype.gruntBuild = function gruntBuild() {
111111
var done = this.async();
112112

113113
this.log(chalk.bold('\nBuilding dist folder, please wait...'));
114-
var child = exec('grunt build', function (err, stdout) {
114+
var child = exec('grunt build', function(err, stdout) {
115115
done();
116116
}.bind(this));
117-
child.stdout.on('data', function (data) {
117+
child.stdout.on('data', function(data) {
118118
this.log(data.toString());
119119
}.bind(this));
120120
};
@@ -126,8 +126,44 @@ Generator.prototype.cfPush = function cfPush() {
126126
this.log(chalk.bold("\nUploading your initial application code.\n This may take " + chalk.cyan('several minutes') + " depending on your connection speed..."));
127127

128128
var randomRoute = this.routeName === '' ? '--random-route' : '';
129-
var child = exec(['cf push', this.appname, randomRoute].join(' '), { cwd: 'dist' }, function (err, stdout, stderr) {
129+
var child = exec(['cf push', this.appname, randomRoute, ' --no-start'].join(' '), { cwd: 'dist' }, function(err, stdout, stderr) {
130130
if (err) {
131+
this.abort = true;
132+
this.log.error(err);
133+
} else {
134+
this.log('stdout: ' + stdout);
135+
}
136+
done();
137+
}.bind(this));
138+
child.stdout.on('data', function(data) {
139+
this.log(this._.trim(data.toString(), "\n\r"));
140+
}.bind(this));
141+
};
142+
143+
Generator.prototype.cfSetEnvVars = function cfSetEnvVars() {
144+
if (this.abort) return;
145+
var done = this.async();
146+
147+
var child = exec('cf set-env ' + this.appname + ' NODE_ENV production', { cwd: 'dist' }, function(err, stdout, stderr) {
148+
if (err) {
149+
this.abort = true;
150+
this.log.error(err);
151+
}
152+
done();
153+
154+
}.bind(this));
155+
child.stdout.on('data', function(data) {
156+
this.log(this._.trim(data.toString(), "\n\r"));
157+
}.bind(this));
158+
};
159+
160+
Generator.prototype.cfStart = function cfStart() {
161+
if (this.abort) return;
162+
var done = this.async();
163+
164+
var child = exec('cf start ' + this.appname, { cwd: 'dist' }, function(err, stdout, stderr) {
165+
if (err) {
166+
this.abort = true;
131167
this.log.error(err);
132168
} else {
133169
var hasWarning = false;
@@ -139,36 +175,35 @@ Generator.prototype.cfPush = function cfPush() {
139175

140176
if (this.filters.facebookAuth) {
141177
this.log(chalk.yellow('You will need to set environment variables for facebook auth. From `/dist`:\n\t' +
142-
chalk.bold('cf set-env ' + this.appName + ' FACEBOOK_ID appId\n\t') +
143-
chalk.bold('cf set-env ' + this.appName + ' FACEBOOK_SECRET secret\n')));
178+
chalk.bold('cf set-env ' + this.appname + ' FACEBOOK_ID appId\n\t') +
179+
chalk.bold('cf set-env ' + this.appname + ' FACEBOOK_SECRET secret\n')));
144180
hasWarning = true;
145181
}
146182
if (this.filters.googleAuth) {
147183
this.log(chalk.yellow('You will need to set environment variables for google auth. From `/dist`:\n\t' +
148-
chalk.bold('cf set-env ' + this.appName + ' GOOGLE_ID appId\n\t') +
149-
chalk.bold('cf set-env ' + this.appName + ' GOOGLE_SECRET secret\n')));
184+
chalk.bold('cf set-env ' + this.appname + ' GOOGLE_ID appId\n\t') +
185+
chalk.bold('cf set-env ' + this.appname + ' GOOGLE_SECRET secret\n')));
150186
hasWarning = true;
151187
}
152188
if (this.filters.twitterAuth) {
153189
this.log(chalk.yellow('You will need to set environment variables for twitter auth. From `/dist`:\n\t' +
154-
chalk.bold('cf set-env ' + this.appName + ' TWITTER_ID appId\n\t') +
155-
chalk.bold('cf set-env ' + this.appName + ' TWITTER_SECRET secret\n')));
190+
chalk.bold('cf set-env ' + this.appname + ' TWITTER_ID appId\n\t') +
191+
chalk.bold('cf set-env ' + this.appname + ' TWITTER_SECRET secret\n')));
156192
hasWarning = true;
157193
}
158194

159195
this.log(chalk.green('\nYour app should now be live.'));
160196
if (hasWarning) {
161197
this.log(chalk.green('\nYou may need to address the issues mentioned above and restart the server for the app to work correctly.'));
162198
}
163-
/*
164-
todo: build grunt plugin grunt-cf-deploy and add to this generator
165-
this.log(chalk.yellow('After app modification run\n\t' + chalk.bold('grunt build') +
166-
'\nThen deploy with\n\t' + chalk.bold('grunt cfDeploy')));
167-
*/
199+
this.log(chalk.yellow('After app modification run\n\t' + chalk.bold('grunt build') +
200+
'\nThen deploy (from dist directory ) with\n\t' + chalk.bold('cf push')));
168201
}
169202
done();
203+
170204
}.bind(this));
171-
child.stdout.on('data', function (data) {
205+
child.stdout.on('data', function(data) {
172206
this.log(this._.trim(data.toString(), "\n\r"));
173207
}.bind(this));
174-
};
208+
209+
};

test/cf-test-creation.js

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
/*global describe, beforeEach, it */
2+
'use strict';
3+
var path = require('path');
4+
var helpers = require('yeoman-generator').test;
5+
var chai = require('chai');
6+
var expect = chai.expect;
7+
var fs = require('fs-extra');
8+
var exec = require('child_process').exec;
9+
10+
describe('angular-fullstack:cloudfoundry', function () {
11+
var gen, defaultOptions = {
12+
script: 'js',
13+
markup: 'html',
14+
stylesheet: 'sass',
15+
router: 'uirouter',
16+
bootstrap: true,
17+
uibootstrap: false,
18+
mongoose: false,
19+
auth: false,
20+
oauth: [],
21+
socketio: false
22+
};
23+
24+
25+
function generatorTest(generatorType, name, mockPrompt, callback) {
26+
gen.run({}, function () {
27+
var afGenerator;
28+
var deps = [path.join('../..', generatorType)];
29+
afGenerator = helpers.createGenerator('angular-fullstack:' + generatorType, deps, [name]);
30+
31+
helpers.mockPrompt(afGenerator, mockPrompt);
32+
afGenerator.run([], function () {
33+
callback();
34+
});
35+
});
36+
}
37+
38+
beforeEach(function (done) {
39+
this.timeout(10000);
40+
this.appname = 'testapp' + Math.floor(Math.random()*100000000).toString();
41+
var deps = [
42+
'../../app',
43+
[
44+
helpers.createDummyGenerator(),
45+
'ng-component:app'
46+
]
47+
];
48+
49+
helpers.testDirectory(path.join(__dirname, this.appname), function (err) {
50+
if (err) {
51+
return done(err);
52+
}
53+
54+
gen = helpers.createGenerator('angular-fullstack:app', deps);
55+
gen.options['skip-install'] = true;
56+
57+
fs.mkdirSync(__dirname + '/' + this.appname + '/client');
58+
fs.symlinkSync(__dirname + '/fixtures/node_modules', __dirname + '/' + this.appname + '/node_modules');
59+
fs.symlinkSync(__dirname +'/fixtures/bower_components', __dirname + '/' + this.appname + '/client/bower_components');
60+
61+
helpers.mockPrompt(gen, defaultOptions);
62+
this.timeout(60000);
63+
64+
done();
65+
}.bind(this));
66+
});
67+
68+
afterEach(function (done) {
69+
exec('cf delete ' + this.appname + " -r -f", function (error, stdout, stderr) {
70+
gen.log("Deleting cloudfoundry app instance for " + this.appname + "...");
71+
if (error) {
72+
gen.log(error);
73+
}
74+
done();
75+
}.bind(this));
76+
77+
});
78+
79+
afterEach(function (done) {
80+
exec('rm -rf ' + this.appname, { cwd: '..' }, function (error, stdout, stderr) {
81+
gen.log("Deleting local app instance for " + this.appname + "...");
82+
if (error) {
83+
gen.log(error);
84+
}
85+
done();
86+
}.bind(this));
87+
88+
});
89+
90+
it('copies procfile and manifest files with named route', function (done) {
91+
var mockPromptOptions = {
92+
routeName: Math.floor(Math.random()*100000000).toString(),
93+
apiEndpoint: ''
94+
};
95+
generatorTest('cloudfoundry', 'cf-test', mockPromptOptions, function () {
96+
helpers.assertFile([
97+
'dist/Procfile',
98+
'dist/manifest.yml'
99+
]);
100+
exec('cf app ' + this.appname, function (error, stdout, stderr) {
101+
if (error) {
102+
console.log(error);
103+
console.log(stderr);
104+
}
105+
106+
gen.log(stdout);
107+
expect(stdout, 'App failed to start: \n' + stdout).to.contain('running');
108+
done();
109+
});
110+
}.bind(this));
111+
});
112+
113+
it('copies procfile and manifest files with blank route name', function (done) {
114+
var mockPromptOptions = {
115+
routeName: '',
116+
apiEndpoint: ''
117+
};
118+
generatorTest('cloudfoundry', 'cf-test', mockPromptOptions, function () {
119+
helpers.assertFile([
120+
'dist/Procfile',
121+
'dist/manifest.yml'
122+
]);
123+
exec('cf app ' + this.appname, function (error, stdout, stderr) {
124+
if (error) {
125+
console.log(error);
126+
console.log(stderr);
127+
}
128+
gen.log(stdout);
129+
expect(stdout, 'App failed to start: \n' + stdout).to.contain('running');
130+
done();
131+
});
132+
}.bind(this));
133+
});
134+
135+
it('copies procfile and manifest files with specific apiendpoint', function (done) {
136+
var mockPromptOptions = {
137+
routeName: '',
138+
apiEndpoint: 'api.run.pivotal.io'
139+
};
140+
generatorTest('cloudfoundry', 'cf-test', mockPromptOptions, function () {
141+
helpers.assertFile([
142+
'dist/Procfile',
143+
'dist/manifest.yml'
144+
]);
145+
exec('cf app ' + this.appname, function (error, stdout, stderr) {
146+
if (error) {
147+
console.log(error);
148+
console.log(stderr);
149+
}
150+
gen.log(stdout);
151+
expect(stdout, 'App failed to start: \n' + stdout).to.contain('running');
152+
done();
153+
});
154+
}.bind(this));
155+
});
156+
157+
});

0 commit comments

Comments
 (0)