Commit 82ec00ff by Jeffrey Phillips Committed by GitHub

Initial addition of common services/providers/constants (#1)

* Initial addition of common services/providers/constants

* Add travis build-ability, update .gitignore, fix incorrect case of Openshift

* remove unnecessary use of openshift-jvm in install-deps script

* Update dependencies, use range for angular components
parent 516b176c
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
# Change these settings to your own preference
indent_style = space
indent_size = 2
# We recommend you to keep these unchanged
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
node_modules
.tmp
.bundle
.idea
bower_components
phantomjsdriver.log
.DS_Store
test/test-results.xml
{
"node": true,
"browser": true,
"esnext": true,
"bitwise": true,
"camelcase": false,
"curly": true,
"eqeqeq": true,
"expr" : true,
"immed": true,
"indent": 2,
"latedef": "nofunc",
"newcap": false,
"noarg": true,
"quotmark": false,
"regexp": true,
"smarttabs": true,
"strict": true,
"sub" : true,
"undef": true,
"unused": true,
"globals": {
"jsyaml": false,
"angular": false,
"ansi_up": false,
"c3": false,
"d3": false,
"hawtioPluginLoader": false,
"HawtioCore": false,
"Logger" : false,
"LabelSelector": false,
"ResourceGroupVersion": false,
"moment": false,
"Messenger" : false,
"URI": false,
"Clipboard": false,
"$": false,
"_" : false,
"URITemplate": false,
"saveAs": false
}
}
language: node_js
node_js:
- "4"
cache:
directories:
- node_modules
- bower_components
before_script:
- make build
script:
- hack/test-headless.sh test
- hack/verify-dist.sh
module.exports = function (grunt) {
'use strict';
require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
function init () {
grunt.initConfig({
availabletasks: {
tasks: {
options: {
descriptions: {
'help': 'Task list helper for your Grunt enabled projects.',
'clean': 'Deletes the content of the dist directory.',
'build': 'Builds the project (including documentation) into the dist directory. You can specify modules to be built as arguments (' +
'grunt build:buttons:notification) otherwise all available modules are built.',
'test': 'Executes the karma testsuite.'
},
groups: {
'Basic project tasks': ['help', 'clean', 'build', 'test']
}
}
}
},
clean: {
all: ['dist/*']
},
concat: {
options: {
separator: ';'
},
dist: {
src: ['src/**/*.module.js', 'src/**/*.js'],
dest: 'dist/origin-web-common.js'
}
},
karma: {
unit: {
configFile: 'test/karma.conf.js',
singleRun: true,
browsers: ['PhantomJS']
}
},
// ng-annotate tries to make the code safe for minification automatically
// by using the Angular long form for dependency injection.
ngAnnotate: {
dist: {
files: [{
src: 'dist/origin-web-common.js',
dest: 'dist/origin-web-common.js'
}]
}
},
uglify: {
options: {
mangle: false
},
build: {
files: {},
src: 'dist/origin-web-common.js',
dest: 'dist/origin-web-common.min.js'
}
}
});
// You can specify which modules to build as arguments of the build task.
grunt.registerTask('build', 'Create bootstrap build files', function () {
var concatSrc = [];
if (this.args.length) {
this.args.forEach(function (file) {
if (grunt.file.exists('./src/' + file)) {
grunt.log.ok('Adding ' + file + ' to the build queue.');
concatSrc.push('src/' + file + '/*.js');
} else {
grunt.fail.warn('Unable to build module \'' + file + '\'. The module doesn\'t exist.');
}
});
} else {
concatSrc = 'src/**/*.js';
}
grunt.task.run(['clean', 'concat', 'ngAnnotate', 'uglify:build', 'test']);
});
// Runs all the tasks of build with the exception of tests
grunt.registerTask('deploy', 'Prepares the project for deployment. Does not run unit tests', function () {
var concatSrc = 'src/**/*.js';
grunt.task.run(['clean', 'concat', 'ngAnnotate', 'uglify:build']);
});
grunt.registerTask('default', ['build']);
grunt.registerTask('test', ['karma']);
grunt.registerTask('check', ['test']);
grunt.registerTask('help', ['availabletasks']);
}
init({});
};
This diff is collapsed. Click to expand it.
.DEFAULT_GOAL := build
# Pull in dependencies
#
# Examples:
# make install
install:
hack/install-deps.sh
.PHONY: install
# Clean up all dependencies
#
# Args:
# GRUNT_FLAGS: Extra flags to pass to 'grunt test'
#
# Example:
# make clean
# make test GRUNT_FLAGS='--stack'
clean:
grunt clean $(GRUNT_FLAGS)
hack/clean-deps.sh
.PHONY: clean
# Run `grunt build`
#
# Args:
# GRUNT_FLAGS: Extra flags to pass to 'grunt build'
#
# Examples:
# make build
# make build GRUNT_FLAGS='--verbose'
build: install
grunt build $(GRUNT_FLAGS)
.PHONY: build
# Run all tests
#
# Args:
# GRUNT_FLAGS: Extra flags to pass to 'grunt test'
#
# Examples:
# make test
# make test GRUNT_FLAGS='--gruntfile=~/special/Gruntfile.js'
test: build
hack/verify-dist.sh
hack/test-headless.sh test $(GRUNT_FLAGS)
hack/test-headless.sh test-integration $(GRUNT_FLAGS)
.PHONY: test
# origin-web-common
\ No newline at end of file
OpenShift Common Web Services
==============================
The common services used for the [OpenShift Management Console](https://github.com/openshift/origin-web-console).
[![Build Status](https://travis-ci.org/openshift/origin-web-common.svg?branch=master)](https://travis-ci.org/openshift/origin-web-common)
Contributing
------------
#### Getting started
1. Install [Nodejs](http://nodejs.org/) and [npm](https://www.npmjs.org/)
2. Install [grunt-cli](http://gruntjs.com/installing-grunt) and [bower](http://bower.io/) by running `npm install -g grunt-cli bower` (may need to be run with sudo)
3. Install dependencies by running `npm install` and `bower install`
4. Build and run tests by running `npm build`
#### Before opening a pull request
Please configure your editor to use the
following settings to avoid common code inconsistencies and dirty
diffs:
* Use soft-tabs set to two spaces.
* Trim trailing white space on save.
* Set encoding to UTF-8.
* Add new line at end of files.
Or [configure your editor](http://editorconfig.org/#download) to
utilize [`.editorconfig`](https://github.com/openshift/origin-web-common/blob/master/.editorconfig),
which will apply these settings automatically.
Then:
1. If needed, run `grunt build` to update the files under the dist directory
2. Run the spec tests with `grunt test`
4. Rebase and squash changes to a single commit
{
"name": "origin-web-comon",
"version": "0.0.1",
"main": [
"dist/origin-web-common.js"
],
"authors": [
"Red Hat"
],
"description": "Library of common services and components for OpenShift Web Console.",
"license": "Apache-2.0",
"ignore": [
"Gruntfile.js",
"package.json",
".bowerrc",
".editorconfig",
".gitignore",
".jshintrc",
"src",
"README.md",
"bower.json",
".bower.json",
"grunt-ngdocs-index.tmpl"
],
"repository": {
"type": "git",
"url": "git://github.com/openshift/origin-web-common.git"
},
"dependencies": {
"angular": "1.5.11 < 2.0.0",
"angular-sanitize": "1.5.11 < 2.0.0",
"angular-utf8-base64": "0.0.5",
"hawtio-core": "2.0.11",
"hawtio-extension-service": "2.0.2",
"jquery": "2.1.4",
"kubernetes-container-terminal": "1.0.2",
"lodash": "3.10.1",
"messenger": "1.4.1",
"microplugin": "0.0.3",
"openshift-object-describer": "1.1.2",
"sifter": "0.4.1",
"uri.js": "1.18.0"
},
"devDependencies": {
"angular-mocks": "1.5.11 < 2.0.0",
"angular-scenario": "1.5.11 < 2.0.0"
},
"resolutions": {
"angular-sanitize": "~1.5.11",
"angular": "~1.5.11"
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
#!/bin/bash
set -e
# We don't need bower to be installed globally for the system, so
# we can amend our path to look into the local node_modules for the
# correct binaries.
repo_root="$( dirname "${BASH_SOURCE}" )/.."
export PATH="${PATH}:${repo_root}/node_modules/bower/bin"
if which bower > /dev/null 2>&1 ; then
# In case upstream components change things without incrementing versions
echo "Clearing bower cache..."
bower cache clean --allow-root
else
echo "Skipping bower cache clean, bower not installed."
fi
echo "Cleaning up bower_components and node_modules..."
rm -rf bower_components/* node_modules/*
#!/bin/bash
set -e
STARTTIME=$(date +%s)
TMPDIR="${TMPDIR:-"/tmp"}"
LOG_DIR="${LOG_DIR:-$(mktemp -d ${TMPDIR}/openshift.assets.logs.XXXX)}"
function cmd() {
local cmd="$1"
local tries="${2:-1}"
local log_file=$(mktemp ${LOG_DIR}/install-assets.XXXX)
echo "[install-assets] ${cmd}"
rc="0"
while [[ "$tries" -gt 0 ]]; do
rc="0"
$cmd &> ${log_file} || rc=$?
[[ "$rc" == "0" ]] && return 0
((tries--))
done
echo "[ERROR] Command '${cmd}' failed with rc ${rc}, logs:" && cat ${log_file}
exit $rc
}
# We don't need grunt and bower to be installed globally for the system,
# so we can amend our path to look into the local node_modules for the
# correct binaries.
repo_root="$( dirname "${BASH_SOURCE}" )/.."
export PATH="${PATH}:${repo_root}/node_modules/bower/bin:${repo_root}/node_modules/grunt-cli/bin"
# Install bower if needed
if ! which bower > /dev/null 2>&1 ; then
cmd "npm install bower"
fi
# Install grunt if needed
if ! which grunt > /dev/null 2>&1 ; then
cmd "npm install grunt-cli"
fi
cmd "npm install --unsafe-perm"
# In case upstream components change things without incrementing versions
cmd "bower cache clean --allow-root"
cmd "bower update --allow-root" 3
ret=$?; ENDTIME=$(date +%s); echo "$0 took $(($ENDTIME - $STARTTIME)) seconds"; exit "$ret"
#!/bin/bash
set -o errexit
set -o nounset
set -o pipefail
function cleanup_xvfb() {
if [[ -n "${xvfb_process:-}" ]]; then
kill -SIGTERM "${xvfb_process}"
fi
}
trap cleanup_xvfb EXIT
if ! which xdpyinfo >/dev/null 2>&1; then
echo "[ERROR] The \`xdpyinfo\` utility is required to run this script!."
exit 1
fi
if ! which Xvfb >/dev/null 2>&1; then
echo "[ERROR] The \`Xvfb\` utility is required to run this script!."
exit 1
fi
# We don't need grunt to be installed globally for the system, so
# we can amend our path to look into the local node_modules for the
# correct binaries.
repo_root="$( dirname "${BASH_SOURCE}" )/.."
export PATH="${PATH}:${repo_root}/node_modules/grunt-cli/bin"
echo "[INFO] Starting virtual framebuffer for headless tests..."
export DISPLAY=':10'
export SCREEN='0'
Xvfb "${DISPLAY}" -screen "${SCREEN}" 1024x768x24 -ac &
xvfb_process="$!"
while ! xdpyinfo -d "${DISPLAY}" >/dev/null 2>&1; do
sleep 0.2
done
grunt "$@"
#!/bin/bash
set -e
# We don't need grunt to be installed globally for the system, so
# we can amend our path to look into the local node_modules for the
# correct binaries.
repo_root="$( dirname "${BASH_SOURCE}" )/.."
export PATH="${PATH}:${repo_root}/node_modules/grunt-cli/bin"
grunt build
echo "Verifying that checked in built files under dist match the source..."
if [[ $(git status -s -u dist*) ]]; then
git status -vv -u dist*
echo "Built dist does not match what is committed, run 'grunt build' and include the results in your commit."
exit 1
else
echo "Verified."
fi
{
"author": "Red Hat",
"name": "origin-web-common",
"version": "0.0.1",
"license": "Apache-2.0",
"description": "Library of common services and components for OpenShift Web Console.",
"homepage": "https://github.com/openshift/origin-web-common.git",
"dependencies": {
"angular": "1.5.11"
},
"devDependencies": {
"express": "3.4.4",
"grunt": "0.4.5",
"grunt-angular-templates": "0.5.7",
"grunt-available-tasks": "0.4.3",
"grunt-contrib-clean": "0.4.1",
"grunt-contrib-concat": "0.3.0",
"grunt-contrib-connect": "0.5.0",
"grunt-contrib-copy": "0.5.0",
"grunt-contrib-cssmin": "0.9.0",
"grunt-contrib-jshint": "0.7.0",
"grunt-contrib-uglify": "0.2.5",
"grunt-contrib-watch": "0.5.3",
"grunt-eslint": "~17.1.0",
"grunt-karma": "0.8.3",
"grunt-ng-annotate": "^1.0.1",
"grunt-remove": "^0.1.0",
"karma": "0.12.23",
"karma-chrome-launcher": "0.1.4",
"karma-coverage": "^1.1.1",
"karma-firefox-launcher": "0.1.3",
"karma-jasmine": "0.2.2",
"karma-junit-reporter": "0.2.2",
"karma-ng-html2js-preprocessor": "~0.1",
"karma-phantomjs-launcher": "^1.0.0",
"matchdep": "0.3.0",
"nsp": "^2.6.1"
},
"scripts": {
"test": "grunt test"
},
"repository": {
"type": "git",
"url": "https://github.com/openshift/origin-web-common.git"
},
"engines": {
"node": ">=0.10.10"
}
}
// This is the default configuration for the dev mode of the web console.
// A generated version of this config is created at run-time when running
// the web console from the openshift binary.
//
// To change configuration for local development, copy this file to
// assets/app/config.local.js and edit the copy.
if (!window.OPENSHIFT_CONFIG) {
window.OPENSHIFT_CONFIG = {
apis: {
hostPort: "localhost:8443",
prefix: "/apis"
},
api: {
openshift: {
hostPort: "localhost:8443",
prefix: "/oapi"
},
k8s: {
hostPort: "localhost:8443",
prefix: "/api"
}
},
auth: {
oauth_authorize_uri: "https://localhost:8443/oauth/authorize",
oauth_redirect_base: "https://localhost:9000/dev-console",
oauth_client_id: "openshift-web-console",
logout_uri: ""
},
loggingURL: "",
metricsURL: ""
};
window.OPENSHIFT_VERSION = {
openshift: "dev-mode",
kubernetes: "dev-mode"
};
}
/**
* @name openshiftCommon
*
* @description
* Base module for openshiftCommon.
*/
angular.module('openshiftCommon', ['kubernetesUI', 'ab-base64'])
.config(function(AuthServiceProvider) {
AuthServiceProvider.UserStore('MemoryUserStore');
})
.constant("API_CFG", _.get(window.OPENSHIFT_CONFIG, "api", {}))
.constant("APIS_CFG", _.get(window.OPENSHIFT_CONFIG, "apis", {}))
.constant("AUTH_CFG", _.get(window.OPENSHIFT_CONFIG, "auth", {}))
.constant("LOGGING_URL", _.get(window.OPENSHIFT_CONFIG, "loggingURL"))
.constant("METRICS_URL", _.get(window.OPENSHIFT_CONFIG, "metricsURL"))
.constant("LIMIT_REQUEST_OVERRIDES", _.get(window.OPENSHIFT_CONFIG, "limitRequestOverrides"))
.config(function($httpProvider, AuthServiceProvider, RedirectLoginServiceProvider, AUTH_CFG, API_CFG, kubernetesContainerSocketProvider) {
$httpProvider.interceptors.push('AuthInterceptor');
AuthServiceProvider.LoginService('RedirectLoginService');
AuthServiceProvider.LogoutService('DeleteTokenLogoutService');
// TODO: fall back to cookie store when localStorage is unavailable (see known issues at http://caniuse.com/#feat=namevalue-storage)
AuthServiceProvider.UserStore('LocalStorageUserStore');
RedirectLoginServiceProvider.OAuthClientID(AUTH_CFG.oauth_client_id);
RedirectLoginServiceProvider.OAuthAuthorizeURI(AUTH_CFG.oauth_authorize_uri);
RedirectLoginServiceProvider.OAuthRedirectURI(URI(AUTH_CFG.oauth_redirect_base).segment("oauth").toString());
// Configure the container terminal
kubernetesContainerSocketProvider.WebSocketFactory = "ContainerWebSocket";
});
hawtioPluginLoader.addModule('openshiftCommon');
// API Discovery, this runs before the angular app is bootstrapped
// TODO we want this to be possible with a single request against the API instead of being dependent on the numbers of groups and versions
hawtioPluginLoader.registerPreBootstrapTask(function(next) {
// Skips api discovery, needed to run spec tests
if ( _.get(window, "OPENSHIFT_CONFIG.api.k8s.resources") ) {
next();
return;
}
var api = {
k8s: {},
openshift: {}
};
var apis = {};
var API_DISCOVERY_ERRORS = [];
var protocol = window.location.protocol + "//";
// Fetch /api/v1 for legacy k8s resources, we will never bump the version of these legacy apis so fetch version immediately
var k8sBaseURL = protocol + window.OPENSHIFT_CONFIG.api.k8s.hostPort + window.OPENSHIFT_CONFIG.api.k8s.prefix;
var k8sDeferred = $.get(k8sBaseURL + "/v1")
.done(function(data) {
api.k8s.v1 = _.indexBy(data.resources, 'name');
})
.fail(function(data, textStatus, jqXHR) {
API_DISCOVERY_ERRORS.push({
data: data,
textStatus: textStatus,
xhr: jqXHR
});
});
// Fetch /oapi/v1 for legacy openshift resources, we will never bump the version of these legacy apis so fetch version immediately
var osBaseURL = protocol + window.OPENSHIFT_CONFIG.api.openshift.hostPort + window.OPENSHIFT_CONFIG.api.openshift.prefix;
var osDeferred = $.get(osBaseURL + "/v1")
.done(function(data) {
api.openshift.v1 = _.indexBy(data.resources, 'name');
})
.fail(function(data, textStatus, jqXHR) {
API_DISCOVERY_ERRORS.push({
data: data,
textStatus: textStatus,
xhr: jqXHR
});
});
// Fetch /apis to get the list of groups and versions, then fetch each group/
// Because the api discovery doc returns arrays and we want maps, this creates a structure like:
// {
// extensions: {
// name: "extensions",
// preferredVersion: "v1beta1",
// versions: {
// v1beta1: {
// version: "v1beta1",
// groupVersion: "extensions/v1beta1"
// resources: {
// daemonsets: {
// /* resource returned from discovery API */
// }
// }
// }
// }
// }
// }
var apisBaseURL = protocol + window.OPENSHIFT_CONFIG.apis.hostPort + window.OPENSHIFT_CONFIG.apis.prefix;
var apisDeferred = $.get(apisBaseURL)
.then(function(data) {
var apisDeferredVersions = [];
_.each(data.groups, function(apiGroup) {
var group = {
name: apiGroup.name,
preferredVersion: apiGroup.preferredVersion.version,
versions: {}
};
apis[group.name] = group;
_.each(apiGroup.versions, function(apiVersion) {
var versionStr = apiVersion.version;
group.versions[versionStr] = {
version: versionStr,
groupVersion: apiVersion.groupVersion
};
apisDeferredVersions.push($.get(apisBaseURL + "/" + apiVersion.groupVersion)
.done(function(data) {
group.versions[versionStr].resources = _.indexBy(data.resources, 'name');
})
.fail(function(data, textStatus, jqXHR) {
API_DISCOVERY_ERRORS.push({
data: data,
textStatus: textStatus,
xhr: jqXHR
});
}));
});
});
return $.when.apply(this, apisDeferredVersions);
}, function(data, textStatus, jqXHR) {
API_DISCOVERY_ERRORS.push({
data: data,
textStatus: textStatus,
xhr: jqXHR
});
});
// Will be called on success or failure
var discoveryFinished = function() {
window.OPENSHIFT_CONFIG.api.k8s.resources = api.k8s;
window.OPENSHIFT_CONFIG.api.openshift.resources = api.openshift;
window.OPENSHIFT_CONFIG.apis.groups = apis;
if (API_DISCOVERY_ERRORS.length) {
window.OPENSHIFT_CONFIG.apis.API_DISCOVERY_ERRORS = API_DISCOVERY_ERRORS;
}
next();
};
$.when(k8sDeferred,osDeferred,apisDeferred).always(discoveryFinished);
});
'use strict';
angular.module("openshiftCommon")
.factory("AuthorizationService", function($q, $cacheFactory, Logger, $interval, APIService, DataService){
var currentProject = null;
var cachedRulesByProject = $cacheFactory('rulesCache', {
number: 10
});
// Permisive mode will cause no checks to be done for the user actions.
var permissiveMode = false;
var REVIEW_RESOURCES = ["localresourceaccessreviews", "localsubjectaccessreviews", "resourceaccessreviews", "selfsubjectrulesreviews", "subjectaccessreviews"];
// Transform data from:
// rules = {resources: ["jobs"], apiGroups: ["extensions"], verbs:["create","delete","get","list","update"]}
// into:
// normalizedRules = {"extensions": {"jobs": ["create","delete","get","list","update"]}}
var normalizeRules = function(rules) {
var normalizedRules = {};
_.each(rules, function(rule) {
_.each(rule.apiGroups, function(apiGroup) {
if (!normalizedRules[apiGroup]) {
normalizedRules[apiGroup] = {};
}
_.each(rule.resources, function(resource) {
normalizedRules[apiGroup][resource] = rule.verbs;
});
});
});
return normalizedRules;
};
// Check if resource name meets one of following conditions, since those resources can't be create/update via `Add to project` page:
// - 'projectrequests'
// - subresource that contains '/', eg: 'builds/source', 'builds/logs', ...
// - resource is in REVIEW_RESOURCES list
var checkResource = function(resource) {
if (resource === "projectrequests" || _.contains(resource, "/") || _.contains(REVIEW_RESOURCES, resource)) {
return false;
} else {
return true;
}
};
// Check if user can create/update any resource on the 'Add to project' so the button will be displayed.
var canAddToProjectCheck = function(rules) {
return _.some(rules, function(rule) {
return _.some(rule.resources, function(resource) {
return checkResource(resource) && !_.isEmpty(_.intersection(rule.verbs ,(["*", "create", "update"])));
});
});
};
// forceRefresh is a boolean to bust the cache & request new perms
var getProjectRules = function(projectName, forceRefresh) {
var deferred = $q.defer();
currentProject = projectName;
var projectRules = cachedRulesByProject.get(projectName);
var rulesResource = "selfsubjectrulesreviews";
if (!projectRules || projectRules.forceRefresh || forceRefresh) {
// Check if APIserver contains 'selfsubjectrulesreviews' resource. If not switch to permissive mode.
if (APIService.apiInfo(rulesResource)) {
Logger.log("AuthorizationService, loading user rules for " + projectName + " project");
var object = {kind: "SelfSubjectRulesReview",
apiVersion: "v1"
};
DataService.create(rulesResource, null, object, {namespace: projectName}).then(
function(data) {
var normalizedData = normalizeRules(data.status.rules);
var canUserAddToProject = canAddToProjectCheck(data.status.rules);
cachedRulesByProject.put(projectName, {rules: normalizedData,
canAddToProject: canUserAddToProject,
forceRefresh: false,
cacheTimestamp: _.now()
});
deferred.resolve();
}, function() {
permissiveMode = true;
deferred.resolve();
});
} else {
Logger.log("AuthorizationService, resource 'selfsubjectrulesreviews' is not part of APIserver. Switching into permissive mode.");
permissiveMode = true;
deferred.resolve();
}
} else {
// Using cached data.
Logger.log("AuthorizationService, using cached rules for " + projectName + " project");
if ((_.now() - projectRules.cacheTimestamp) >= 600000) {
projectRules.forceRefresh = true;
}
deferred.resolve();
}
return deferred.promise;
};
var getRulesForProject = function(projectName) {
return _.get(cachedRulesByProject.get(projectName || currentProject), ['rules']);
};
// _canI checks whether any rule allows the specified verb (directly or via a wildcard verb) on the literal group and resource.
var _canI = function(rules, verb, group, resource) {
var resources = rules[group];
if (!resources) {
return false;
}
var verbs = resources[resource];
if (!verbs) {
return false;
}
return _.contains(verbs, verb) || _.contains(verbs, '*');
};
// canI checks whether any rule allows the specified verb on the specified group-resource (directly or via a wildcard rule).
var canI = function(resource, verb, projectName) {
if (permissiveMode) {
return true;
}
// normalize to structured form
var r = APIService.toResourceGroupVersion(resource);
var rules = getRulesForProject(projectName || currentProject);
if (!rules) {
return false;
}
return _canI(rules, verb, r.group, r.resource) ||
_canI(rules, verb, '*', '*' ) ||
_canI(rules, verb, r.group, '*' ) ||
_canI(rules, verb, '*', r.resource);
};
var canIAddToProject = function(projectName) {
if (permissiveMode) {
return true;
} else {
return !!_.get(cachedRulesByProject.get(projectName || currentProject), ['canAddToProject']);
}
};
return {
checkResource: checkResource,
getProjectRules: getProjectRules,
canI: canI,
canIAddToProject: canIAddToProject,
getRulesForProject: getRulesForProject
};
});
'use strict';
angular.module('openshiftCommon')
.factory('base64util', function() {
return {
pad: function(data){
if (!data) { return ""; }
switch (data.length % 4) {
case 1: return data + "===";
case 2: return data + "==";
case 3: return data + "=";
default: return data;
}
}
};
});
'use strict';
angular.module('openshiftCommon')
.factory('Constants', function() {
var constants = _.clone(window.OPENSHIFT_CONSTANTS || {});
var version = _.clone(window.OPENSHIFT_VERSION || {});
constants.VERSION = version;
return constants;
});
'use strict';
// Logout strategies
angular.module('openshiftCommon')
.provider('DeleteTokenLogoutService', function() {
this.$get = function($q, $injector, Logger) {
var authLogger = Logger.get("auth");
return {
logout: function(user, token) {
authLogger.log("DeleteTokenLogoutService.logout()", user, token);
// If we don't have a token, we're done
if (!token) {
authLogger.log("DeleteTokenLogoutService, no token, returning immediately");
return $q.when({});
}
// Lazily get the data service. Can't explicitly depend on it or we get circular dependencies.
var DataService = $injector.get('DataService');
// Use the token to delete the token
// Never trigger a login when deleting our token
var opts = {http: {auth: {token: token, triggerLogin: false}}};
// TODO: Change this to return a promise that "succeeds" even if the token delete fails?
return DataService.delete("oauthaccesstokens", token, {}, opts);
},
};
};
});
'use strict';
angular.module('openshiftCommon')
.provider('Logger', function() {
this.$get = function() {
// Wraps the global Logger from https://github.com/jonnyreeves/js-logger
var OSLogger = Logger.get("OpenShift");
var logger = {
get: function(name) {
var logger = Logger.get("OpenShift/" + name);
var logLevel = "OFF";
if (localStorage) {
logLevel = localStorage['OpenShiftLogLevel.' + name] || logLevel;
}
logger.setLevel(Logger[logLevel]);
return logger;
},
log: function() {
OSLogger.log.apply(OSLogger, arguments);
},
info: function() {
OSLogger.info.apply(OSLogger, arguments);
},
debug: function() {
OSLogger.debug.apply(OSLogger, arguments);
},
warn: function() {
OSLogger.warn.apply(OSLogger, arguments);
},
error: function() {
OSLogger.error.apply(OSLogger, arguments);
}
};
// Set default log level
var logLevel = "ERROR";
if (localStorage) {
logLevel = localStorage['OpenShiftLogLevel.main'] || logLevel;
}
OSLogger.setLevel(Logger[logLevel]);
return logger;
};
});
'use strict';
/* jshint unused: false */
// UserStore objects able to remember user and tokens for the current user
angular.module('openshiftCommon')
.provider('MemoryUserStore', function() {
this.$get = function(Logger){
var authLogger = Logger.get("auth");
var _user = null;
var _token = null;
return {
available: function() {
return true;
},
getUser: function(){
authLogger.log("MemoryUserStore.getUser", _user);
return _user;
},
setUser: function(user, ttl) {
// TODO: honor ttl
authLogger.log("MemoryUserStore.setUser", user);
_user = user;
},
getToken: function() {
authLogger.log("MemoryUserStore.getToken", _token);
return _token;
},
setToken: function(token, ttl) {
// TODO: honor ttl
authLogger.log("MemoryUserStore.setToken", token);
_token = token;
}
};
};
})
.provider('SessionStorageUserStore', function() {
this.$get = function(Logger){
var authLogger = Logger.get("auth");
var userkey = "SessionStorageUserStore.user";
var tokenkey = "SessionStorageUserStore.token";
return {
available: function() {
try {
var x = String(new Date().getTime());
sessionStorage['SessionStorageUserStore.test'] = x;
var y = sessionStorage['SessionStorageUserStore.test'];
sessionStorage.removeItem('SessionStorageUserStore.test');
return x === y;
} catch(e) {
return false;
}
},
getUser: function(){
try {
var user = JSON.parse(sessionStorage[userkey]);
authLogger.log("SessionStorageUserStore.getUser", user);
return user;
} catch(e) {
authLogger.error("SessionStorageUserStore.getUser", e);
return null;
}
},
setUser: function(user, ttl) {
// TODO: honor ttl
if (user) {
authLogger.log("SessionStorageUserStore.setUser", user);
sessionStorage[userkey] = JSON.stringify(user);
} else {
authLogger.log("SessionStorageUserStore.setUser", user, "deleting");
sessionStorage.removeItem(userkey);
}
},
getToken: function() {
try {
var token = sessionStorage[tokenkey];
authLogger.log("SessionStorageUserStore.getToken", token);
return token;
} catch(e) {
authLogger.error("SessionStorageUserStore.getToken", e);
return null;
}
},
setToken: function(token, ttl) {
// TODO: honor ttl
if (token) {
authLogger.log("SessionStorageUserStore.setToken", token);
sessionStorage[tokenkey] = token;
} else {
authLogger.log("SessionStorageUserStore.setToken", token, "deleting");
sessionStorage.removeItem(tokenkey);
}
}
};
};
})
.provider('LocalStorageUserStore', function() {
this.$get = function(Logger){
var authLogger = Logger.get("auth");
var userkey = "LocalStorageUserStore.user";
var tokenkey = "LocalStorageUserStore.token";
var ttlKey = function(key) {
return key + ".ttl";
};
var setTTL = function(key, ttl) {
if (ttl) {
var expires = new Date().getTime() + ttl*1000;
localStorage[ttlKey(key)] = expires;
authLogger.log("LocalStorageUserStore.setTTL", key, ttl, new Date(expires).toString());
} else {
localStorage.removeItem(ttlKey(key));
authLogger.log("LocalStorageUserStore.setTTL deleting", key);
}
};
var isTTLExpired = function(key) {
var ttl = localStorage[ttlKey(key)];
if (!ttl) {
return false;
}
var expired = parseInt(ttl) < new Date().getTime();
authLogger.log("LocalStorageUserStore.isTTLExpired", key, expired);
return expired;
};
return {
available: function() {
try {
var x = String(new Date().getTime());
localStorage['LocalStorageUserStore.test'] = x;
var y = localStorage['LocalStorageUserStore.test'];
localStorage.removeItem('LocalStorageUserStore.test');
return x === y;
} catch(e) {
return false;
}
},
getUser: function(){
try {
if (isTTLExpired(userkey)) {
authLogger.log("LocalStorageUserStore.getUser expired");
localStorage.removeItem(userkey);
setTTL(userkey, null);
return null;
}
var user = JSON.parse(localStorage[userkey]);
authLogger.log("LocalStorageUserStore.getUser", user);
return user;
} catch(e) {
authLogger.error("LocalStorageUserStore.getUser", e);
return null;
}
},
setUser: function(user, ttl) {
if (user) {
authLogger.log("LocalStorageUserStore.setUser", user, ttl);
localStorage[userkey] = JSON.stringify(user);
setTTL(userkey, ttl);
} else {
authLogger.log("LocalStorageUserStore.setUser", user, "deleting");
localStorage.removeItem(userkey);
setTTL(userkey, null);
}
},
getToken: function() {
try {
if (isTTLExpired(tokenkey)) {
authLogger.log("LocalStorageUserStore.getToken expired");
localStorage.removeItem(tokenkey);
setTTL(tokenkey, null);
return null;
}
var token = localStorage[tokenkey];
authLogger.log("LocalStorageUserStore.getToken", token);
return token;
} catch(e) {
authLogger.error("LocalStorageUserStore.getToken", e);
return null;
}
},
setToken: function(token, ttl) {
if (token) {
authLogger.log("LocalStorageUserStore.setToken", token, ttl);
localStorage[tokenkey] = token;
setTTL(tokenkey, ttl);
} else {
authLogger.log("LocalStorageUserStore.setToken", token, ttl, "deleting");
localStorage.removeItem(tokenkey);
setTTL(tokenkey, null);
}
}
};
};
});
'use strict';
/* jshint unused: false */
angular.module('openshiftCommon')
.factory('Notification', function($rootScope) {
function Notification() {
this.messenger = Messenger({
extraClasses: 'messenger-fixed messenger-on-bottom messenger-on-right',
theme: 'flat',
messageDefaults: {
showCloseButton: true,
hideAfter: 10
}
});
var self = this;
$rootScope.$on( "$routeChangeStart", function(event, next, current) {
self.clear();
});
}
// Opts:
// id - if an id is passed only one message with this id will ever be shown
// mustDismiss - the user must explicitly dismiss the message, it will not auto-hide
Notification.prototype.notify = function(type, message, opts) {
opts = opts || {};
var notifyOpts = {
type: type,
// TODO report this issue upstream to messenger, they don't handle messages with invalid html
// they should be escaping it
message: $('<div/>').text(message).html(),
id: opts.id,
actions: opts.actions
};
if (opts.mustDismiss) {
notifyOpts.hideAfter = false;
}
this.messenger.post(notifyOpts);
};
Notification.prototype.success = function(message, opts) {
this.notify("success", message, opts);
};
Notification.prototype.info = function(message, opts) {
this.notify("info", message, opts);
};
Notification.prototype.error = function(message, opts) {
this.notify("error", message, opts);
};
Notification.prototype.warning = function(message, opts) {
this.notify("warning", message, opts);
};
Notification.prototype.clear = function() {
this.messenger.hideAll();
};
return new Notification();
});
'use strict';
// Login strategies
angular.module('openshiftCommon')
.provider('RedirectLoginService', function() {
var _oauth_client_id = "";
var _oauth_authorize_uri = "";
var _oauth_redirect_uri = "";
this.OAuthClientID = function(id) {
if (id) {
_oauth_client_id = id;
}
return _oauth_client_id;
};
this.OAuthAuthorizeURI = function(uri) {
if (uri) {
_oauth_authorize_uri = uri;
}
return _oauth_authorize_uri;
};
this.OAuthRedirectURI = function(uri) {
if (uri) {
_oauth_redirect_uri = uri;
}
return _oauth_redirect_uri;
};
this.$get = function($location, $q, Logger, base64) {
var authLogger = Logger.get("auth");
var getRandomInts = function(length) {
var randomValues;
if (window.crypto && window.Uint32Array) {
try {
var r = new Uint32Array(length);
window.crypto.getRandomValues(r);
randomValues = [];
for (var j=0; j < length; j++) {
randomValues.push(r[j]);
}
} catch(e) {
authLogger.debug("RedirectLoginService.getRandomInts: ", e);
randomValues = null;
}
}
if (!randomValues) {
randomValues = [];
for (var i=0; i < length; i++) {
randomValues.push(Math.floor(Math.random() * 4294967296));
}
}
return randomValues;
};
var nonceKey = "RedirectLoginService.nonce";
var makeState = function(then) {
var nonce = String(new Date().getTime()) + "-" + getRandomInts(8).join("");
try {
window.localStorage[nonceKey] = nonce;
} catch(e) {
authLogger.log("RedirectLoginService.makeState, localStorage error: ", e);
}
return base64.urlencode(JSON.stringify({then: then, nonce:nonce}));
};
var parseState = function(state) {
var retval = {
then: null,
verified: false
};
var nonce = "";
try {
nonce = window.localStorage[nonceKey];
window.localStorage.removeItem(nonceKey);
} catch(e) {
authLogger.log("RedirectLoginService.parseState, localStorage error: ", e);
}
try {
var data = state ? JSON.parse(base64.urldecode(state)) : {};
if (data && data.nonce && nonce && data.nonce === nonce) {
retval.verified = true;
retval.then = data.then;
}
} catch(e) {
authLogger.error("RedirectLoginService.parseState, state error: ", e);
}
authLogger.error("RedirectLoginService.parseState", retval);
return retval;
};
return {
// Returns a promise that resolves with {user:{...}, token:'...', ttl:X}, or rejects with {error:'...'[,error_description:'...',error_uri:'...']}
login: function() {
if (_oauth_client_id === "") {
return $q.reject({error:'invalid_request', error_description:'RedirectLoginServiceProvider.OAuthClientID() not set'});
}
if (_oauth_authorize_uri === "") {
return $q.reject({error:'invalid_request', error_description:'RedirectLoginServiceProvider.OAuthAuthorizeURI() not set'});
}
if (_oauth_redirect_uri === "") {
return $q.reject({error:'invalid_request', error_description:'RedirectLoginServiceProvider.OAuthRedirectURI not set'});
}
var deferred = $q.defer();
var uri = new URI(_oauth_authorize_uri);
// Never send a local fragment to remote servers
var returnUri = new URI($location.url()).fragment("");
uri.query({
client_id: _oauth_client_id,
response_type: 'token',
state: makeState(returnUri.toString()),
redirect_uri: _oauth_redirect_uri
});
authLogger.log("RedirectLoginService.login(), redirecting", uri.toString());
window.location.href = uri.toString();
// Return a promise we never intend to keep, because we're redirecting to another page
return deferred.promise;
},
// Parses oauth callback parameters from window.location
// Returns a promise that resolves with {token:'...',then:'...',verified:true|false}, or rejects with {error:'...'[,error_description:'...',error_uri:'...']}
// If no token and no error is present, resolves with {}
// Example error codes: https://tools.ietf.org/html/rfc6749#section-5.2
finish: function() {
// Get url
var u = new URI($location.url());
// Read params
var queryParams = u.query(true);
var fragmentParams = new URI("?" + u.fragment()).query(true);
authLogger.log("RedirectLoginService.finish()", queryParams, fragmentParams);
// Error codes can come in query params or fragment params
// Handle an error response from the OAuth server
var error = queryParams.error || fragmentParams.error;
if (error) {
var error_description = queryParams.error_description || fragmentParams.error_description;
var error_uri = queryParams.error_uri || fragmentParams.error_uri;
authLogger.log("RedirectLoginService.finish(), error", error, error_description, error_uri);
return $q.reject({
error: error,
error_description: error_description,
error_uri: error_uri
});
}
var stateData = parseState(fragmentParams.state);
// Handle an access_token response
if (fragmentParams.access_token && (fragmentParams.token_type || "").toLowerCase() === "bearer") {
var deferred = $q.defer();
deferred.resolve({
token: fragmentParams.access_token,
ttl: fragmentParams.expires_in,
then: stateData.then,
verified: stateData.verified
});
return deferred.promise;
}
// No token and no error is invalid
return $q.reject({
error: "invalid_request",
error_description: "No API token returned"
});
}
};
};
});
'use strict';
// Provide a websocket implementation that behaves like $http
// Methods:
// $ws({
// url: "...", // required
// method: "...", // defaults to WATCH
// })
// returns a promise to the opened WebSocket
//
// $ws.available()
// returns true if WebSockets are available to use
angular.module('openshiftCommon')
.provider('$ws', function($httpProvider) {
// $get method is called to build the $ws service
this.$get = function($q, $injector, Logger) {
var authLogger = Logger.get("auth");
authLogger.log("$wsProvider.$get", arguments);
// Build list of interceptors from $httpProvider when constructing the $ws service
// Build in reverse-order, so the last interceptor added gets to handle the request first
var _interceptors = [];
angular.forEach($httpProvider.interceptors, function(interceptorFactory) {
if (angular.isString(interceptorFactory)) {
_interceptors.unshift($injector.get(interceptorFactory));
} else {
_interceptors.unshift($injector.invoke(interceptorFactory));
}
});
// Implement $ws()
var $ws = function(config) {
config.method = angular.uppercase(config.method || "WATCH");
authLogger.log("$ws (pre-intercept)", config.url.toString());
var serverRequest = function(config) {
authLogger.log("$ws (post-intercept)", config.url.toString());
var ws = new WebSocket(config.url, config.protocols);
if (config.onclose) { ws.onclose = config.onclose; }
if (config.onmessage) { ws.onmessage = config.onmessage; }
if (config.onopen) { ws.onopen = config.onopen; }
if (config.onerror) { ws.onerror = config.onerror; }
return ws;
};
// Apply interceptors to request config
var chain = [serverRequest, undefined];
var promise = $q.when(config);
angular.forEach(_interceptors, function(interceptor) {
if (interceptor.request || interceptor.requestError) {
chain.unshift(interceptor.request, interceptor.requestError);
}
// TODO: figure out how to get interceptors to handle response errors from web sockets
// if (interceptor.response || interceptor.responseError) {
// chain.push(interceptor.response, interceptor.responseError);
// }
});
while (chain.length) {
var thenFn = chain.shift();
var rejectFn = chain.shift();
promise = promise.then(thenFn, rejectFn);
}
return promise;
};
// Implement $ws.available()
$ws.available = function() {
try {
return !!WebSocket;
}
catch(e) {
return false;
}
};
return $ws;
};
})
/* A WebSocket factory for kubernetesContainerTerminal */
.factory("ContainerWebSocket", function(API_CFG, $ws) {
return function AuthWebSocket(url, protocols) {
var scheme;
if (url.indexOf("/") === 0) {
scheme = window.location.protocol === "http:" ? "ws://" : "wss://";
url = scheme + API_CFG.openshift.hostPort + url;
}
return $ws({ url: url, method: "WATCH", protocols: protocols, auth: {} });
};
});
// Karma configuration
// http://karma-runner.github.io/0.12/config/configuration-file.html
// Generated on 2014-09-12 using
// generator-karma 0.8.3
module.exports = function(config) {
'use strict';
config.set({
// base path, that will be used to resolve files and exclude
basePath: '../',
// testing framework to use (jasmine/mocha/qunit/...)
frameworks: ['jasmine'],
// list of files / patterns to load in the browser
files: [
"bower_components/jquery/dist/jquery.js",
"bower_components/lodash/lodash.js",
"bower_components/angular/angular.js",
'bower_components/angular-mocks/angular-mocks.js',
"bower_components/angular-sanitize/angular-sanitize.js",
"bower_components/js-logger/src/logger.js",
"bower_components/messenger/build/js/messenger.js",
"bower_components/angular-utf8-base64/angular-utf8-base64.js",
"bower_components/uri.js/src/URI.js",
"bower_components/uri.js/src/URITemplate.js",
"bower_components/uri.js/src/jquery.URI.js",
"bower_components/uri.js/src/URI.fragmentURI.js",
"bower_components/hawtio-core/hawtio-core.js",
"bower_components/kubernetes-container-terminal/dist/container-terminal.js",
"bower_components/hawtio-extension-service/dist/hawtio-extension-service.js",
'src/**/*.js',
'test/spec/spec-helper.js',
'test/spec/fixtures/api-discovery.js',
'test/spec/**/*.js'
],
// list of files / patterns to exclude
exclude: [],
// use dots reporter, as travis terminal does not support escaping sequences
// possible values: 'dots', 'progress'
// CLI --reporters progress
reporters: ['progress', 'junit'],
junitReporter: {
// will be resolved to basePath (in the same way as files/exclude patterns)
outputFile: 'test/test-results.xml'
},
// web server port
port: 8443,
colors: true,
// level of logging
// possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
logLevel: config.LOG_DEBUG,
// enable / disable watching file and executing tests whenever any file changes
// CLI --auto-watch --no-auto-watch
autoWatch: true,
// Start these browsers, currently available:
// - Chrome
// - ChromeCanary
// - Firefox
// - Opera
// - Safari (only Mac)
// - PhantomJS
// - IE (only Windows)
// CLI --browsers Chrome,Firefox,Safari
browsers: [process.env.TRAVIS ? 'Firefox' : 'Chrome'],
// If browser does not capture in given timeout [ms], kill it
// CLI --capture-timeout 5000
captureTimeout: 20000,
// Auto run tests on start (when browsers are captured) and exit
// CLI --single-run --no-single-run
singleRun: false,
// report which specs are slower than 500ms
// CLI --report-slower-than 500
reportSlowerThan: 500
});
};
"use strict";
describe("DataService", function(){
var DataService;
beforeEach(function(){
inject(function(_DataService_){
DataService = _DataService_;
});
});
describe("#url", function(){
var tc = [
// Empty tests
[null, null],
[{}, null],
// Unknown resources
[{resource:''}, null],
[{resource:'bogus'}, null],
// Kind is not allowed
[{resource:'Pod'}, null],
[{resource:'User'}, null],
// resource normalization
[{resource:'users'}, "http://localhost:8443/oapi/v1/users"],
[{resource:'Users'}, "http://localhost:8443/oapi/v1/users"],
[{resource:'oauthaccesstokens'}, "http://localhost:8443/oapi/v1/oauthaccesstokens"],
[{resource:'OAuthAccessTokens'}, "http://localhost:8443/oapi/v1/oauthaccesstokens"],
[{resource:'pods'}, "http://localhost:8443/api/v1/pods"],
[{resource:'Pods'}, "http://localhost:8443/api/v1/pods"],
// OpenShift resource
[{resource:'builds' }, "http://localhost:8443/oapi/v1/builds"],
[{resource:'builds', namespace:"foo" }, "http://localhost:8443/oapi/v1/namespaces/foo/builds"],
[{resource:'builds', name:"bar"}, "http://localhost:8443/oapi/v1/builds/bar"],
[{resource:'builds', namespace:"foo", name:"bar"}, "http://localhost:8443/oapi/v1/namespaces/foo/builds/bar"],
// k8s resource
[{resource:'replicationcontrollers' }, "http://localhost:8443/api/v1/replicationcontrollers"],
[{resource:'replicationcontrollers', namespace:"foo" }, "http://localhost:8443/api/v1/namespaces/foo/replicationcontrollers"],
[{resource:'replicationcontrollers', name:"bar"}, "http://localhost:8443/api/v1/replicationcontrollers/bar"],
[{resource:'replicationcontrollers', namespace:"foo", name:"bar"}, "http://localhost:8443/api/v1/namespaces/foo/replicationcontrollers/bar"],
// Subresources and webhooks
[{resource:'pods/proxy', name:"mypod:1123", namespace:"foo"}, "http://localhost:8443/api/v1/namespaces/foo/pods/mypod%3A1123/proxy"],
[{resource:'builds/clone', name:"mybuild", namespace:"foo"}, "http://localhost:8443/oapi/v1/namespaces/foo/builds/mybuild/clone"],
[{resource:'buildconfigs/instantiate', name:"mycfg", namespace:"foo"}, "http://localhost:8443/oapi/v1/namespaces/foo/buildconfigs/mycfg/instantiate"],
[{resource:'buildconfigs/webhooks/123/github', name:"mycfg", namespace:"foo"}, "http://localhost:8443/oapi/v1/namespaces/foo/buildconfigs/mycfg/webhooks/123/github"],
[{resource:'buildconfigs/webhooks/123?234/github', name:"mycfg", namespace:"foo"}, "http://localhost:8443/oapi/v1/namespaces/foo/buildconfigs/mycfg/webhooks/123%3F234/github"],
// Subresources aren't lowercased
[{resource:'buildconfigs/webhooks/Aa1/github', name:"mycfg", namespace:"foo"}, "http://localhost:8443/oapi/v1/namespaces/foo/buildconfigs/mycfg/webhooks/Aa1/github"],
// Plain websocket
[{resource:'pods', namespace:"foo", isWebsocket:true }, "ws://localhost:8443/api/v1/namespaces/foo/pods"],
// Watch resource
[{resource:'pods', namespace:"foo", isWebsocket:true, watch: true }, "ws://localhost:8443/api/v1/namespaces/foo/pods?watch=true"],
[{resource:'pods', namespace:"foo", isWebsocket:true, watch: true, resourceVersion:"5" }, "ws://localhost:8443/api/v1/namespaces/foo/pods?watch=true&resourceVersion=5"],
// Follow log
// subresource is ignored without a resource name
[{resource:'pods/log', namespace:"foo", isWebsocket:true, follow: true }, "ws://localhost:8443/api/v1/namespaces/foo/pods?follow=true"],
[{resource:'builds/log', namespace:"foo", isWebsocket:true, follow: true }, "ws://localhost:8443/oapi/v1/namespaces/foo/builds?follow=true"],
// subresource is honored with a resource name
[{resource:'pods/log', name:"p", namespace:"foo", isWebsocket:true, follow: true }, "ws://localhost:8443/api/v1/namespaces/foo/pods/p/log?follow=true"],
[{resource:'builds/log', name:"b", namespace:"foo", isWebsocket:true, follow: true }, "ws://localhost:8443/oapi/v1/namespaces/foo/builds/b/log?follow=true"],
// Namespaced subresource with params
[{resource:'pods/proxy', name:"mypod", namespace:"myns", myparam1:"myvalue"}, "http://localhost:8443/api/v1/namespaces/myns/pods/mypod/proxy?myparam1=myvalue"],
// Different API versions
[{resource:'builds',version:'v1beta3'}, null],
[{resource:'builds',version:'v1' }, "http://localhost:8443/oapi/v1/builds"],
[{resource:'builds',version:'unknown'}, null],
[{resource:'pods', version:'v1beta3'}, null],
[{resource:'pods', version:'v1' }, "http://localhost:8443/api/v1/pods"],
[{resource:'pods', version:'unknown'}, null],
// Different API groups
[{resource:'jobs', group: 'extensions', version:'v1beta1'}, "http://localhost:8443/apis/extensions/v1beta1/jobs"]
];
angular.forEach(tc, function(item) {
it('should generate a correct URL for ' + JSON.stringify(item[0]), function() {
expect(DataService.url(item[0])).toEqual(item[1]);
});
});
});
});
"use strict";
//load the module
beforeEach(module('openshiftCommon'));
beforeEach(module(function ($provide) {
$provide.provider("HawtioNavBuilder", function() {
function Mocked() {}
this.create = function() {return this;};
this.id = function() {return this;};
this.title = function() {return this;};
this.template = function() {return this;};
this.isSelected = function() {return this;};
this.href = function() {return this;};
this.page = function() {return this;};
this.subPath = function() {return this;};
this.build = function() {return this;};
this.join = function() {return "";};
this.$get = function() {return new Mocked();};
});
$provide.factory("HawtioNav", function(){
return {add: function() {}};
});
$provide.factory("HawtioExtension", function() {
return {
add: function() {}
};
});
// Make sure a base location exists in the generated test html
if (!$('head base').length) {
$('head').append($('<base href="/">'));
}
angular.module('openshiftCommon').config(function(AuthServiceProvider) {
AuthServiceProvider.UserStore('MemoryUserStore');
})
.constant("API_CFG", _.get(window.OPENSHIFT_CONFIG, "api", {}))
.constant("APIS_CFG", _.get(window.OPENSHIFT_CONFIG, "apis", {}))
.constant("AUTH_CFG", _.get(window.OPENSHIFT_CONFIG, "auth", {}))
.constant("LOGGING_URL", _.get(window.OPENSHIFT_CONFIG, "loggingURL"))
.constant("METRICS_URL", _.get(window.OPENSHIFT_CONFIG, "metricsURL"))
.constant("LIMIT_REQUEST_OVERRIDES", _.get(window.OPENSHIFT_CONFIG, "limitRequestOverrides"))
.config(function($httpProvider, AuthServiceProvider, AUTH_CFG, API_CFG) {
AuthServiceProvider.LoginService('RedirectLoginService');
AuthServiceProvider.LogoutService('DeleteTokenLogoutService');
AuthServiceProvider.UserStore('LocalStorageUserStore');
});
hawtioPluginLoader.addModule('openshiftCommon');
}));
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment