Merge branch 'master' into star

This commit is contained in:
Jimmy Zelinskie 2015-02-18 17:36:58 -05:00
commit 917dd6b674
229 changed files with 10807 additions and 3003 deletions

View file

@ -0,0 +1,272 @@
function RepoBuildCtrl($scope, Restangular, ApiService, $routeParams, $rootScope, $location, $interval, $sanitize,
ansi2html, AngularViewArray, AngularPollChannel) {
var namespace = $routeParams.namespace;
var name = $routeParams.name;
// Watch for changes to the current parameter.
$scope.$on('$routeUpdate', function(){
if ($location.search().current) {
$scope.setCurrentBuild($location.search().current, false);
}
});
$scope.builds = null;
$scope.pollChannel = null;
$scope.buildDialogShowCounter = 0;
$scope.showNewBuildDialog = function() {
$scope.buildDialogShowCounter++;
};
$scope.handleBuildStarted = function(newBuild) {
if (!$scope.builds) { return; }
$scope.builds.unshift(newBuild);
$scope.setCurrentBuild(newBuild['id'], true);
};
$scope.adjustLogHeight = function() {
var triggerOffset = 0;
if ($scope.currentBuild && $scope.currentBuild.trigger) {
triggerOffset = 85;
}
$('.build-logs').height($(window).height() - 415 - triggerOffset);
};
$scope.askRestartBuild = function(build) {
$('#confirmRestartBuildModal').modal({});
};
$scope.askCancelBuild = function(build) {
bootbox.confirm('Are you sure you want to cancel this build?', function(r) {
if (r) {
var params = {
'repository': namespace + '/' + name,
'build_uuid': build.id
};
ApiService.cancelRepoBuild(null, params).then(function() {
if (!$scope.builds) { return; }
$scope.builds.splice($.inArray(build, $scope.builds), 1);
if ($scope.builds.length) {
$scope.currentBuild = $scope.builds[0];
} else {
$scope.currentBuild = null;
}
}, ApiService.errorDisplay('Cannot cancel build'));
}
});
};
$scope.restartBuild = function(build) {
$('#confirmRestartBuildModal').modal('hide');
var subdirectory = '';
if (build['job_config']) {
subdirectory = build['job_config']['build_subdir'] || '';
}
var data = {
'file_id': build['resource_key'],
'subdirectory': subdirectory,
'docker_tags': build['job_config']['docker_tags']
};
if (build['pull_robot']) {
data['pull_robot'] = build['pull_robot']['name'];
}
var params = {
'repository': namespace + '/' + name
};
ApiService.requestRepoBuild(data, params).then(function(newBuild) {
if (!$scope.builds) { return; }
$scope.builds.unshift(newBuild);
$scope.setCurrentBuild(newBuild['id'], true);
});
};
$scope.hasLogs = function(container) {
return container.logs.hasEntries;
};
$scope.setCurrentBuild = function(buildId, opt_updateURL) {
if (!$scope.builds) { return; }
// Find the build.
for (var i = 0; i < $scope.builds.length; ++i) {
if ($scope.builds[i].id == buildId) {
$scope.setCurrentBuildInternal(i, $scope.builds[i], opt_updateURL);
return;
}
}
};
$scope.processANSI = function(message, container) {
var filter = container.logs._filter = (container.logs._filter || ansi2html.create());
// Note: order is important here.
var setup = filter.getSetupHtml();
var stream = filter.addInputToStream(message);
var teardown = filter.getTeardownHtml();
return setup + stream + teardown;
};
$scope.setCurrentBuildInternal = function(index, build, opt_updateURL) {
if (build == $scope.currentBuild) { return; }
$scope.logEntries = null;
$scope.logStartIndex = null;
$scope.currentParentEntry = null;
$scope.currentBuild = build;
if (opt_updateURL) {
if (build) {
$location.search('current', build.id);
} else {
$location.search('current', null);
}
}
// Timeout needed to ensure the log element has been created
// before its height is adjusted.
setTimeout(function() {
$scope.adjustLogHeight();
}, 1);
// Stop any existing polling.
if ($scope.pollChannel) {
$scope.pollChannel.stop();
}
// Create a new channel for polling the build status and logs.
var conductStatusAndLogRequest = function(callback) {
getBuildStatusAndLogs(build, callback);
};
$scope.pollChannel = AngularPollChannel.create($scope, conductStatusAndLogRequest, 5 * 1000 /* 5s */);
$scope.pollChannel.start();
};
var processLogs = function(logs, startIndex, endIndex) {
if (!$scope.logEntries) { $scope.logEntries = []; }
// If the start index given is less than that requested, then we've received a larger
// pool of logs, and we need to only consider the new ones.
if (startIndex < $scope.logStartIndex) {
logs = logs.slice($scope.logStartIndex - startIndex);
}
for (var i = 0; i < logs.length; ++i) {
var entry = logs[i];
var type = entry['type'] || 'entry';
if (type == 'command' || type == 'phase' || type == 'error') {
entry['logs'] = AngularViewArray.create();
entry['index'] = $scope.logStartIndex + i;
$scope.logEntries.push(entry);
$scope.currentParentEntry = entry;
} else if ($scope.currentParentEntry) {
$scope.currentParentEntry['logs'].push(entry);
}
}
return endIndex;
};
var getBuildStatusAndLogs = function(build, callback) {
var params = {
'repository': namespace + '/' + name,
'build_uuid': build.id
};
ApiService.getRepoBuildStatus(null, params, true).then(function(resp) {
if (build != $scope.currentBuild) { callback(false); return; }
// Note: We use extend here rather than replacing as Angular is depending on the
// root build object to remain the same object.
var matchingBuilds = $.grep($scope.builds, function(elem) {
return elem['id'] == resp['id']
});
var currentBuild = matchingBuilds.length > 0 ? matchingBuilds[0] : null;
if (currentBuild) {
currentBuild = $.extend(true, currentBuild, resp);
} else {
currentBuild = resp;
$scope.builds.push(currentBuild);
}
// Load the updated logs for the build.
var options = {
'start': $scope.logStartIndex
};
ApiService.getRepoBuildLogsAsResource(params, true).withOptions(options).get(function(resp) {
if (build != $scope.currentBuild) { callback(false); return; }
// Process the logs we've received.
$scope.logStartIndex = processLogs(resp['logs'], resp['start'], resp['total']);
// If the build status is an error, open the last two log entries.
if (currentBuild['phase'] == 'error' && $scope.logEntries.length > 1) {
var openLogEntries = function(entry) {
if (entry.logs) {
entry.logs.setVisible(true);
}
};
openLogEntries($scope.logEntries[$scope.logEntries.length - 2]);
openLogEntries($scope.logEntries[$scope.logEntries.length - 1]);
}
// If the build phase is an error or a complete, then we mark the channel
// as closed.
callback(currentBuild['phase'] != 'error' && currentBuild['phase'] != 'complete');
}, function() {
callback(false);
});
}, function() {
callback(false);
});
};
var fetchRepository = function() {
var params = {'repository': namespace + '/' + name};
$rootScope.title = 'Loading Repository...';
$scope.repository = ApiService.getRepoAsResource(params).get(function(repo) {
if (!repo.can_write) {
$rootScope.title = 'Unknown builds';
$scope.accessDenied = true;
return;
}
$rootScope.title = 'Repository Builds';
$scope.repo = repo;
getBuildInfo();
});
};
var getBuildInfo = function(repo) {
var params = {
'repository': namespace + '/' + name
};
ApiService.getRepoBuilds(null, params).then(function(resp) {
$scope.builds = resp.builds;
if ($location.search().current) {
$scope.setCurrentBuild($location.search().current, false);
} else if ($scope.builds.length > 0) {
$scope.setCurrentBuild($scope.builds[0].id, true);
}
});
};
fetchRepository();
}

