sample Projects UI, cleanup vendor/

- Update dependencies
- Remove lib/
- Dashboard
This commit is contained in:
Matej Kramny 2016-03-28 18:28:26 +01:00
parent e212c88bd2
commit 121567fa03
45 changed files with 257 additions and 2029 deletions

View File

@ -13,17 +13,4 @@ mongodb:
volumes:
- /tmp/mongodb:/data/db
expose:
- 27017
semaphore:
image: castawaylabs/semaphore:latest
environment:
MONGODB_URL: mongodb://mongodb/semaphore
REDIS_HOST: redis
expose:
- 80
ports:
- 8088:80
links:
- redis:redis
- mongodb:mongodb
- 27017

View File

@ -1,125 +0,0 @@
var config = require('./config'),
express = require('express'),
routes = require('./routes'),
http = require('http'),
path = require('path'),
mongoose = require('mongoose'),
util = require('./util'),
session = require('express-session'),
RedisStore = require('connect-redis')(session),
passport = require('passport'),
auth = require('./auth'),
bugsnag = require('bugsnag'),
socketPassport = require('passport.socketio'),
bodyParser = require('body-parser'),
logtrail = require('logtrail');
var app = exports.app = express();
logtrail.configure({
timestamps: {
enabled: false
},
stacktrace: true,
basedir: __dirname
});
console.log = logtrail.log.bind(logtrail);
var releaseStage = config.production ? "production" : "development";
bugsnag.register(config.credentials.bugsnag_key, {
notifyReleaseStages: ["production"],
releaseStage: releaseStage
});
mongoose.connect(config.credentials.db, config.credentials.db_options);
var sessionStore = new RedisStore({
host: config.credentials.redis_host,
port: config.credentials.redis_port,
ttl: 604800000,
pass: config.credentials.redis_key
});
var db = mongoose.connection
db.on('error', console.error.bind(console, 'Mongodb Connection Error:'));
db.once('open', function callback () {
if (!config.is_testing) console.log("Mongodb connection established")
});
// all environments
app.enable('trust proxy');
app.set('port', config.port || 3000); // Port
app.set('views', __dirname + '/views');
app.set('view engine', 'jade'); // Templating engine
app.set('app version', config.version); // App version
app.set('x-powered-by', false);
app.set('view cache', config.production);
config.configure(app);
app.use(function(req, res, next) {
res.set('x-frame-options', 'SAMEORIGIN');
res.set('x-xss-protection', '1; mode=block');
next();
});
var publicFolder = path.join(__dirname, '..', 'public');
app.use(require('less-middleware')(publicFolder));
app.use(require('serve-static')(publicFolder));
app.use(require('morgan')(config.production ? 'combined' : 'dev'));
app.use(bugsnag.requestHandler);
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(bodyParser.json());
app.use(require('cookie-parser')());
app.use(session({
secret: "#semaphore",
name: 'semaphore',
store: sessionStore,
proxy: true,
saveUninitialized: false,
resave: false,
cookie: {
secure: config.credentials.is_ssl,
maxAge: 604800000
}
}));
app.use(passport.initialize());
app.use(passport.session());
// Custom middleware
app.use(function(req, res, next) {
res.locals.user = req.user;
res.locals.loggedIn = res.locals.user != null;
next();
});
// routes
routes.router(app);
app.use(bugsnag.errorHandler);
var server = http.createServer(app)
server.listen(app.get('port'), function(){
console.log('Semaphore listening on port ' + app.get('port'));
});
exports.io = io = require('socket.io').listen(server)
config.init();
io.use(socketPassport.authorize({
cookieParser: require('cookie-parser'),
secret: "#semaphore",
key: 'semaphore',
store: sessionStore,
passport: passport,
fail: function(data, message, error, accept) {
accept(false);
}
}))

View File

@ -1,13 +0,0 @@
var passport = require('passport')
, models = require('./models')
, bugsnag = require('bugsnag')
passport.serializeUser(function(user, done) {
done(null, user._id);
});
passport.deserializeUser(function(id, done) {
models.User.findOne({
_id: id
}, done);
})

View File

@ -1,86 +0,0 @@
var fs = require('fs'),
env = process.env;
try {
var credentials = require('./credentials.json');
} catch (e) {
if (!(process.env.MONGODB_URL && process.env.REDIS_HOST)) {
console.log("\nNo credentials.json File or env variables!\n");
process.exit(1);
} else {
credentials = require('./credentials.default.json');
}
}
exports.credentials = credentials;
['redis_port', 'redis_host', 'redis_key', 'bugsnag_key', 'port'].forEach(function (key) {
if (env[key.toUpperCase()]) {
exports.credentials[key] = env[key.toUpperCase()];
}
});
if (env.SMTP_USER) {
exports.credentials.smtp.user = env.SMTP_USER;
}
if (env.SMTP_PASS) {
exports.credentials.smtp.pass = env.SMTP_PASS;
}
if (env.MONGODB_URL) {
exports.credentials.db = env.MONGODB_URL;
}
exports.version = require('../package.json').version;
exports.hash = 'dirty';
exports.production = process.env.NODE_ENV == "production";
exports.port = process.env.PORT || credentials.port;
exports.path = __dirname;
if (process.platform.match(/^win/) == null) {
try {
var spawn_process = require('child_process').spawn
var readHash = spawn_process('git', ['rev-parse', '--short', 'HEAD']);
readHash.stdout.on('data', function (data) {
exports.hash = data.toString().trim();
require('./app').app.locals.versionHash = exports.hash;
})
} catch (e) {
console.log("\n~= Unable to obtain git commit hash =~\n")
}
}
exports.configure = function (app) {
app.locals.pretty = exports.production // Pretty HTML outside production mode
app.locals.version = exports.version;
app.locals.versionHash = exports.hash;
app.locals.production = exports.production;
app.locals.use_analytics = credentials.use_analytics;
}
exports.init = function () {
var models = require('./models');
models.User.findOne({
}).exec(function (err, user) {
if (!user) {
console.log("Creating Admin user admin@semaphore.local!");
var admin = new models.User({
email: 'admin@semaphore.local',
username: 'semaphore',
name: 'Administrator'
});
models.User.hashPassword('CastawayLabs', function (hash) {
admin.password = hash;
admin.save();
});
}
});
models.Task.remove({
status: 'Running'
}, function (err) {
if (err) throw err;
});
}

View File

@ -1,21 +0,0 @@
{
"redis_port": 6379,
"redis_host": "127.0.0.1",
"redis_key": "",
"use_analytics": false,
"is_ssl": false,
"bugsnag_key": "",
"smtp": {
"user": "",
"pass": ""
},
"db": "mongodb://127.0.0.1/semaphore",
"db_options": {
"auto_reconnect": true,
"native_parser": true,
"server": {
"auto_reconnect": true
}
},
"port": 80
}

View File

@ -1,23 +0,0 @@
var mongoose = require('mongoose')
var ObjectId = mongoose.Schema.ObjectId;
var schema = mongoose.Schema({
created: {
type: Date,
default: Date.now
},
credential_type: {
type: String,
enum: ['ssh', 'vault', 'git']
},
name: String,
password: String,
private_key: String,
public_key: String
});
schema.index({
name: 1
});
module.exports = mongoose.model('Credential', schema);

View File

@ -1,26 +0,0 @@
var mongoose = require('mongoose')
var ObjectId = mongoose.Schema.ObjectId;
var schema = mongoose.Schema({
created: {
type: Date,
default: Date.now
},
name: String,
hostname: String,
group: {
type: ObjectId,
ref: 'HostGroup'
},
playbook: {
type: ObjectId,
ref: 'Playbook'
}
});
schema.index({
name: 1,
hostname: 1
});
module.exports = mongoose.model('Host', schema);

View File

@ -1,20 +0,0 @@
var mongoose = require('mongoose')
var ObjectId = mongoose.Schema.ObjectId;
var schema = mongoose.Schema({
created: {
type: Date,
default: Date.now
},
name: String,
playbook: {
type: ObjectId,
ref: 'Playbook'
}
});
schema.index({
name: 1
});
module.exports = mongoose.model('HostGroup', schema);

View File

@ -1,25 +0,0 @@
var mongoose = require('mongoose')
var ObjectId = mongoose.Schema.ObjectId;
var schema = mongoose.Schema({
created: {
type: Date,
default: Date.now
},
credential_type: {
type: String,
enum: ['ssh', 'vault', 'git']
},
name: String,
// vault password
password: String,
// private keys for ssh/git
private_key: String,
public_key: String
});
schema.index({
name: 1
});
module.exports = mongoose.model('Identity', schema);

View File

@ -1,22 +0,0 @@
var mongoose = require('mongoose')
var ObjectId = mongoose.Schema.ObjectId;
var schema = mongoose.Schema({
created: {
type: Date,
default: Date.now
},
playbook: {
type: ObjectId,
ref: 'Playbook'
},
name: String,
play_file: String, //x.yml
use_vault: Boolean
});
schema.index({
name: 1
});
module.exports = mongoose.model('Job', schema);

View File

@ -1,22 +0,0 @@
var mongoose = require('mongoose')
var ObjectId = mongoose.Schema.ObjectId;
var schema = mongoose.Schema({
created: {
type: Date,
default: Date.now
},
name: String,
location: String, // Git URL
vault_password: String,
identity: {
type: ObjectId,
ref: 'Identity'
}
});
schema.index({
name: 1
});
module.exports = mongoose.model('Playbook', schema);

View File

@ -1,28 +0,0 @@
var mongoose = require('mongoose')
var ObjectId = mongoose.Schema.ObjectId;
var schema = mongoose.Schema({
created: {
type: Date,
default: Date.now
},
job: {
type: ObjectId,
ref: 'Job'
},
playbook: {
type: ObjectId,
ref: 'Playbook'
},
output: String,
status: {
type: String,
enum: ['Completed', 'Failed', 'Running', 'Queued']
}
});
schema.index({
status: 1
});
module.exports = mongoose.model('Task', schema);

View File

@ -1,34 +0,0 @@
var bcrypt = require('bcrypt')
var mongoose = require('mongoose')
var ObjectId = mongoose.Schema.ObjectId;
var schema = mongoose.Schema({
created: {
type: Date,
default: Date.now
},
username: String,
name: String,
email: String,
password: String
});
schema.index({
email: 1
});
schema.statics.hashPassword = function(password, cb) {
bcrypt.hash(password, 10, function(err, hash) {
cb(hash);
});
}
schema.methods.comparePassword = function (password, cb) {
bcrypt.compare(password, this.password, function(err, res) {
// res is boolean
cb(res);
})
}
module.exports = mongoose.model('User', schema);

View File

@ -1,13 +0,0 @@
var manifest = [
'Host',
'HostGroup',
'Job',
'Identity',
'Playbook',
'Task',
'User'
];
manifest.forEach(function (model) {
module.exports[model] = require('./'+model);
});

View File