View file

@ -0,0 +1,282 @@
function SetupCtrl($scope, $timeout, ApiService, Features, UserService, ContainerService, CoreDialog) {
if (!Features.SUPER_USERS) {
return;
}
$scope.HOSTNAME_REGEX = '^[a-zA-Z-0-9\.]+(:[0-9]+)?$';
$scope.validateHostname = function(hostname) {
if (hostname.indexOf('127.0.0.1') == 0 || hostname.indexOf('localhost') == 0) {
return 'Please specify a non-localhost hostname. "localhost" will refer to the container, not your machine.'
}
return null;
};
// Note: The values of the enumeration are important for isStepFamily. For example,
// *all* states under the "configuring db" family must start with "config-db".
$scope.States = {
// Loading the state of the product.
'LOADING': 'loading',
// The configuration directory is missing.
'MISSING_CONFIG_DIR': 'missing-config-dir',
// The config.yaml exists but it is invalid.
'INVALID_CONFIG': 'config-invalid',
// DB is being configured.
'CONFIG_DB': 'config-db',
// DB information is being validated.
'VALIDATING_DB': 'config-db-validating',
// DB information is being saved to the config.
'SAVING_DB': 'config-db-saving',
// A validation error occurred with the database.
'DB_ERROR': 'config-db-error',
// Database is being setup.
'DB_SETUP': 'setup-db',
// Database setup has succeeded.
'DB_SETUP_SUCCESS': 'setup-db-success',
// An error occurred when setting up the database.
'DB_SETUP_ERROR': 'setup-db-error',
// The container is being restarted for the database changes.
'DB_RESTARTING': 'setup-db-restarting',
// A superuser is being configured.
'CREATE_SUPERUSER': 'create-superuser',
// The superuser is being created.
'CREATING_SUPERUSER': 'create-superuser-creating',
// An error occurred when setting up the superuser.
'SUPERUSER_ERROR': 'create-superuser-error',
// The superuser was created successfully.
'SUPERUSER_CREATED': 'create-superuser-created',
// General configuration is being setup.
'CONFIG': 'config',
// The configuration is fully valid.
'VALID_CONFIG': 'valid-config',
// The container is being restarted for the configuration changes.
'CONFIG_RESTARTING': 'config-restarting',
// The product is ready for use.
'READY': 'ready'
}
$scope.csrf_token = window.__token;
$scope.currentStep = $scope.States.LOADING;
$scope.errors = {};
$scope.stepProgress = [];
$scope.hasSSL = false;
$scope.hostname = null;
$scope.$watch('currentStep', function(currentStep) {
$scope.stepProgress = $scope.getProgress(currentStep);
switch (currentStep) {
case $scope.States.CONFIG:
$('#setupModal').modal('hide');
break;
case $scope.States.MISSING_CONFIG_DIR:
$scope.showMissingConfigDialog();
break;
case $scope.States.INVALID_CONFIG:
$scope.showInvalidConfigDialog();
break;
case $scope.States.DB_SETUP:
$scope.performDatabaseSetup();
// Fall-through.
case $scope.States.CREATE_SUPERUSER:
case $scope.States.DB_RESTARTING:
case $scope.States.CONFIG_DB:
case $scope.States.VALID_CONFIG:
case $scope.States.READY:
$('#setupModal').modal({
keyboard: false,
backdrop: 'static'
});
break;
}
});
$scope.restartContainer = function(state) {
$scope.currentStep = state;
ContainerService.restartContainer(function() {
$scope.checkStatus()
});
};
$scope.showSuperuserPanel = function() {
$('#setupModal').modal('hide');
var prefix = $scope.hasSSL ? 'https' : 'http';
var hostname = $scope.hostname;
window.location = prefix + '://' + hostname + '/superuser';
};
$scope.configurationSaved = function(config) {
$scope.hasSSL = config['PREFERRED_URL_SCHEME'] == 'https';
$scope.hostname = config['SERVER_HOSTNAME'];
$scope.currentStep = $scope.States.VALID_CONFIG;
};
$scope.getProgress = function(step) {
var isStep = $scope.isStep;
var isStepFamily = $scope.isStepFamily;
var States = $scope.States;
return [
isStepFamily(step, States.CONFIG_DB),
isStepFamily(step, States.DB_SETUP),
isStep(step, States.DB_RESTARTING),
isStepFamily(step, States.CREATE_SUPERUSER),
isStep(step, States.CONFIG),
isStep(step, States.VALID_CONFIG),
isStep(step, States.CONFIG_RESTARTING),
isStep(step, States.READY)
];
};
$scope.isStepFamily = function(step, family) {
if (!step) { return false; }
return step.indexOf(family) == 0;
};
$scope.isStep = function(step) {
for (var i = 1; i < arguments.length; ++i) {
if (arguments[i] == step) {
return true;
}
}
return false;
};
$scope.showInvalidConfigDialog = function() {
var message = "The <code>config.yaml</code> file found in <code>conf/stack</code> could not be parsed."
var title = "Invalid configuration file";
CoreDialog.fatal(title, message);
};
$scope.showMissingConfigDialog = function() {
var message = "A volume should be mounted into the container at <code>/conf/stack</code>: " +
"<br><br><pre>docker run -v /path/to/config:/conf/stack</pre>" +
"<br>Once fixed, restart the container. For more information, " +
"<a href='https://coreos.com/docs/enterprise-registry/initial-setup/'>" +
"Read the Setup Guide</a>"
var title = "Missing configuration volume";
CoreDialog.fatal(title, message);
};
$scope.parseDbUri = function(value) {
if (!value) { return null; }
// Format: mysql+pymysql://<username>:<url escaped password>@<hostname>/<database_name>
var uri = URI(value);
return {
'kind': uri.protocol(),
'username': uri.username(),
'password': uri.password(),
'server': uri.host(),
'database': uri.path() ? uri.path().substr(1) : ''
};
};
$scope.serializeDbUri = function(fields) {
if (!fields['server']) { return ''; }
try {
if (!fields['server']) { return ''; }
if (!fields['database']) { return ''; }
var uri = URI();
uri = uri && uri.host(fields['server']);
uri = uri && uri.protocol(fields['kind']);
uri = uri && uri.username(fields['username']);
uri = uri && uri.password(fields['password']);
uri = uri && uri.path('/' + (fields['database'] || ''));
uri = uri && uri.toString();
} catch (ex) {
return '';
}
return uri;
};
$scope.createSuperUser = function() {
$scope.currentStep = $scope.States.CREATING_SUPERUSER;
ApiService.scCreateInitialSuperuser($scope.superUser, null).then(function(resp) {
UserService.load();
$scope.checkStatus();
}, function(resp) {
$scope.currentStep = $scope.States.SUPERUSER_ERROR;
$scope.errors.SuperuserCreationError = ApiService.getErrorMessage(resp, 'Could not create superuser');
});
};
$scope.performDatabaseSetup = function() {
$scope.currentStep = $scope.States.DB_SETUP;
ApiService.scSetupDatabase(null, null).then(function(resp) {
if (resp['error']) {
$scope.currentStep = $scope.States.DB_SETUP_ERROR;
$scope.errors.DatabaseSetupError = resp['error'];
} else {
$scope.currentStep = $scope.States.DB_SETUP_SUCCESS;
}
}, ApiService.errorDisplay('Could not setup database. Please report this to support.'))
};
$scope.validateDatabase = function() {
$scope.currentStep = $scope.States.VALIDATING_DB;
$scope.databaseInvalid = null;
var data = {
'config': {
'DB_URI': $scope.databaseUri
},
'hostname': window.location.host
};
var params = {
'service': 'database'
};
ApiService.scValidateConfig(data, params).then(function(resp) {
var status = resp.status;
if (status) {
$scope.currentStep = $scope.States.SAVING_DB;
ApiService.scUpdateConfig(data, null).then(function(resp) {
$scope.checkStatus();
}, ApiService.errorDisplay('Cannot update config. Please report this to support'));
} else {
$scope.currentStep = $scope.States.DB_ERROR;
$scope.errors.DatabaseValidationError = resp.reason;
}
}, ApiService.errorDisplay('Cannot validate database. Please report this to support'));
};
$scope.checkStatus = function() {
ContainerService.checkStatus(function(resp) {
$scope.currentStep = resp['status'];
}, $scope.hasSSL);
};
// Load the initial status.
$scope.checkStatus();
}