@ -1,171 +0,0 @@
var passport = require('passport')
, models = require('../models')
, validator = require('validator')
, util = require('../util')
, config = require('../config')
, async = require('async')
, express = require('express')
, mongoose = require('mongoose')
exports.unauthorized = function (app, template) {
// Unrestricted -- non-authorized people can access!
template([
'login'
], {
prefix: 'auth'
});
var auth = express.Router();
auth.post('/password', doLogin)
.get('/loggedin', isLoggedIn)
.get('/logout', doLogout)
app.use('/auth', auth);
}
exports.router = function (app) {
// Restricted -- only authorized people can access!
app.post('/auth/register', doRegister)
}
function isLoggedIn (req, res) {
res.send({
hasSession: req.user != null,
isLoggedIn: res.locals.loggedIn
});
}
function doLogin (req, res) {
var auth = req.body.auth;
var isValid = true;
if (!validator.isLength(auth, 4)) {
isValid = false;
}
// validate password
if (!validator.isLength(req.body.password, 6)) {
isValid = false;
}
if (!isValid) {
return authCallback(false, null, req, res);
}
var query = {
email: auth.toLowerCase()
};
models.User.findOne(query, function(err, user) {
if (err) {
throw err;
}
if (user == null) {
return authCallback(false, null, req, res);
}
user.comparePassword(req.body.password, function (matches) {
authCallback(matches, user, req, res);
});
})
}
function authCallback (isValid, user, req, res) {
if (!isValid) {
res.send(400, {
message: "Nope. Incorrect Credentials!"
});
return;
}
req.login(user, function(err) {
if (err) throw err;
res.send(201)
})
}
function doRegister (req, res) {
var errs = {
name: false,
email: false,
password: false,
username: false
};
var userObject = req.body.user;
if (!(userObject && typeof userObject === 'object')) {
return res.send(400, {
message: 'Invalid Request'
});
}
var email = userObject.email;
if (email) {
email = email.toLowerCase();
}
var password = userObject.password;
var username = userObject.username;
var name = userObject.name;
errs.email = !validator.isEmail(email);
errs.username = !validator.isLength(username, 3, 15);
errs.name = !validator.isLength(name, 4, 50);
if (!(username && username.match(/^[a-zA-Z0-9_-]{3,15}$/) && validator.isAscii(username))) {
// Errornous username
errs.username = true;
}
// validate password
errs.password = !validator.isLength(password, 8, 100);
if (!(errs.username == false && errs.password == false && errs.name == false && errs.email == false)) {
res.send(400, {
fields: errs,
message: ''
});
return;
}
// Register
var user = new models.User({
email: email,
username: username,
name: name
});
models.User.hashPassword(password, function (hash) {
user.password = hash;
user.save();
// log in now
req.login(user, function(err) {
if (err) throw err;
res.send({
message: "Registration Successful",
user_id: user._id
});
});
});
}
function doLogout (req, res) {
req.logout();
req.session.destroy();
res.format({
json: function() {
res.send(201)
},
html: function() {
res.redirect('/')
}
})
}

View File

@ -1,116 +0,0 @@
var models = require('../../models')
var mongoose = require('mongoose')
var express = require('express')
exports.unauthorized = function (app, template) {
template([
'view'
], {
prefix: 'job'
});
}
exports.httpRouter = function (app) {
}
exports.router = function (app) {
app.param('hostgroup_id', getHostGroup)
app.param('host_id', getHost)
var job = express.Router();
job.get('/', viewHostGroup)
.delete('/', removeHostGroup)
.get('/hosts', getHosts)
.post('/hosts', addHost)
app.use('/playbook/:playbook_id/hostgroup/:hostgroup_id', job);
var host = express.Router();
host.get('/', viewHost)
.delete('/', removeHost)
app.use('/playbook/:playbook_id/hostgroup/:hostgroup_id/host/:host_id', host);
}
/*
* hostgroup -> hosts
*/
function getHosts (req, res) {
models.Host.find({
group: req.hostgroup._id
}).sort('-created').exec(function (err, hosts) {
res.send(hosts)
})
}
function addHost (req, res) {
var host = new models.Host({
group: req.hostgroup._id,
playbook: req.playbook._id,
name: req.body.name,
hostname: req.body.hostname
});
host.save(function () {
res.send(host);
});
}
/*
* hostgroup (singular)
*/
function getHostGroup (req, res, next, id) {
models.HostGroup.findOne({
_id: id
}).exec(function (err, hostgroup) {
if (err || !hostgroup) {
return res.send(404);
}
req.hostgroup = hostgroup;
next();
});
}
function viewHostGroup (req, res) {
res.send(req.hostgroup);
}
function removeHostGroup (req, res) {
req.hostgroup.remove(function (err) {
res.send(201);
})
}
/*
* hostgroup -> host (singular)
*/
function getHost (req, res, next, id) {
models.Host.findOne({
_id: id
}).exec(function (err, host) {
console.log(host);
if (err || !host) {
return res.send(404);
}
req.group_host = host;
next();
});
}
function viewHost (req, res) {
res.send(req.group_host);
}
function removeHost (req, res) {
console.log(req.group_host)
req.group_host.remove(function (err) {
res.send(201);
})
}

View File

@ -1,44 +0,0 @@
var models = require('../../models')
var mongoose = require('mongoose')
var host = require('./host')
exports.unauthorized = function (app, template) {
template([
'hosts'
], {
prefix: 'host'
});
host.unauthorized(app, template);
}
exports.httpRouter = function (app) {
host.httpRouter(app);
}
exports.router = function (app) {
app.get('/playbook/:playbook_id/hostgroups', get)
.post('/playbook/:playbook_id/hostgroups', add)
host.router(app);
}
function get (req, res) {
models.HostGroup.find({
playbook: req.playbook._id
}).sort('-created').exec(function (err, hosts) {
res.send(hosts)
})
}
function add (req, res) {
var hostgroup = new models.HostGroup({
playbook: req.playbook._id,
name: req.body.name
});
hostgroup.save(function () {
res.send(hostgroup);
});
}

View File

@ -1,52 +0,0 @@
var models = require('../../models')
var mongoose = require('mongoose')
var identity = require('./identity')
var validator = require('validator')
exports.unauthorized = function (app, template) {
template([
'add',
'list'
], {
prefix: 'identity'
});
identity.unauthorized(app, template);
}
exports.httpRouter = function (app) {
identity.httpRouter(app);
}
exports.router = function (app) {
app.get('/identities', get)
.post('/identities', add)
identity.router(app);
}
function get (req, res) {
models.Identity.find({
}).sort('-created').select('-public_key -private_key -password').exec(function (err, identities) {
res.send(identities)
})
}
function add (req, res) {
if (!validator.isLength(req.body.name, 1)) {
return res.send(400);
}
var identity = new models.Identity({
name: req.body.name,
password: req.body.password,
private_key: req.body.private_key,
public_key: req.body.public_key
});
identity.save(function () {
res.send(identity);
});
}

View File

@ -1,56 +0,0 @@
var models = require('../../models')
var mongoose = require('mongoose')
var express = require('express')
exports.unauthorized = function (app, template) {
template([
'view'
], {
prefix: 'identity'
});
}
exports.httpRouter = function (app) {
}
exports.router = function (app) {
var identity = express.Router();
identity.get('/', view)
.put('/', save)
.delete('/', remove)
app.param('identity_id', get)
app.use('/identity/:identity_id', identity);
}
function get (req, res, next, id) {
models.Identity.findOne({
_id: id
}).select('-private_key -password').exec(function (err, identity) {
if (err || !identity) {
return res.send(404);
}
req.identity = identity;
next();
});
}
function view (req, res) {
res.send(req.identity);
}
function save (req, res) {
req.identity.name = req.body.name;
req.identity.password = req.body.password;
req.identity.save();
res.send(201);
}
function remove (req, res) {
req.identity.remove(function (err) {
res.send(201);
})
}

View File

@ -1,66 +0,0 @@
var util = require('../util')
auth = require('./auth'),
playbooks = require('./playbook/playbooks'),
profile = require('./profile'),
identities = require('./identity/identities'),
jobs = require('./job/jobs'),
hosts = require('./host/hosts'),
tasks = require('./task/tasks'),
users = require('./user/users')
exports.router = function(app) {
var templates = require('../templates')(app);
templates.route([
auth,
playbooks,
identities,
jobs,
hosts,
tasks,
users
]);
templates.add('homepage')
templates.add('abstract')
templates.setup();
app.get('/', layout);
app.all('*', util.authorized);
// Handle HTTP reqs
playbooks.httpRouter(app);
identities.httpRouter(app);
jobs.httpRouter(app);
hosts.httpRouter(app);
tasks.httpRouter(app);
users.httpRouter(app);
// only json beyond this point
app.get('*', function(req, res, next) {
res.format({
json: function() {
next()
},
html: function() {
layout(req, res);
}
});
});
auth.router(app);
playbooks.router(app);
profile.router(app);
identities.router(app);
jobs.router(app);
hosts.router(app);
tasks.router(app);
users.router(app);
}
function layout (req, res) {
if (res.locals.loggedIn) {
res.render('layout')
} else {
res.render('auth');
}
}

View File

@ -1,47 +0,0 @@
var models = require('../../models')
var mongoose = require('mongoose')
var express = require('express')
exports.unauthorized = function (app, template) {
template([
'view'
], {
prefix: 'job'
});
}
exports.httpRouter = function (app) {
}
exports.router = function (app) {
var job = express.Router();
job.get('/', view)
.delete('/', remove)
app.param('job_id', get)
app.use('/playbook/:playbook_id/job/:job_id', job);
}
function get (req, res, next, id) {
models.Job.findOne({
_id: id
}).exec(function (err, job) {
if (err || !job) {
return res.send(404);
}
req.job = job;
next();
});
}
function view (req, res) {
res.send(req.job);
}
function remove (req, res) {
req.job.remove(function (err) {
res.send(201);
})
}

View File

@ -1,46 +0,0 @@
var models = require('../../models')
var mongoose = require('mongoose')
var job = require('./job')
exports.unauthorized = function (app, template) {
template([
'jobs'
], {
prefix: 'job'
});
job.unauthorized(app, template);
}
exports.httpRouter = function (app) {
job.httpRouter(app);
}
exports.router = function (app) {
app.get('/playbook/:playbook_id/jobs', get)
.post('/playbook/:playbook_id/jobs', add)
job.router(app);
}
function get (req, res) {
models.Job.find({
playbook: req.playbook._id
}).sort('-created').exec(function (err, jobs) {
res.send(jobs)
})
}
function add (req, res) {
var job = new models.Job({
playbook: req.playbook._id,
name: req.body.name,
play_file: req.body.play_file,
use_vault: req.body.use_vault
})
job.save(function () {
res.send(job);
});
}

View File