View file

@ -0,0 +1,229 @@
function SuperUserAdminCtrl($scope, $timeout, ApiService, Features, UserService, ContainerService, AngularPollChannel, CoreDialog) {
if (!Features.SUPER_USERS) {
return;
}
// Monitor any user changes and place the current user into the scope.
UserService.updateUserIn($scope);
$scope.configStatus = null;
$scope.requiresRestart = null;
$scope.logsCounter = 0;
$scope.newUser = {};
$scope.createdUser = null;
$scope.systemUsage = null;
$scope.debugServices = null;
$scope.debugLogs = null;
$scope.pollChannel = null;
$scope.logsScrolled = false;
$scope.csrf_token = encodeURIComponent(window.__token);
$scope.dashboardActive = false;
$scope.setDashboardActive = function(active) {
$scope.dashboardActive = active;
};
$scope.configurationSaved = function() {
$scope.requiresRestart = true;
};
$scope.showCreateUser = function() {
$scope.createdUser = null;
$('#createUserModal').modal('show');
};
$scope.viewSystemLogs = function(service) {
if ($scope.pollChannel) {
$scope.pollChannel.stop();
}
$scope.debugService = service;
$scope.debugLogs = null;
$scope.pollChannel = AngularPollChannel.create($scope, $scope.loadServiceLogs, 2 * 1000 /* 2s */);
$scope.pollChannel.start();
};
$scope.loadServiceLogs = function(callback) {
if (!$scope.debugService) { return; }
var params = {
'service': $scope.debugService
};
var errorHandler = ApiService.errorDisplay('Cannot load system logs. Please contact support.',
function() {
callback(false);
})
ApiService.getSystemLogs(null, params, /* background */true).then(function(resp) {
$scope.debugLogs = resp['logs'];
callback(true);
}, errorHandler);
};
$scope.loadDebugServices = function() {
if ($scope.pollChannel) {
$scope.pollChannel.stop();
}
$scope.debugService = null;
ApiService.listSystemLogServices().then(function(resp) {
$scope.debugServices = resp['services'];
}, ApiService.errorDisplay('Cannot load system logs. Please contact support.'))
};
$scope.getUsage = function() {
if ($scope.systemUsage) { return; }
ApiService.getSystemUsage().then(function(resp) {
$scope.systemUsage = resp;
}, ApiService.errorDisplay('Cannot load system usage. Please contact support.'))
}
$scope.loadUsageLogs = function() {
$scope.logsCounter++;
};
$scope.loadUsers = function() {
if ($scope.users) {
return;
}
$scope.loadUsersInternal();
};
$scope.loadUsersInternal = function() {
ApiService.listAllUsers().then(function(resp) {
$scope.users = resp['users'];
$scope.showInterface = true;
}, function(resp) {
$scope.users = [];
$scope.usersError = resp['data']['message'] || resp['data']['error_description'];
});
};
$scope.showChangePassword = function(user) {
$scope.userToChange = user;
$('#changePasswordModal').modal({});
};
$scope.createUser = function() {
$scope.creatingUser = true;
$scope.createdUser = null;
var errorHandler = ApiService.errorDisplay('Cannot create user', function() {
$scope.creatingUser = false;
$('#createUserModal').modal('hide');
});
ApiService.createInstallUser($scope.newUser, null).then(function(resp) {
$scope.creatingUser = false;
$scope.newUser = {};
$scope.createdUser = resp;
$scope.loadUsersInternal();
}, errorHandler)
};
$scope.showDeleteUser = function(user) {
if (user.username == UserService.currentUser().username) {
bootbox.dialog({
"message": 'Cannot delete yourself!',
"title": "Cannot delete user",
"buttons": {
"close": {
"label": "Close",
"className": "btn-primary"
}
}
});
return;
}
$scope.userToDelete = user;
$('#confirmDeleteUserModal').modal({});
};
$scope.changeUserPassword = function(user) {
$('#changePasswordModal').modal('hide');
var params = {
'username': user.username
};
var data = {
'password': user.password
};
ApiService.changeInstallUser(data, params).then(function(resp) {
$scope.loadUsersInternal();
}, ApiService.errorDisplay('Could not change user'));
};
$scope.deleteUser = function(user) {
$('#confirmDeleteUserModal').modal('hide');
var params = {
'username': user.username
};
ApiService.deleteInstallUser(null, params).then(function(resp) {
$scope.loadUsersInternal();
}, ApiService.errorDisplay('Cannot delete user'));
};
$scope.sendRecoveryEmail = function(user) {
var params = {
'username': user.username
};
ApiService.sendInstallUserRecoveryEmail(null, params).then(function(resp) {
bootbox.dialog({
"message": "A recovery email has been sent to " + resp['email'],
"title": "Recovery email sent",
"buttons": {
"close": {
"label": "Close",
"className": "btn-primary"
}
}
});
}, ApiService.errorDisplay('Cannot send recovery email'))
};
$scope.restartContainer = function() {
$('#restartingContainerModal').modal({
keyboard: false,
backdrop: 'static'
});
ContainerService.restartContainer(function() {
$scope.checkStatus()
});
};
$scope.checkStatus = function() {
ContainerService.checkStatus(function(resp) {
$('#restartingContainerModal').modal('hide');
$scope.configStatus = resp['status'];
$scope.requiresRestart = resp['requires_restart'];
if ($scope.configStatus == 'ready') {
$scope.loadUsers();
} else {
var message = "Installation of this product has not yet been completed." +
"<br><br>Please read the " +
"<a href='https://coreos.com/docs/enterprise-registry/initial-setup/'>" +
"Setup Guide</a>"
var title = "Installation Incomplete";
CoreDialog.fatal(title, message);
}
});
};
// Load the initial status.
$scope.checkStatus();
}