@ -1,69 +0,0 @@
var models = require('../../models')
var mongoose = require('mongoose')
var express = require('express')
exports.unauthorized = function (app, template) {
template([
'tasks',
'jobs',
'hosts',
'view'
], {
prefix: 'playbook'
});
}
exports.httpRouter = function (app) {
}
exports.router = function (app) {
var playbook = express.Router();
playbook.get('/', view)
.put('/', save)
.delete('/', remove)
app.param('playbook_id', getPlaybook)
app.use('/playbook/:playbook_id', playbook);
}
function getPlaybook (req, res, next, id) {
models.Playbook.findOne({
_id: id
}).select('-vault_password').exec(function (err, playbook) {
if (err || !playbook) {
return res.send(404);
}
req.playbook = playbook;
next();
});
}
function view (req, res) {
res.send(req.playbook);
}
function save (req, res) {
req.playbook.name = req.body.name;
req.playbook.location = req.body.location;
if (typeof req.body.vault_password == 'string' && req.body.vault_password.length > 0) {
req.playbook.vault_password = req.body.vault_password;
}
if (typeof req.body.identity == 'string' && req.body.identity.length > 0) {
try {
req.playbook.identity = mongoose.Types.ObjectId(req.body.identity);
} catch (e) {}
}
req.playbook.save();
res.send(201);
}
function remove (req, res) {
req.playbook.remove(function (err) {
res.send(201);
})
}

View File

@ -1,51 +0,0 @@
var models = require('../../models')
var mongoose = require('mongoose')
var playbook = require('./playbook')
exports.unauthorized = function (app, template) {
template([
'add',
'list'
], {
prefix: 'playbook'
});
playbook.unauthorized(app, template);
}
exports.httpRouter = function (app) {
playbook.httpRouter(app);
}
exports.router = function (app) {
app.get('/playbooks', getPlaybooks)
.post('/playbooks', addPlaybook)
playbook.router(app);
}
function getPlaybooks (req, res) {
models.Playbook.find({
}).sort('-created').select('-vault_password').exec(function (err, playbooks) {
res.send(playbooks)
})
}
function addPlaybook (req, res) {
var playbook = new models.Playbook({
name: req.body.name,
location: req.body.location,
vault_password: req.body.vault_password
})
if (typeof req.body.identity == 'string' && req.body.identity.length > 0) {
try {
playbook.identity = mongoose.Types.ObjectId(req.body.identity);
} catch (e) {}
}
playbook.save(function () {
res.send(playbook);
});
}

View File

@ -1,13 +0,0 @@
exports.router = function (app, template) {
app.get('/profile', getProfile)
}
function getProfile (req, res) {
res.send({
_id: req.user._id,
name: req.user.name,
username: req.user.username,
email: req.user.email
})
}

View File

@ -1,63 +0,0 @@
var models = require('../../models')
var mongoose = require('mongoose')
var express = require('express')
var jobRunner = require('../../runner');
exports.unauthorized = function (app, template) {
template([
'view'
], {
prefix: 'task'
});
}
exports.httpRouter = function (app) {
}
exports.router = function (app) {
var task = express.Router();
task.get('/', view)
.delete('/', remove)
app.param('task_id', get)
app.use('/playbook/:playbook_id/job/:job_id/task/:task_id', task);
}
function get (req, res, next, id) {
models.Task.findOne({
_id: id
}).exec(function (err, task) {
if (err || !task) {
return res.send(404);
}
req.task = task;
next();
});
}
function view (req, res) {
res.send(req.task);
}
function remove (req, res) {
if (req.task.status == 'Running') {
return res.send(400, 'Job is Running.');
}
jobRunner.queue.pause();
for (var i = 0; i < jobRunner.queue.tasks.length; i++) {
if (jobRunner.queue.tasks[i].data._id.toString() == req.task._id.toString()) {
// This is our task
jobRunner.queue.tasks.splice(i, 1);
break;
}
}
jobRunner.queue.resume();
req.task.remove(function (err) {
res.send(201);
})
}

View File

@ -1,80 +0,0 @@
var models = require('../../models')
var mongoose = require('mongoose')
var task = require('./task')
var app = require('../../app')
var jobRunner = require('../../runner');
exports.unauthorized = function (app, template) {
template([
'tasks'
], {
prefix: 'task'
});
task.unauthorized(app, template);
}
exports.httpRouter = function (app) {
task.httpRouter(app);
}
exports.router = function (app) {
app.get('/playbook/:playbook_id/tasks', getTasks)
.get('/playbook/:playbook_id/job/:job_id/tasks', get)
.post('/playbook/:playbook_id/job/:job_id/tasks', add)
.post('/playbook/:playbook_id/job/:job_id/run', runJob)
task.router(app);
}
function get (req, res) {
models.Task.find({
job: req.job._id
}).populate('job').sort('-created').exec(function (err, tasks) {
res.send(tasks)
})
}
function getTasks (req, res) {
models.Task.find({
playbook: req.playbook._id
}).populate('job').sort('-created').exec(function (err, tasks) {
res.send(tasks)
})
}
function add (req, res) {
var task = new models.Task({
job: req.job._id,
status: 'Queued'
})
task.save(function () {
res.send(task);
});
}
function runJob (req, res) {
var task = new models.Task({
job: req.job._id,
playbook: req.playbook._id,
status: 'Queued'
});
task.save(function (err) {
task.populate('job', function () {
app.io.emit('playbook.update', {
task_id: task._id,
playbook_id: req.playbook._id,
task: task
});
jobRunner.queue.push(task);
})
});
res.send(201)
}

View File

@ -1,60 +0,0 @@
var models = require('../../models'),
mongoose = require('mongoose'),
express = require('express');
exports.unauthorized = function (app, template) {
template([
'view'
], {
prefix: 'user'
});
}
exports.httpRouter = function (app) {
}
exports.router = function (app) {
var user = express.Router();
user.get('/', view)
.put('/', save)
.delete('/', remove)
app.param('user_id', get)
app.use('/user/:user_id', user);
}
function get (req, res, next, id) {
models.User.findOne({
_id: id
})
.select('-password')
.exec(function (err, user) {
if (err || !user) {
return res.send(404);
}
req._user = user;
next();
});
}
function view (req, res) {
res.send(req._user);
}
function save (req, res) {
req._user.name = req.body.name;
models.User.hashPassword(req.body.password, function (hash) {
req._user.password = hash;
});
req._user.save();
res.send(201);
}
function remove (req, res) {
req._user.remove(function (err) {
res.send(201);
})
}

View File

@ -1,63 +0,0 @@
var models = require('../../models'),
mongoose = require('mongoose'),
validator = require('validator'),
user = require('./user');
exports.unauthorized = function (app, template) {
template([
'add',
'list'
], {
prefix: 'user'
});
user.unauthorized(app, template);
}
exports.httpRouter = function (app) {
user.httpRouter(app);
}
exports.router = function (app) {
app.get('/users', get)
.post('/users', add)
user.router(app);
}
function get (req, res) {
models.User.find({})
.sort('-created')
.select('-password')
.exec(function (err, users) {
res.send(users);
});
}
function add (req, res) {
var user = new models.User({
name: req.body.name,
email: req.body.email,
username: req.body.username
});
if (user.name.length == 0 || user.name.email == 0) {
return res.send(400);
}
models.User.findOne({
email: user.email
}, function (_, existingUser) {
if (existingUser) {
return res.send(400);
}
models.User.hashPassword(req.body.password, function (hash) {
user.password = hash;
user.save(function () {
res.send(user);
});
});
});
}

View File

@ -1,242 +0,0 @@
var async = require('async'),
fs = require('fs'),
spawn = require('child_process').spawn;
var config = require('./config'),
models = require('./models'),
app = require('./app');
var home = process.env.HOME + '/';
var user = process.env.USER;
exports.queue = async.queue(worker, 1);
function worker (task, callback) {
// Task is to be model Task
// Download the git project
// Set up hosts file
// Set up vault pwd file
// Set up private key
// Execute ansible-playbook -i hosts --ask-vault-pass --private-key=~/.ssh/ansible_key task.yml
async.waterfall([
function (done) {
task.populate('job', function (err) {
done(err, task);
})
},
function (task, done) {
models.Playbook.findOne({ _id: task.playbook }, function (err, playbook) {
done(err, task, playbook)
});
},
function (task, playbook, done) {
// mark task as running and send an update via socketio
task.status = 'Running';
app.io.emit('playbook.update', {
task_id: task._id,
playbook_id: playbook._id,
task: task
});
models.Task.update({
_id: task._id
}, {
$set: {
status: 'Running'
}
}, function (err) {
done(err, task, playbook);
});
},
function (task, playbook, done) {
playbook.populate('identity', function (err) {
done(err, task, playbook)
});
},
installHostKeys,
pullGit,
setupHosts,
setupVault,
playTheBook
], function (err) {
if (err) {
task.status = 'Failed';
} else {
task.status = 'Completed';
}
var rmrf = spawn('rm', ['-rf', home + 'playbook_'+task.playbook])
rmrf.on('close', function () {
app.io.emit('playbook.update', {
task_id: task._id,
playbook_id: task.playbook,
task: task
});
task.save();
callback(err);
});
});
}
function installHostKeys (task, playbook, done) {
// Install the private key
playbookOutputHandler.call(task, "Updating SSH Keys\n");
var location = home + '.ssh/id_rsa';
fs.mkdir( home + '.ssh', 448, function() {
async.parallel([
function (done) {
fs.writeFile(location, playbook.identity.private_key, {
mode: 384 // base 8 = 0600
}, done);
},
function (done) {
fs.writeFile(location+'.pub', playbook.identity.public_key, {
mode: 420 // base 8 = 0644
}, done);
},
function (done) {
var config = "Host *\n\
StrictHostKeyChecking no\n\
CheckHostIp no\n\
PasswordAuthentication no\n\
PreferredAuthentications publickey\n";
fs.writeFile(home + '.ssh/config', config, {
mode: 420 // 0644
}, done);
}
], function (err) {
playbookOutputHandler.call(task, "SSH Keys Updated.\n");
done(err, task, playbook)
});
});
}
function pullGit (task, playbook, done) {
// Pull from git
playbookOutputHandler.call(task, "\nDownloading Playbook.\n");
var install = spawn(config.path+"/scripts/pullGit.sh", [playbook.location, 'playbook_'+playbook._id], {
cwd: home,
env: {
HOME: home,
OLDPWD: home,
PWD: home,
LOGNAME: user,
USER: user,
TERM: 'xterm',
SHELL: '/bin/bash',
PATH: process.env.PATH+':/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin',
LANG: 'en_GB.UTF-8'
}
});
install.stdout.on('data', playbookOutputHandler.bind(task));
install.stderr.on('data', playbookOutputHandler.bind(task));
install.on('close', function(code) {
playbookOutputHandler.call(task, "\n\nPlaybook Downloaded.\n");
done(null, task, playbook);
});
}
function setupHosts (task, playbook, done) {
var hostfile = '';
models.HostGroup.find({
playbook: playbook._id
}, function (err, hostgroups) {
async.each(hostgroups, function (group, cb) {
models.Host.find({
group: group._id
}, function (err, hosts) {
hostfile += "["+group.name+"]\n";
for (var i = 0; i < hosts.length; i++) {
hostfile += hosts[i].hostname+"\n";
}
cb();
});
}, function () {
playbookOutputHandler.call(task, "\nSet up Ansible Hosts file with contents:\n"+hostfile+"\n");
fs.writeFile(home + 'playbook_'+playbook._id+'/semaphore_hosts', hostfile, function (err) {
done(err, task, playbook);
});
});
});
}
function setupVault (task, playbook, done) {
fs.writeFile(home + 'playbook_'+playbook._id+'/semaphore_vault_pwd', playbook.vault_password, function (err) {
done(err, task, playbook);
})
}
function playTheBook (task, playbook, done) {
playbookOutputHandler.call(task, "\nStarting play "+task.job.play_file+".\n");
var args = ['-i', 'semaphore_hosts'];
if (task.job.use_vault && playbook.vault_password && playbook.vault_password.length > 0) {
args.push('--vault-password-file='+'semaphore_vault_pwd');
}
// private key to login to server[s]
args.push('--private-key=' + home + '.ssh/id_rsa');
// the playbook file
args.push(task.job.play_file);
var playbook = spawn("ansible-playbook", args, {
cwd: home + 'playbook_'+playbook._id,
env: {
HOME: home,
OLDPWD: home,
PWD: home + 'playbook_'+playbook._id,
LOGNAME: user,
USER: user,
TERM: 'xterm',
SHELL: '/bin/bash',
PATH: process.env.PATH+':/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin',
LANG: 'en_GB.UTF-8',
PYTHONPATH: process.env.PYTHONPATH,
PYTHONUNBUFFERED: 1
}
});
playbook.stdout.on('data', playbookOutputHandler.bind(task));
playbook.stderr.on('data', playbookOutputHandler.bind(task));
playbook.on('close', function(code) {
console.log('done.', code);
if (code !== 0) {
// Task failed
return done('Failed with code '+code);
}
done();
});
}
function playbookOutputHandler (chunk) {
chunk = chunk.toString('utf8');
if (!this.output) {
this.output = "";
}
this.output += chunk;
app.io.emit('playbook.output', {
task_id: this._id,
playbook_id: this.playbook,
output: chunk
});
console.log(chunk);
}

View File

@ -1,10 +0,0 @@
#!/bin/bash
printf "#\041/bin/bash\nssh -i \"$HOME/.ssh/id_rsa\" \$1 \$2\n" > $HOME/ssh_wrapper.sh
chmod +x $HOME/ssh_wrapper.sh
cd $HOME
GIT_SSH=$HOME/ssh_wrapper.sh git clone "$1" $2
if [ $? -ne 0 ]; then
exit 2
fi

View File

@ -1,67 +0,0 @@
// By Matej Kramny <matej@matej.me>
// Please leave this comment here.
module.exports = function(app) {
var self = this;
self.routes = [];
self.app = app
self.route = function (controller) {
if (!(controller instanceof Array)) {
controller = [controller];
}
for (c in controller) {
controller[c].unauthorized(self.app, self.add.bind(self));
}
}
self.makeRoute = function(route, view) {
return {
route: route,
view: view
}
}
self.add = function (routes, opts) {
var args = arguments;
var prefix = opts ? opts.prefix : null;
if (!prefix) prefix = '';
else prefix += '/';
if (typeof routes === 'string') {
self.routes.push(self.makeRoute(prefix+routes, prefix+routes));
return;
}
if (Object.prototype.toString.call(routes) == '[object Object]') {
self.routes.push(routes);
return;
}
for (var i = 0; i < routes.length; i++) {
var r;
if (typeof routes[i] == 'string') {
r = self.makeRoute(prefix+routes[i], prefix+routes[i]);
} else if (routes[i] instanceof Array) {
r = self.makeRoute(prefix+routes[i][0], routes[i][1]);
} else {
r = routes[i]
}
self.routes.push(r);
}
}
self.setup = function () {
for (var i = 0; i < routes.length; i++) {
app.get('/view/'+routes[i].route, self.getView.bind(routes[i]));
}
}
self.getView = function (req, res) {
res.render(this.view);
}
return self;
}

View File

@ -1,16 +0,0 @@
exports.authorized = function (req, res, next) {
if (res.locals.loggedIn) {
next()
} else {
res.format({
html: function() {
res.render('auth');
},
json: function() {
res.send(403, {
message: "Unauthorized"
})
}
})
}
}

View File

@ -1,8 +1,7 @@
[
"/vendor/jquery.min",
"/vendor/angular-1.2.29",
"/vendor/angular-couch-potato/dist/angular-couch-potato",
"/vendor/angular-ui-router/release/angular-ui-router.min",
"/vendor/angular.min",
"/vendor/angular-couch-potato",
"/vendor/angular-ui-router.min",
"/vendor/angular-ui.min",
"/vendor/angular-loading-bar/loading-bar",
"/vendor/moment/moment.min",

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,3 @@
.jumbotron: .container
h1 Hey there,
p We're logging you out <i class="fa fa-fw fa-spin fa-spinner"></i>

View File

@ -0,0 +1,11 @@
.container-fluid
.row
.col-md-8
ul
li Scheduled job {x} was successfully executed. Took 30s
.col-md-4
.panel.panel-default
.panel-heading Projects
button.btn.btn-default.btn-xs.pull-right: i.fa.fa-fw.fa-plus
ul.list-group
li.list-group-item(ng-repeat="project in projects") {{ project.name }}

View File

@ -1,16 +0,0 @@
h2 If you're just getting started
ol
li Read about how Ansible Playbooks work, and try them out
li Host your playbook on a git repository (e.g. github / gitlab)
li Create a deploy key
li Create an Identity (inside Semaphore). Add your Deploy Private and Public keys
li Create a playbook, set the git url to your repository
li Define hosts which you would like your playbook to use
li Create a job that runs your playbook task file
li Press Run
h3 Playbook Variables
p This is a bit advanced, but a good practice may be providing your variables through <code>group_vars/{host-group}.yml</code>. These can be encrypted using ansible-vault. The vault password can then be provided using a Semaphore Identity.

View File

@ -10,24 +10,25 @@ html(lang="en" ng-app="semaphore")
link(rel="stylesheet" href="/public/css/semaphore.css")
body
.navbar.navbar-default(ng-if="loggedIn"): .container
.navbar.navbar-default(ng-if="loggedIn"): .container-fluid
.navbar-header
a.navbar-brand(ui-sref="homepage") Semaphore
a.navbar-brand(ui-sref="homepage") SEMAPHORE
nav.nav.navbar-nav
li: a(ui-sref="playbooks") Playbooks
li: a(ui-sref="identities.list") Identities
li: a(ui-sref="users.list") Users
ul.nav.navbar-nav.navbar-right(style="margin-right: 0;")
li(uib-dropdown)
a(uib-dropdown-toggle)
i.fa.fa-fw.fa-user
i.caret
ul(uib-dropdown-menu)
li: a(ui-sref="user") Profile
li: a Settings
li.divider
li: a(ui-sref="auth.logout") Log out
nav.nav.navbar-nav.pull-right
li: a(ui-sref="addPlaybook") Add Playbook
.container
.col-lg-12
ui-view(autoscroll="false")
p.lead.text-center
i.fa.fa-spin.fa-cog
| Loading...
ui-view(autoscroll="false")
p.lead.text-center
i.fa.fa-spin.fa-cog
| Loading...
footer: a.github-corner(href="https://github.com/ansible-semaphore/semaphore" target="_blank" title="Ansible-Semaphore on GitHub")
svg(width="80" height="80" viewbox="0 0 250 250")

View File

@ -7,6 +7,10 @@ app.config(['$httpProvider', function ($httpProvider) {
return {
request: function (request) {
var url = request.url;
if (url.indexOf('/tpl/') !== -1) {
request.url = url = url.replace('/tpl/', '/public/html/');
}
if (!(url.indexOf('/public') !== -1 || url.indexOf('://') !== -1)) {
request.url = "/api" + request.url;
request.headers['Cache-Control'] = 'no-cache';
@ -51,7 +55,7 @@ app.run(['$rootScope', '$window', '$couchPotato', '$injector', '$state', '$http'
$rootScope.user = user;
$rootScope.loggedIn = true;
}, function () {
$state.go('login');
$state.go('auth.login');
});
}

View File

@ -0,0 +1,9 @@
define(function () {
app.registerController('DashboardCtrl', function ($scope, $http) {
$scope.projects = [{
name: 'Hey there'
}, {
name: 'Test project'
}];
})
})

View File

@ -1,11 +1,27 @@
app.config(function ($stateProvider, $urlRouterProvider, $locationProvider, $couchPotatoProvider) {
$stateProvider.state('login', {
$stateProvider.state('auth', {
url: '/auth',
abstract: true,
templateUrl: '/tpl/abstract.html'
})
.state('auth.login', {
url: '/login',
pageTitle: "Sign In",
templateUrl: "/public/html/auth/login.html",
templateUrl: '/tpl/auth/login.html',
controller: "SignInCtrl",
resolve: {
dummy: $couchPotatoProvider.resolveDependencies(['controllers/auth/login'])
$d: $couchPotatoProvider.resolveDependencies(['controllers/auth/login'])
}
})
.state('auth.logout', {
url: '/logout',
public: true,
templateUrl: '/tpl/auth/logout.html',
controller: ['$http', '$rootScope', '$state', function ($http, $rootScope, $state) {
$http.post('/auth/logout').success(function () {
$rootScope.refreshUser();
$state.go('auth.login');
});
}]
});
});

View File

@ -7,19 +7,15 @@ app.config(function ($stateProvider, $urlRouterProvider, $locationProvider, $cou
$urlRouterProvider.otherwise('/');
$stateProvider
.state('homepage', {
.state('dashboard', {
url: '/',
pageTitle: 'Homepage',
templateUrl: "/public/html/homepage.html"
})
.state('logout', {
url: '/logout',
pageTitle: 'Log Out',
controller: function ($scope) {
window.location = "/logout";
pageTitle: 'Dashboard',
templateUrl: '/tpl/dashboard.html',
controller: 'DashboardCtrl',
resolve: {
$d: $couchPotatoProvider.resolveDependencies(['controllers/dashboard'])
}
})
});
});
app.run(function($rootScope, $state, $stateParams, $http) {

View File

@ -2,7 +2,6 @@ package auth
import (
"database/sql"
"fmt"
"net/mail"
"strings"
"time"
@ -38,7 +37,6 @@ func Login(c *gin.Context) {
}
query, args, _ := q.ToSql()
fmt.Println(query, args)
var user models.User
if err := database.Mysql.SelectOne(&user, query, args...); err != nil {
@ -65,3 +63,12 @@ func Login(c *gin.Context) {
c.AbortWithStatus(204)
}
func Logout(c *gin.Context) {
session := c.MustGet("session").(models.Session)
if err := database.Redis.Del("session:" + session.ID).Err(); err != nil {
panic(err)
}
c.AbortWithStatus(204)
}

View File

@ -23,6 +23,7 @@ func Route(r *gin.Engine) {
func(api *gin.RouterGroup) {
api.POST("/login", auth.Login)
api.POST("/logout", auth.Logout)
}(api.Group("/auth"))
api.Use(MustAuthenticate)