+
-
-
{{ template.metadata.name }}
-
- Version {{ template | annotation:'version' }}
- Provided By: {{ template | provider }}
-
-
{{ template | description }}
-
-
Source
{{ templateUrl }}
-
+
+
Images
@@ -31,8 +23,8 @@ Images
Items will be created in the {{ projectDisplayName() }} project.
diff --git a/assets/test/karma.conf.js b/assets/test/karma.conf.js
index 30b55a763fc6..66886bc5434e 100644
--- a/assets/test/karma.conf.js
+++ b/assets/test/karma.conf.js
@@ -45,6 +45,7 @@ module.exports = function(config) {
"bower_components/kubernetes-label-selector/labelFilter.js",
'app/scripts/**/*.js',
//'test/mock/**/*.js',
+ 'test/spec/spec-helper.js',
'test/spec/**/*.js'
],
diff --git a/assets/test/spec/controllers/create/createFromImageSpec.js b/assets/test/spec/controllers/create/createFromImageSpec.js
new file mode 100644
index 000000000000..3c9a77fa2fa3
--- /dev/null
+++ b/assets/test/spec/controllers/create/createFromImageSpec.js
@@ -0,0 +1,43 @@
+"use strict";
+
+describe("CreateFromImageController", function(){
+ var controller;
+ var $scope = {
+ name: "apPname",
+ projectName: "aProjectName"
+ };
+ var $routeParams = {
+ imageName: "anImageName",
+ imageTag: "latest",
+ namespace: "aNamespace"
+ };
+ var DataService = {};
+ var Navigate = {};
+
+
+ beforeEach(function(){
+ inject(function(_$controller_){
+ // The injector unwraps the underscores (_) from around the parameter names when matching
+ controller = _$controller_("CreateFromImageController", {
+ $scope: $scope,
+ $routeParams: $routeParams,
+ DataService: {
+ get: function(kind){
+ if(kind === 'imageRepositories'){
+ return {};
+ }
+ return {};
+ }
+ },
+ Navigate: {
+ toErrorPage: function(message){}
+ },
+ NameGenerator: {
+ suggestFromSourceUrl: function(sourceUrl, kinds, namespace){
+ return "aName";
+ }
+ }
+ });
+ });
+ });
+});
\ No newline at end of file
diff --git a/assets/test/spec/directives/oscKeyValuesSpec.js b/assets/test/spec/directives/oscKeyValuesSpec.js
new file mode 100644
index 000000000000..7031930835f8
--- /dev/null
+++ b/assets/test/spec/directives/oscKeyValuesSpec.js
@@ -0,0 +1,124 @@
+"use strict";
+
+describe("KeyValuesEntryController", function(){
+ var scope, controller;
+ beforeEach(function(){
+ scope = {
+ value: "foo"
+ };
+ inject(function(_$controller_){
+ // The injector unwraps the underscores (_) from around the parameter names when matching
+ controller = _$controller_("KeyValuesEntryController", {$scope: scope});
+ });
+ });
+
+ describe("#edit", function(){
+ it("should copy the original value", function(){
+ scope.edit();
+ expect(scope.value).toEqual(scope.originalValue);
+ expect(scope.editing).toEqual(true);
+ });
+ });
+
+ describe("#cancel", function(){
+ it("should reset value to the original value", function(){
+ scope.originalValue = "bar";
+ scope.cancel();
+ expect(scope.value).toEqual("bar");
+ expect(scope.editing).toEqual(false);
+ });
+ });
+
+ describe("#update", function(){
+ var entries = { foo: "abc"};
+ it("should update the entries for the key when the value is not empty", function(){
+ scope.update("foo", "bar", entries);
+ expect(entries["foo"]).toEqual("bar");
+ expect(scope.editing).toEqual(false);
+ });
+ });
+});
+
+describe("KeyValuesController", function(){
+ var scope, controller;
+
+ beforeEach(function(){
+ scope = {
+ entries: { "foo": "bar"},
+ form: {
+ $setPristine: function(){},
+ $setUntouched: function(){},
+ $setValidity: function(){},
+ },
+ readonlyKeys: ""
+ };
+ inject(function(_$controller_){
+ // The injector unwraps the underscores (_) from around the parameter names when matching
+ controller = _$controller_("KeyValuesController", {$scope: scope});
+ });
+
+ });
+
+ describe("#allowDelete", function(){
+
+ it("should when the deletePolicy equals always", function(){
+ expect(scope.allowDelete("foo")).toBe(true);
+ });
+
+ it("should not when the deletePolicy equals never", function(){
+ scope.deletePolicy = "never";
+ expect(scope.allowDelete("foo")).toBe(false);
+ });
+
+ it("should when the deletePolicy equals added and the entry was not originally in entries", function(){
+ scope.deletePolicy = "added";
+ scope.key = "abc";
+ scope.value = "def";
+ scope.addEntry();
+ expect(scope.allowDelete("abc")).toBe(true);
+ });
+
+ it("should not when the deletePolicy equals added and the entry was originally in entries", function(){
+ scope.deletePolicy = "added";
+ expect(scope.allowDelete("foo")).toBe(false);
+ });
+
+ });
+
+ describe("#addEntry", function(){
+ it("should not add the entry if the key is in the readonly list", function(){
+ scope.readonlyKeys = "abc";
+ scope.key = "abc";
+ scope.value = "xyz";
+ scope.addEntry();
+ expect(scope.entries["abc"]).toBe(undefined);
+ expect(scope.key).toEqual("abc");
+ expect(scope.value).toEqual("xyz");
+ });
+
+ it("should add the key/value to the scope", function(){
+ scope.key = "foo";
+ scope.value = "bar";
+ scope.addEntry();
+ scope.key = "abc";
+ scope.value = "def";
+ scope.addEntry();
+ expect(scope.entries["foo"]).toEqual("bar");
+ expect(scope.entries["abc"]).toEqual("def");
+ expect(scope.key).toEqual(null);
+ expect(scope.value).toEqual(null);
+ });
+
+ });
+
+ describe("#deleteEntry", function(){
+ //TODO add test for nonrecognized key?
+
+ it("should delete the key/value from the scope", function(){
+ scope.deleteEntry("foo");
+ expect(scope.entries["foo"]).toBe(undefined);
+ });
+ });
+});
+
+
diff --git a/assets/test/spec/directives/oscResourceNameValidatorSpec.js b/assets/test/spec/directives/oscResourceNameValidatorSpec.js
new file mode 100644
index 000000000000..5ff5204ba777
--- /dev/null
+++ b/assets/test/spec/directives/oscResourceNameValidatorSpec.js
@@ -0,0 +1,63 @@
+"use strict";
+
+describe("oscResourceNameValidator", function(){
+
+ var $scope, form;
+ beforeEach(function(){
+ inject(function($compile, $rootScope){
+ $scope = $rootScope;
+ var element = angular.element(
+ '
'
+ );
+ $scope.model = { name: null };
+ $compile(element)($scope);
+ form = $scope.form;
+ });
+ });
+
+ it("should disallow a null name", function(){
+ form.name.$setViewValue(null);
+ expect(form.name.$valid).toBe(false);
+ });
+
+ it("should disallow an empty name", function(){
+ form.name.$setViewValue("");
+ expect(form.name.$valid).toBe(false);
+ });
+
+ it("should disallow a blank name", function(){
+ form.name.$setViewValue(" ");
+ expect(form.name.$valid).toBe(false);
+ });
+
+ it("should disallow a name with a blank", function(){
+ form.name.$setViewValue("foo bar");
+ expect(form.name.$valid).toBe(false);
+ });
+
+ it("should disallow a name with starting with a .", function(){
+ form.name.$setViewValue(".foobar");
+ expect(form.name.$valid).toBe(false);
+ });
+
+ it("should disallow a name that is too long", function(){
+ form.name.$setViewValue("abcdefghijklmnopqrstuvwxy");
+ expect(form.name.$valid).toBe(false);
+ });
+
+
+
+ it("should allow a name with a dash", function(){
+ form.name.$setViewValue("foo-bar");
+ expect(form.name.$valid).toBe(true);
+ });
+
+ it("should allow a name with a dot", function(){
+ form.name.$setViewValue("foo99.bar");
+ expect(form.name.$valid).toBe(true);
+ });
+
+});
+
diff --git a/assets/test/spec/filters/defaultIfEmptySpec.js b/assets/test/spec/filters/defaultIfEmptySpec.js
new file mode 100644
index 000000000000..ca3b1fe48179
--- /dev/null
+++ b/assets/test/spec/filters/defaultIfEmptySpec.js
@@ -0,0 +1,32 @@
+"use strict";
+
+describe("defaultIfBlankFilter", function(){
+ var defaultIfBlank;
+
+ beforeEach(
+ inject(function(defaultIfBlankFilter){
+ defaultIfBlank = defaultIfBlankFilter;
+ })
+ );
+
+ it("should return the value if a non-string", function(){
+ expect(defaultIfBlank(1, "foo")).toBe("1");
+ });
+
+ it("should return the value if a non empty string", function(){
+ expect(defaultIfBlank("theValue", "foo")).toBe("theValue");
+ });
+
+ it("should return the default if a null string", function(){
+ expect(defaultIfBlank(null, "foo")).toBe("foo");
+ });
+
+ it("should return the default if an empty string", function(){
+ expect(defaultIfBlank("", "foo")).toBe("foo");
+ });
+
+ it("should return the default if a blank string", function(){
+ expect(defaultIfBlank(" ", "foo")).toBe("foo");
+ });
+
+});
\ No newline at end of file
diff --git a/assets/test/spec/filters/httpHttpsSpec.js b/assets/test/spec/filters/httpHttpsSpec.js
new file mode 100644
index 000000000000..fbef64267339
--- /dev/null
+++ b/assets/test/spec/filters/httpHttpsSpec.js
@@ -0,0 +1,18 @@
+"use strict";
+
+describe("httpHttpsFilter", function(){
+
+ it("should return https:// when true", function(){
+ inject(function(httpHttpsFilter){
+ expect(httpHttpsFilter(true)).toBe("https://");
+ })
+ });
+
+ it("should return http:// when false", function(){
+ inject(function(httpHttpsFilter){
+ expect(httpHttpsFilter(false)).toBe("http://");
+ })
+ });
+});
+
+
diff --git a/assets/test/spec/filters/valuesInSpec.js b/assets/test/spec/filters/valuesInSpec.js
new file mode 100644
index 000000000000..0a2bd82a7862
--- /dev/null
+++ b/assets/test/spec/filters/valuesInSpec.js
@@ -0,0 +1,22 @@
+"use strict";
+
+describe("valuesInFilter", function(){
+ var filter;
+ var entries = {
+ foo: "bar",
+ abc: "xyz",
+ another: "value"
+ };
+ beforeEach(function(){
+ inject(function(valuesInFilter){
+ filter = valuesInFilter;
+ });
+ });
+
+ it("should return a subset of the entries", function(){
+ var results = filter(entries,"foo,another");
+ delete entries["abc"];
+ expect(results).toEqual(entries);
+ });
+});
+
\ No newline at end of file
diff --git a/assets/test/spec/filters/valuesNotInSpec.js b/assets/test/spec/filters/valuesNotInSpec.js
new file mode 100644
index 000000000000..fa1a5600308e
--- /dev/null
+++ b/assets/test/spec/filters/valuesNotInSpec.js
@@ -0,0 +1,23 @@
+"use strict";
+
+describe("valuesNotInFilter", function(){
+ var filter;
+ var entries = {
+ foo: "bar",
+ abc: "xyz",
+ another: "value"
+ };
+ beforeEach(function(){
+ inject(function(valuesNotInFilter){
+ filter = valuesNotInFilter;
+ });
+ });
+
+ it("should return a subset of the entries", function(){
+ var results = filter(entries,"foo,another");
+ delete entries["foo"];
+ delete entries["another"];
+ expect(results).toEqual(entries);
+ });
+});
+
\ No newline at end of file
diff --git a/assets/test/spec/filters/yesNoSpec.js b/assets/test/spec/filters/yesNoSpec.js
new file mode 100644
index 000000000000..6dde32f696d8
--- /dev/null
+++ b/assets/test/spec/filters/yesNoSpec.js
@@ -0,0 +1,18 @@
+"use strict";
+
+describe("yesNoFilter", function(){
+
+ it("should return Yes when true", function(){
+ inject(function(yesNoFilter){
+ expect(yesNoFilter(true)).toBe("Yes");
+ })
+ });
+
+ it("should return No when false", function(){
+ inject(function(yesNoFilter){
+ expect(yesNoFilter(false)).toBe("No");
+ })
+ });
+});
+
+
diff --git a/assets/test/spec/services/applicationGeneratorSpec.js b/assets/test/spec/services/applicationGeneratorSpec.js
new file mode 100644
index 000000000000..579d34844e14
--- /dev/null
+++ b/assets/test/spec/services/applicationGeneratorSpec.js
@@ -0,0 +1,353 @@
+"use strict";
+
+describe("ApplicationGenerator", function(){
+ var ApplicationGenerator;
+ var input;
+
+ beforeEach(function(){
+ module('openshiftConsole', function($provide){
+ $provide.value("DataService",{
+ osApiVersion: "v1beta1",
+ k8sApiVersion: "v1beta3"
+ });
+ });
+
+ inject(function(_ApplicationGenerator_){
+ ApplicationGenerator = _ApplicationGenerator_;
+ ApplicationGenerator._generateSecret = function(){
+ return "secret101";
+ };
+ });
+
+ input = {
+ name: "ruby-hello-world",
+ routing: true,
+ buildConfig: {
+ sourceUrl: "https://github.com/openshift/ruby-hello-world.git",
+ buildOnSourceChange: true,
+ buildOnImageChange: true
+ },
+ deploymentConfig: {
+ deployOnConfigChange: true,
+ deployOnNewImage: true,
+ envVars: {
+ "ADMIN_USERNAME" : "adminEME",
+ "ADMIN_PASSWORD" : "xFSkebip",
+ "MYSQL_ROOT_PASSWORD" : "qX6JGmjX",
+ "MYSQL_DATABASE" : "root"
+ }
+ },
+ labels : {
+ foo: "bar",
+ abc: "xyz"
+ },
+ scaling: {
+ replicas: 1
+ },
+ imageName: "origin-ruby-sample",
+ imageTag: "latest",
+ imageRepo: {
+ "kind": "ImageRepository",
+ "apiVersion": "v1beta1",
+ "metadata": {
+ "name": "origin-ruby-sample",
+ "namespace": "test",
+ "selfLink": "/osapi/v1beta1/imageRepositories/origin-ruby-sample?namespace=test",
+ "uid": "ea1d67fc-c358-11e4-90e6-080027c5bfa9",
+ "resourceVersion": "150",
+ "creationTimestamp": "2015-03-05T16:58:58Z"
+ },
+ "tags": {
+ "latest": "ea15999fd97b2f1bafffd615697ef8c14abdfd9ab17ff4ed67cf5857fec8d6c0"
+ },
+ "status": {
+ "dockerImageRepository": "172.30.17.58:5000/test/origin-ruby-sample"
+ }
+ },
+ image: {
+ "kind" : "Image",
+ "metadata" : {
+ "name" : "ea15999fd97b2f1bafffd615697ef8c14abdfd9ab17ff4ed67cf5857fec8d6c0"
+ },
+ "dockerImageMetadata" : {
+ "ContainerConfig" : {
+ "ExposedPorts": {
+ "443/tcp": {},
+ "80/tcp": {}
+ },
+ "Env": [
+ "STI_SCRIPTS_URL"
+ ]
+ }
+ }
+ }
+ };
+ });
+
+ describe("#_generateService", function(){
+ it("should generate a headless service when no ports are exposed", function(){
+ var copy = angular.copy(input);
+ copy.image.dockerImageMetadata.ContainerConfig.ExposedPorts = {};
+ var service = ApplicationGenerator._generateService(copy, "theServiceName", "None");
+ expect(service).toEqual(
+ {
+ "kind": "Service",
+ "apiVersion": "v1beta3",
+ "metadata": {
+ "name": "theServiceName",
+ "labels" : {
+ "foo" : "bar",
+ "abc" : "xyz" }
+ },
+ "spec": {
+ "portalIP" : "None",
+ "selector": {
+ "deploymentconfig": "ruby-hello-world"
+ }
+ }
+ });
+ });
+ });
+
+ describe("#_generateRoute", function(){
+
+ it("should generate nothing if routing is not required", function(){
+ input.routing = false;
+ expect(ApplicationGenerator._generateRoute(input, input.name, "theServiceName")).toBe(null);
+ });
+
+ it("should generate an unsecure Route when routing is required", function(){
+ var route = ApplicationGenerator._generateRoute(input, input.name, "theServiceName");
+ expect(route).toEqual({
+ kind: "Route",
+ apiVersion: 'v1beta1',
+ metadata: {
+ name: "ruby-hello-world",
+ labels : {
+ "foo" : "bar",
+ "abc" : "xyz"
+ }
+ },
+ serviceName: "theServiceName",
+ tls: {
+ termination: "unsecure"
+ }
+ });
+ });
+ });
+
+ describe("generating applications from image that includes source", function(){
+ var resources;
+ beforeEach(function(){
+ resources = ApplicationGenerator.generate(input);
+ });
+
+ it("should generate a BuildConfig for the source", function(){
+ expect(resources.buildConfig).toEqual(
+ {
+ "apiVersion": "v1beta1",
+ "kind": "BuildConfig",
+ "metadata": {
+ "name": "ruby-hello-world",
+ labels : {
+ "foo" : "bar",
+ "abc" : "xyz",
+ "name": "ruby-hello-world",
+ "generatedby": "OpenShiftWebConsole"
+ }
+ },
+ "parameters": {
+ "output": {
+ "to": {
+ "name": "ruby-hello-world"
+ }
+ },
+ "source": {
+ "git": {
+ "ref": "master",
+ "uri": "https://github.com/openshift/ruby-hello-world.git"
+ },
+ "type": "Git"
+ },
+ "strategy": {
+ "type": "STI",
+ "stiStrategy" : {
+ "image" : "172.30.17.58:5000/test/origin-ruby-sample:latest"
+ }
+ }
+ },
+ "triggers": [
+ {
+ "generic": {
+ "secret": "secret101"
+ },
+ "type": "generic"
+ },
+ {
+ "github": {
+ "secret": "secret101"
+ },
+ "type": "github"
+ },
+ {
+ "imageChange" : {
+ "image" : "172.30.17.58:5000/test/origin-ruby-sample:latest",
+ "from" : {
+ "name" : "origin-ruby-sample"
+ },
+ "tag" : "latest"
+ },
+ "type" : "imageChange"
+ }
+ ]
+
+ }
+ );
+ });
+
+ it("should generate an ImageRepository for the build output", function(){
+ expect(resources.imageRepo).toEqual(
+ {
+ "apiVersion": "v1beta1",
+ "kind": "ImageRepository",
+ "metadata": {
+ "name": "ruby-hello-world",
+ labels : {
+ "foo" : "bar",
+ "abc" : "xyz",
+ "name": "ruby-hello-world",
+ "generatedby": "OpenShiftWebConsole"
+ }
+ }
+ }
+ );
+ });
+
+ it("should generate a Service for the build output", function(){
+ expect(resources.service).toEqual(
+ {
+ "kind": "Service",
+ "apiVersion": "v1beta3",
+ "metadata": {
+ "name": "ruby-hello-world",
+ "labels" : {
+ "foo" : "bar",
+ "abc" : "xyz",
+ "name": "ruby-hello-world",
+ "generatedby": "OpenShiftWebConsole"
+ }
+ },
+ "spec": {
+ "port": 80,
+ "containerPort" : 80,
+ "protocol": "tcp",
+ "selector": {
+ "deploymentconfig": "ruby-hello-world"
+ }
+ }
+ }
+ );
+ });
+
+ it("should generate a DeploymentConfig for the BuildConfig output image", function(){
+ var resources = ApplicationGenerator.generate(input);
+ expect(resources.deploymentConfig).toEqual(
+ {
+ "apiVersion": "v1beta1",
+ "kind": "DeploymentConfig",
+ "metadata": {
+ "name": "ruby-hello-world",
+ "labels": {
+ "foo" : "bar",
+ "abc" : "xyz",
+ "name": "ruby-hello-world",
+ "generatedby" : "OpenShiftWebConsole",
+ "deploymentconfig": "ruby-hello-world"
+ }
+ },
+ "template": {
+ "controllerTemplate": {
+ "podTemplate": {
+ "desiredState": {
+ "manifest": {
+ "containers": [
+ {
+ "image": "ruby-hello-world:latest",
+ "name": "ruby-hello-world",
+ "ports": [
+ {
+ "containerPort": 443,
+ "name": "ruby-hello-world-tcp-443",
+ "protocol": "tcp"
+ },
+ {
+ "containerPort": 80,
+ "name": "ruby-hello-world-tcp-80",
+ "protocol": "tcp"
+ }
+ ],
+ "env" : [
+ {
+ "name": "ADMIN_USERNAME",
+ "value": "adminEME"
+ },
+ {
+ "name": "ADMIN_PASSWORD",
+ "value": "xFSkebip"
+ },
+ {
+ "name": "MYSQL_ROOT_PASSWORD",
+ "value": "qX6JGmjX"
+ },
+ {
+ "name": "MYSQL_DATABASE",
+ "value": "root"
+ }
+ ]
+ }
+ ],
+ "version": "v1beta3"
+ }
+ },
+ "labels": {
+ "foo" : "bar",
+ "abc" : "xyz",
+ "name": "ruby-hello-world",
+ "generatedby" : "OpenShiftWebConsole",
+ "deploymentconfig": "ruby-hello-world"
+ }
+ },
+ "replicaSelector": {
+ "deploymentconfig": "ruby-hello-world"
+ },
+ "replicas": 1
+ },
+ "strategy": {
+ "type": "Recreate"
+ }
+ },
+ "triggers": [
+ {
+ "type": "ImageChange",
+ "imageChangeParams": {
+ "automatic": true,
+ "containerNames": [
+ "ruby-hello-world"
+ ],
+ "from": {
+ "name": "ruby-hello-world"
+ },
+ "tag": "latest"
+ }
+ },
+ {
+ "type": "ConfigChange"
+ }
+ ]
+ }
+ );
+ });
+
+ });
+
+});
\ No newline at end of file
diff --git a/assets/test/spec/services/nameGeneratorSpec.js b/assets/test/spec/services/nameGeneratorSpec.js
new file mode 100644
index 000000000000..d6538566b2c8
--- /dev/null
+++ b/assets/test/spec/services/nameGeneratorSpec.js
@@ -0,0 +1,31 @@
+"use strict";
+
+describe("NameGenerator", function(){
+ var NameGenerator;
+
+ beforeEach(function($provide){
+
+ inject(function(_NameGenerator_){
+ NameGenerator = _NameGenerator_;
+ });
+ });
+
+ describe("#suggestFromSourceUrl", function(){
+
+ var sourceUrl = "git@github.com:openshift/ruby-hello-world.git";
+
+ it("should suggest a name based on git source url ending with 'git'", function(){
+ var result = NameGenerator.suggestFromSourceUrl(sourceUrl);
+ expect(result).toEqual("ruby-hello-world");
+ });
+
+ it("should suggest a name based on git source url not ending with 'git'", function(){
+
+ sourceUrl = "git@github.com:openshift/ruby-hello-world";
+ var result = NameGenerator.suggestFromSourceUrl(sourceUrl);
+ expect(result).toEqual("ruby-hello-world");
+ });
+
+ });
+
+});
diff --git a/assets/test/spec/spec-helper.js b/assets/test/spec/spec-helper.js
new file mode 100644
index 000000000000..93587427058e
--- /dev/null
+++ b/assets/test/spec/spec-helper.js
@@ -0,0 +1,44 @@
+"use strict";
+// Angular is refusing to recognize the HawtioNav stuff
+// when testing even though its being loaded
+ 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() {}};
+ });
+
+}));
+
+beforeEach(function(){
+ module('openshiftConsole', function($provide){
+ $provide.factory("DataService", function(){
+ return {};
+ });
+ });
+});
+
+// Make sure a base location exists in the generated test html
+ if (!$('head base').length) {
+ $('head').append($('
'));
+ }
+
+ angular.module('openshiftConsole').config(function(AuthServiceProvider) {
+ AuthServiceProvider.UserStore('MemoryUserStore');
+ });
+
+ //load the module
+beforeEach(module('openshiftConsole'));
diff --git a/examples/image-streams/image-streams.json b/examples/image-streams/image-streams.json
index 91ed65d8bf06..fb2d06cee89e 100644
--- a/examples/image-streams/image-streams.json
+++ b/examples/image-streams/image-streams.json
@@ -9,7 +9,18 @@
"name": "ruby-20-centos7"
},
"spec": {
- "dockerImageRepository": "openshift/ruby-20-centos7"
+ "dockerImageRepository": "openshift/ruby-20-centos7",
+ "tags": [
+ {
+ "name": "latest",
+ "annotations": {
+ "description": "Build and run Ruby 2.0 applications",
+ "iconClass": "icon-ruby",
+ "tags": "builder,ruby",
+ "version": "2.0"
+ }
+ }
+ ]
}
},
{
@@ -19,7 +30,18 @@
"name": "nodejs-010-centos7"
},
"spec": {
- "dockerImageRepository": "openshift/nodejs-010-centos7"
+ "dockerImageRepository": "openshift/nodejs-010-centos7",
+ "tags": [
+ {
+ "name": "latest",
+ "annotations": {
+ "description" : "Build and run NodeJS 0.10 applications",
+ "iconClass" : "icon-nodejs",
+ "tags" : "builder,nodejs",
+ "version" : "0.10"
+ }
+ }
+ ]
}
},
{
@@ -29,7 +51,18 @@
"name": "wildfly-8-centos"
},
"spec": {
- "dockerImageRepository": "openshift/wildfly-8-centos"
+ "dockerImageRepository": "openshift/wildfly-8-centos",
+ "tags": [
+ {
+ "name": "latest",
+ "annotations": {
+ "description" : "Build and run Java applications on Wildfly 8",
+ "iconClass" : "icon-wildfly",
+ "tags" : "builder,wildfly,java",
+ "version" : "8"
+ }
+ }
+ ]
}
},
{
@@ -63,4 +96,4 @@
}
}
]
-}
+}
\ No newline at end of file
diff --git a/examples/sample-app/application-template-stibuild.json b/examples/sample-app/application-template-stibuild.json
index 70dc657e4870..e4bccde8a268 100644
--- a/examples/sample-app/application-template-stibuild.json
+++ b/examples/sample-app/application-template-stibuild.json
@@ -245,7 +245,9 @@
"kind": "Template",
"metadata": {
"annotations": {
- "description": "This example shows how to create a simple ruby application in openshift origin v3"
+ "description": "This example shows how to create a simple ruby application in openshift origin v3",
+ "tags": "instant-app,ruby,mysql",
+ "iconClass" : "icon-ruby"
},
"name": "ruby-helloworld-sample"
},
diff --git a/pkg/assets/bindata.go b/pkg/assets/bindata.go
index 04bf25824202..4233718d1ac5 100644
--- a/pkg/assets/bindata.go
+++ b/pkg/assets/bindata.go
@@ -10908,6 +10908,7 @@ you can use the generic selector below, but it's slower:
.tile.tile-status {
background-color: #e6ecf1;
border-top: 5px solid #bfcedb;
+ margin-top: 40px;
}
.tile-click {
cursor: pointer;
@@ -11130,6 +11131,23 @@ ul.messenger-theme-flat .messenger-message.alert-info .messenger-message-inner:b
content: "\e604";
background-color: transparent;
}
+.btn-file {
+ position: relative;
+ overflow: hidden;
+}
+.btn-file input[type=file] {
+ position: absolute;
+ top: 0;
+ right: 0;
+ min-width: 100%;
+ min-height: 100%;
+ font-size: 100px;
+ text-align: right;
+ filter: alpha(opacity=0);
+ opacity: 0;
+ cursor: inherit;
+ display: block;
+}
.pod {
padding: 10px;
border-radius: 10px;
@@ -11811,6 +11829,7 @@ body {
}
.console-os .container-main {
background-color: #f8f8f8;
+ padding-bottom: 80px;
-webkit-flex: 1;
-moz-flex: 1;
-ms-flex: 1;
@@ -11818,6 +11837,7 @@ body {
}
.console-os #content-wrap > .container {
margin-top: 35px;
+ margin-bottom: 80px;
}
.console-os #content-wrap > .container h1 {
margin-top: 10px;
@@ -11917,47 +11937,61 @@ body {
text-align: left;
font-weight: normal;
}
-.create-from-template .template-name {
+.create-from-template .template-name,
+.create-from-image .template-name {
text-align: right;
}
-.create-from-template .template-name span.fa {
+.create-from-template .template-name span.fa,
+.create-from-image .template-name span.fa {
font-size: 40px;
}
@media (min-width: 768px) {
- .create-from-template .template-name span.fa {
+ .create-from-template .template-name span.fa,
+ .create-from-image .template-name span.fa {
font-size: 100px;
}
}
-.create-from-template span.fa.visible-xs-inline {
+.create-from-template span.fa.visible-xs-inline,
+.create-from-image span.fa.visible-xs-inline {
margin-right: 10px;
}
-.flow {
+.create-from-template .flow,
+.create-from-image .flow {
+ border-top: 1px solid rgba(0, 0, 0, 0.15);
display: table;
+ margin-top: 60px;
width: 100%;
}
-.flow > .flow-block {
+.create-from-template .flow > .flow-block,
+.create-from-image .flow > .flow-block {
display: inline-block;
}
-.flow > .flow-block.right {
+.create-from-template .flow > .flow-block.right,
+.create-from-image .flow > .flow-block.right {
font-size: 11px;
font-weight: normal;
}
-.flow > .flow-block .action {
+.create-from-template .flow > .flow-block .action,
+.create-from-image .flow > .flow-block .action {
font-size: 11px;
font-weight: normal;
}
-.flow > .flow-block > ul.list-inline {
+.create-from-template .flow > .flow-block > ul.list-inline,
+.create-from-image .flow > .flow-block > ul.list-inline {
margin-bottom: 0;
}
-.flow > .flow-block > ul.list-inline > li {
+.create-from-template .flow > .flow-block > ul.list-inline > li,
+.create-from-image .flow > .flow-block > ul.list-inline > li {
font-size: 11px;
text-align: left;
}
@media (min-width: 767px) {
- .flow > .flow-block {
+ .create-from-template .flow > .flow-block,
+ .create-from-image .flow > .flow-block {
display: table-cell;
}
- .flow > .flow-block.right {
+ .create-from-template .flow > .flow-block.right,
+ .create-from-image .flow > .flow-block.right {
text-align: right;
}
}
@@ -11987,15 +12021,18 @@ body {
.modal.modal-create .modal-content .modal-header {
background-color: transparent;
}
-.modal.modal-create .modal-content .modal-body .template-icon {
+.modal.modal-create .modal-content .modal-body .template-icon,
+.modal.modal-create .modal-content .modal-body .image-icon {
text-align: center;
}
-.modal.modal-create .modal-content .modal-body .template-icon {
+.modal.modal-create .modal-content .modal-body .template-icon,
+.modal.modal-create .modal-content .modal-body .image-icon {
font-size: 80px;
line-height: 80px;
}
@media (min-width: 768px) {
- .modal.modal-create .modal-content .modal-body .template-icon {
+ .modal.modal-create .modal-content .modal-body .template-icon,
+ .modal.modal-create .modal-content .modal-body .image-icon {
font-size: 130px;
line-height: 130px;
}
@@ -12012,7 +12049,8 @@ body {
margin-left: 3px;
}
.action-inline {
- margin-left: 15px;
+ margin-left: 5px;
+ font-size: 11px;
}
.action-inline i.pficon,
.action-inline i.fa {
@@ -12032,13 +12070,22 @@ code {
white-space: normal;
}
.gutter-top-bottom {
- padding: 15px 0;
+ padding: 20px 0;
+}
+.gutter-top-bottom.gutter-top-bottom-2x {
+ padding: 40px 0;
}
.gutter-top {
- padding-top: 15px;
+ padding-top: 20px;
+}
+.gutter-top.gutter-top-2x {
+ padding-top: 40px;
}
.gutter-bottom {
- padding-bottom: 15px;
+ padding-bottom: 20px;
+}
+.gutter-bottom.gutter-bottom-2x {
+ padding-bottom: 40px;
}
select:invalid {
box-shadow: none;
@@ -12078,6 +12125,30 @@ select:invalid {
background-color: #f1f1f1;
color: #666;
}
+.input-number {
+ width: 60px;
+}
+.fade {
+ opacity: 0;
+ -webkit-transition: opacity 0.2s ease 0s;
+ transition: opacity 0.2s ease 0s;
+}
+.fade.in {
+ opacity: 1;
+}
+.collapse {
+ display: none;
+}
+.collapse.in {
+ display: block;
+}
+.collapsing {
+ position: relative;
+ height: 0;
+ overflow: hidden;
+ -webkit-transition: height 0.1s ease;
+ transition: height 0.1s ease;
+}
`)
func css_main_css() ([]byte, error) {
@@ -12870,10 +12941,16 @@ templateUrl:"views/images.html"
templateUrl:"views/pods.html"
}).when("/project/:project/browse/services", {
templateUrl:"views/services.html"
-}).when("/project/:project/catalog", {
-templateUrl:"views/catalog.html"
+}).when("/project/:project/catalog/templates", {
+templateUrl:"views/catalog/templates.html"
+}).when("/project/:project/catalog/images", {
+templateUrl:"views/catalog/images.html"
+}).when("/project/:project/create", {
+templateUrl:"views/create.html"
}).when("/project/:project/create/fromtemplate", {
templateUrl:"views/newfromtemplate.html"
+}).when("/project/:project/create/fromimage", {
+templateUrl:"views/create/fromimage.html"
}).when("/oauth", {
templateUrl:"views/util/oauth.html",
controller:"OAuthController"
@@ -13172,7 +13249,7 @@ this._listCallbacksMap = {}, this._watchCallbacksMap = {}, this._watchOperationM
var a = this;
c.$on("$routeChangeStart", function() {
a._watchWebsocketRetriesMap = {};
-});
+}), this.osApiVersion = "v1beta1", this.k8sApiVersion = "v1beta3";
}
h.prototype.by = function(a) {
if ("metadata.name" === a) return this._data;
@@ -13249,7 +13326,7 @@ h.push(a), j--, e();
});
}), f.promise;
}, i.prototype.get = function(b, e, g, h) {
-h = h || {};
+void 0 !== this._objectType(b) && (b = this._objectType(b)), h = h || {};
var i = !!h.force;
delete h.force;
var j = d.defer(), k = this._data(b, g);
@@ -13427,6 +13504,7 @@ buildConfigHooks:e.openshift,
deploymentConfigs:e.openshift,
images:e.openshift,
imageRepositories:e.openshift,
+imageRepositoryTags:e.openshift,
imageStreams:e.openshift,
imageStreamImages:e.openshift,
imageStreamTags:e.openshift,
@@ -13513,6 +13591,186 @@ namespace:a.metadata.name
});
}) :e.resolve(null), e.promise;
}, new i();
+} ]), angular.module("openshiftConsole").service("ApplicationGenerator", [ "DataService", function(a) {
+var b = a.osApiVersion, c = a.k8sApiVersion, d = {};
+return d._generateSecret = function() {
+function a() {
+return Math.floor(65536 * (1 + Math.random())).toString(16).substring(1);
+}
+return a() + a() + a() + a();
+}, d._getFirstPort = function(a) {
+var b = "None";
+return a.forEach(function(a) {
+"None" === b ? b = a :a.containerPort < b.containerPort && (b = a);
+}), b;
+}, d.generate = function(a) {
+var b = [];
+angular.forEach(a.image.dockerImageMetadata.ContainerConfig.ExposedPorts, function(c, d) {
+var e = d.split("/");
+1 === e.length && e.push("tcp"), b.push({
+containerPort:parseInt(e[0]),
+name:a.name + "-" + e[1] + "-" + e[0],
+protocol:e[1]
+});
+}), a.labels.name = a.name, a.labels.generatedby = "OpenShiftWebConsole";
+var c;
+null !== a.buildConfig.sourceUrl && (c = {
+name:a.name,
+tag:"latest",
+toString:function() {
+return this.name + ":" + this.tag;
+}
+});
+var e = {
+imageRepo:d._generateImageRepo(a),
+buildConfig:d._generateBuildConfig(a, c, a.labels),
+deploymentConfig:d._generateDeploymentConfig(a, c, b, a.labels),
+service:d._generateService(a, a.name, d._getFirstPort(b))
+};
+return e.route = d._generateRoute(a, a.name, e.service.metadata.name), e;
+}, d._generateRoute = function(a, c, d) {
+return a.routing ? {
+kind:"Route",
+apiVersion:b,
+metadata:{
+name:c,
+labels:a.labels
+},
+serviceName:d,
+tls:{
+termination:"unsecure"
+}
+} :null;
+}, d._generateDeploymentConfig = function(a, d, e, f) {
+var g = [];
+angular.forEach(a.deploymentConfig.envVars, function(a, b) {
+g.push({
+name:b,
+value:a
+});
+}), f = angular.copy(f), f.deploymentconfig = a.name;
+var h = {
+apiVersion:b,
+kind:"DeploymentConfig",
+metadata:{
+name:a.name,
+labels:f
+},
+template:{
+controllerTemplate:{
+podTemplate:{
+desiredState:{
+manifest:{
+containers:[ {
+image:d.toString(),
+name:a.name,
+ports:e,
+env:g
+} ],
+version:c
+}
+},
+labels:f
+},
+replicaSelector:{
+deploymentconfig:a.name
+},
+replicas:a.scaling.replicas
+},
+strategy:{
+type:"Recreate"
+}
+},
+triggers:[]
+};
+return a.deploymentConfig.deployOnNewImage && h.triggers.push({
+type:"ImageChange",
+imageChangeParams:{
+automatic:!0,
+containerNames:[ a.name ],
+from:{
+name:d.name
+},
+tag:d.tag
+}
+}), a.deploymentConfig.deployOnConfigChange && h.triggers.push({
+type:"ConfigChange"
+}), h;
+}, d._generateBuildConfig = function(a, c, e) {
+var f = a.imageRepo.status.dockerImageRepository + ":" + a.imageTag, g = [ {
+generic:{
+secret:d._generateSecret()
+},
+type:"generic"
+} ];
+return a.buildConfig.buildOnSourceChange && g.push({
+github:{
+secret:d._generateSecret()
+},
+type:"github"
+}), a.buildConfig.buildOnImageChange && g.push({
+imageChange:{
+image:f,
+from:{
+name:a.imageName
+},
+tag:a.imageTag
+},
+type:"imageChange"
+}), {
+apiVersion:b,
+kind:"BuildConfig",
+metadata:{
+name:a.name,
+labels:e
+},
+parameters:{
+output:{
+to:{
+name:c.name
+}
+},
+source:{
+git:{
+ref:"master",
+uri:a.buildConfig.sourceUrl
+},
+type:"Git"
+},
+strategy:{
+type:"STI",
+stiStrategy:{
+image:f
+}
+}
+},
+triggers:g
+};
+}, d._generateImageRepo = function(a) {
+return {
+apiVersion:b,
+kind:"ImageRepository",
+metadata:{
+name:a.name,
+labels:a.labels
+}
+};
+}, d._generateService = function(a, b, d) {
+var e = {
+kind:"Service",
+apiVersion:c,
+metadata:{
+name:b,
+labels:a.labels
+},
+spec:{
+selector:{
+deploymentconfig:a.name
+}
+}
+};
+return "None" === d ? e.spec.portalIP = "None" :(e.spec.port = d.containerPort, e.spec.containerPort = d.containerPort, e.spec.protocol = d.protocol), e;
+}, d;
} ]), angular.module("openshiftConsole").provider("RedirectLoginService", function() {
var a = "", b = "", c = "";
this.OAuthClientID = function(b) {
@@ -13589,6 +13847,29 @@ return f["delete"]("oAuthAccessTokens", e, {}, g);
}
};
} ];
+}), angular.module("openshiftConsole").service("Navigate", [ "$location", function(a) {
+return {
+toErrorPage:function(b, c) {
+var d = URI("error").query({
+error_description:b,
+error:c
+}).toString();
+a.url(d);
+},
+toProjectOverview:function(b) {
+a.path(this.projectOverviewURL(b));
+},
+projectOverviewURL:function(a) {
+return "project/" + encodeURIComponent(a) + "/overview";
+}
+};
+} ]), angular.module("openshiftConsole").service("NameGenerator", function() {
+return {
+suggestFromSourceUrl:function(a) {
+var b = a.substr(a.lastIndexOf("/") + 1, a.length), c = b.lastIndexOf(".");
+return -1 !== c && (b = b.substr(0, c)), b;
+}
+};
}), angular.module("openshiftConsole").factory("TaskList", [ "$interval", function(a) {
function b() {
this.tasks = [];
@@ -13939,38 +14220,120 @@ a.services = b.select(a.unfilteredServices), h();
}), a.$on("$destroy", function() {
b.unwatchAll(f);
});
+} ]), angular.module("openshiftConsole").controller("CreateFromImageController", [ "$scope", "Logger", "$q", "$routeParams", "DataService", "Navigate", "NameGenerator", "ApplicationGenerator", "TaskList", function(a, b, c, d, e, f, g, h, i) {
+function j(a) {
+d.imageName || f.toErrorPage("Cannot create from source: a base image was not specified"), d.imageTag || f.toErrorPage("Cannot create from source: a base image tag was not specified"), d.sourceURL || f.toErrorPage("Cannot create from source: source url was not specified"), a.emptyMessage = "Loading...", a.imageName = d.imageName, a.imageTag = d.imageTag, a.namespace = d.namespace, a.buildConfig = {
+sourceUrl:d.sourceURL,
+buildOnSourceChange:!0,
+buildOnImageChange:!0
+}, a.deploymentConfig = {
+deployOnNewImage:!0,
+deployOnConfigChange:!0,
+envVars:{}
+}, a.routing = !0, a.labels = {}, a.scaling = {
+replicas:1
+}, e.get("imageStreams", a.imageName, a, {
+namespace:a.namespace
+}).then(function(b) {
+a.imageRepo = b;
+var c = a.imageTag;
+e.get("imageStreamTags", b.metadata.name + ":" + c, {
+namespace:a.namespace
+}).then(function(b) {
+a.image = b, angular.forEach(b.dockerImageMetadata.ContainerConfig.Env, function(b) {
+var c = b.split("=");
+a.deploymentConfig.envVars[c[0]] = c[1];
+});
+}, function() {
+f.toErrorPage("Cannot create from source: the specified image could not be retrieved.");
+});
+}, function() {
+f.toErrorPage("Cannot create from source: the specified image could not be retrieved.");
+}), a.name = g.suggestFromSourceUrl(a.buildConfig.sourceUrl);
+}
+j(a);
+var k = function(a, b, d) {
+function f() {
+0 === j && (h.length > 0 ? g.reject(h) :g.resolve(a));
+}
+var g = c.defer(), h = [], i = [], j = a.length;
+return a.forEach(function(a) {
+e.get(a.kind, a.metadata.name, d, {
+namespace:b,
+errorNotification:!1
+}).then(function(a) {
+h.push(a), j--, f();
+}, function(a) {
+i.push(a), j--, f();
+});
+}), g.promise;
+}, l = function(b) {
+var d = {
+started:"Creating application " + a.name + " in project " + a.projectName,
+success:"Created application " + a.name + " in project " + a.projectName,
+failure:"Failed to create " + a.name + " in project " + a.projectName
+}, g = {};
+i.add(d, g, function() {
+var d = c.defer();
+return e.createList(b, a).then(function(b) {
+var c = [], e = !1;
+b.failure.length > 0 ? b.failure.forEach(function(a) {
+var b = "";
+b = a.data && a.data.details ? a.data.details.kind + " " + a.data.details.id :"object", c.push({
+type:"error",
+message:"Cannot create " + b + ". ",
+details:a.data.message
+}), e = !0;
+}) :c.push({
+type:"success",
+message:"All resource for application " + a.name + " were created successfully."
+}), d.resolve({
+alerts:c,
+hasErrors:e
+});
+}), d.promise;
+}, function(b) {
+a.alerts = [ {
+type:"error",
+message:"An error occurred creating the application.",
+details:"Status: " + b.status + ". " + b.data
+} ];
+}), f.toProjectOverview(a.projectName);
+}, m = function() {
+a.nameTaken = !0;
+};
+a.createApp = function() {
+var c = h.generate(a), d = [];
+angular.forEach(c, function(a) {
+null !== a && (b.debug("Generated resource definition:", a), d.push(a));
+}), k(d, a.namespace, a).then(l, m);
+};
} ]), angular.module("openshiftConsole").controller("NewFromTemplateController", [ "$scope", "$http", "$routeParams", "DataService", "$q", "$location", "TaskList", "$parse", function(a, b, c, d, e, f, g, h) {
function i(a) {
-var b = URI("error").query({
-error_description:a
-}).toString();
-f.url(b);
-}
-function j(a) {
-var b = [], c = m(a);
+var b = [], c = l(a);
return c && c.forEach(function(a) {
b.push(a.image);
}), b;
}
-function k(a) {
+function j(a) {
var b = [], c = [], d = {};
return a.items.forEach(function(a) {
if ("BuildConfig" === a.kind) {
-var e = n(a);
+var e = m(a);
e && b.push({
name:e
});
-var f = o(a);
+var f = n(a);
f && (d[f] = !0);
}
-"DeploymentConfig" === a.kind && (c = c.concat(j(a)));
+"DeploymentConfig" === a.kind && (c = c.concat(i(a)));
}), c.forEach(function(a) {
d[a] || b.push({
name:a
});
}), b;
}
-function l(a) {
+function k(a) {
var b = /^helplink\.(.*)\.title$/, c = /^helplink\.(.*)\.url$/, d = {};
for (var e in a.annotations) {
var f, g = e.match(b);
@@ -13978,7 +14341,7 @@ g ? (f = d[g[1]] || {}, f.title = a.annotations[e], d[g[1]] = f) :(g = e.match(c
}
return d;
}
-var m = h("template.controllerTemplate.podTemplate.desiredState.manifest.containers"), n = h("parameters.strategy.stiStrategy.image"), o = h("parameters.output.to.name || parameters.output.DockerImageReference");
+var l = h("template.controllerTemplate.podTemplate.desiredState.manifest.containers"), m = h("parameters.strategy.stiStrategy.image"), n = h("parameters.output.to.name || parameters.output.DockerImageReference");
a.projectDisplayName = function() {
return this.project && this.project.displayName || this.projectName;
}, a.templateDisplayName = function() {
@@ -13989,8 +14352,8 @@ var c = {
started:"Creating " + a.templateDisplayName() + " in project " + a.projectDisplayName(),
success:"Created " + a.templateDisplayName() + " in project " + a.projectDisplayName(),
failure:"Failed to create " + a.templateDisplayName() + " in project " + a.projectDisplayName()
-}, h = l(a.template);
-g.add(c, h, function() {
+}, f = k(a.template);
+g.add(c, f, function() {
var c = e.defer();
return d.createList(b.items, a).then(function(b) {
var d = [], e = !1;
@@ -14009,7 +14372,7 @@ alerts:d,
hasErrors:e
});
}), c.promise;
-}), f.path("project/" + a.projectName + "/overview");
+}), Navigate.toProjectOverview(a.projectName);
}, function(b) {
a.alerts = [ {
type:"error",
@@ -14020,16 +14383,16 @@ details:"Status: " + b.status + ". " + b.data
}, a.toggleOptionsExpanded = function() {
a.optionsExpanded = !a.optionsExpanded;
};
-var p = c.name, q = c.namespace;
-return p ? (a.emptyMessage = "Loading...", a.alerts = [], a.projectName = c.project, a.projectPromise = $.Deferred(), d.get("projects", a.projectName, a).then(function(b) {
+var o = c.name, p = c.namespace;
+return o ? (a.emptyMessage = "Loading...", a.alerts = [], a.projectName = c.project, a.projectPromise = $.Deferred(), d.get("projects", a.projectName, a).then(function(b) {
a.project = b, a.projectPromise.resolve(b);
-}), void d.get("templates", p, a, {
-namespace:q
+}), void d.get("templates", o, a, {
+namespace:p
}).then(function(b) {
-a.template = b, a.templateImages = k(b), a.hasParameters = a.template.parameters && a.template.parameters.length > 0, a.optionsExpanded = !1, a.templateUrl = b.metadata.selfLink, b.labels = b.labels || {};
+a.template = b, a.templateImages = j(b), a.hasParameters = a.template.parameters && a.template.parameters.length > 0, a.optionsExpanded = !1, a.templateUrl = b.metadata.selfLink, b.labels = b.labels || {};
}, function() {
-i("Cannot create from template: the specified template could not be retrieved.");
-})) :void i("Cannot create from template: a template name was not specified.");
+Navigate.toErrorPage("Cannot create from template: the specified template could not be retrieved.");
+})) :void Navigate.toErrorPage("Cannot create from template: a template name was not specified.");
} ]), angular.module("openshiftConsole").controller("LabelsController", [ "$scope", function(a) {
a.expanded = !0, a.toggleExpanded = function() {
a.expanded = !a.expanded;
@@ -14103,7 +14466,7 @@ b.debug("LogoutController"), c.isLoggedIn() ? (b.debug("LogoutController, logged
c.isLoggedIn() ? (b.debug("LogoutController, logout failed, still logged in"), a.logoutMessage = 'You could not be logged out. Return to the
console.') :d.logout_uri ? (b.debug("LogoutController, logout completed, redirecting to AUTH_CFG.logout_uri", d.logout_uri), window.location.href = d.logout_uri) :(b.debug("LogoutController, logout completed, reloading the page"), window.location.reload(!1));
})) :d.logout_uri ? (b.debug("LogoutController, logout completed, redirecting to AUTH_CFG.logout_uri", d.logout_uri), a.logoutMessage = "Logging out...", window.location.href = d.logout_uri) :(b.debug("LogoutController, not logged in, logout complete"), a.logoutMessage = 'You are logged out. Return to the
console.');
} ]), angular.module("openshiftConsole").controller("CatalogController", [ "$scope", "DataService", "$filter", "LabelFilter", "Logger", function(a, b, c, d, e) {
-a.projectTemplates = {}, a.openshiftTemplates = {}, a.templatesByTag = {}, a.templates = [], a.instantApps = [], b.list("templates", a, function(b) {
+a.projectTemplates = {}, a.openshiftTemplates = {}, a.templatesByTag = {}, a.templates = [], b.list("templates", a, function(b) {
a.projectTemplates = b.by("metadata.name"), f(), g(), e.log("project templates", a.projectTemplates);
}), b.list("templates", {
namespace:"openshift"
@@ -14126,6 +14489,64 @@ c = $.trim(c), a.templatesByTag[c] = a.templatesByTag[c] || [], a.templatesByTag
}
}), e.log("templatesByTag", a.templatesByTag);
};
+} ]), angular.module("openshiftConsole").controller("CatalogImagesController", [ "$scope", "DataService", "$filter", "LabelFilter", "imageEnvFilter", "$routeParams", "Logger", function(a, b, c, d, e, f, g) {
+a.projectImageRepos = {}, a.openshiftImageRepos = {}, a.builders = [], a.images = [], a.sourceURL = f.builderfor;
+var h = function(b) {
+angular.forEach(b, function(b) {
+b.status && (angular.forEach(b.status.tags, function(c) {
+var d = c.tag, e = {
+imageRepo:b,
+imageRepoTag:d,
+name:b.metadata.name + ":" + d
+};
+a.images.push(e);
+var f = [];
+b.spec.tags && angular.forEach(b.spec.tags, function(b) {
+b.annotations && b.annotations.tags && (f = b.annotations.tags.split(/\s*,\s*/)), f.indexOf("builder") >= 0 && a.builders.push(e);
+});
+}), g.info("builders", a.builders));
+});
+};
+b.list("imageStreams", a, function(b) {
+a.projectImageRepos = b.by("metadata.name"), h(a.projectImageRepos, a), g.info("project image repos", a.projectImageRepos);
+}), b.list("imageStreams", {
+namespace:"openshift"
+}, function(b) {
+a.openshiftImageRepos = b.by("metadata.name"), h(a.openshiftImageRepos, {
+namespace:"openshift"
+}), g.info("openshift image repos", a.openshiftImageRepos);
+});
+} ]), angular.module("openshiftConsole").controller("CreateController", [ "$scope", "DataService", "$filter", "LabelFilter", "$location", "Logger", function(a, b, c, d, e, f) {
+a.projectTemplates = {}, a.openshiftTemplates = {}, a.templatesByTag = {}, a.sourceURLPattern = /^(ftp|http|https|git):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/, b.list("templates", a, function(b) {
+a.projectTemplates = b.by("metadata.name"), g(), f.info("project templates", a.projectTemplates);
+}), b.list("templates", {
+namespace:"openshift"
+}, function(b) {
+a.openshiftTemplates = b.by("metadata.name"), g(), f.info("openshift templates", a.openshiftTemplates);
+});
+var g = function() {
+a.templatesByTag = {};
+var b = function(b) {
+if (b.metadata.annotations && b.metadata.annotations.tags) {
+var c = b.metadata.annotations.tags.split(",");
+angular.forEach(c, function(c) {
+c = $.trim(c), a.templatesByTag[c] = a.templatesByTag[c] || [], a.templatesByTag[c].push(b);
+});
+}
+};
+angular.forEach(a.projectTemplates, b), angular.forEach(a.openshiftTemplates, b), f.info("templatesByTag", a.templatesByTag);
+};
+a.createFromSource = function() {
+if (a.from_source_form.$valid) {
+var b = URI.expand("/project/{project}/catalog/images{?q*}", {
+project:a.projectName,
+q:{
+builderfor:a.from_source_url
+}
+});
+e.url(b.toString());
+}
+};
} ]), angular.module("openshiftConsole").directive("relativeTimestamp", function() {
return {
restrict:"E",
@@ -14142,6 +14563,160 @@ timestamp:"="
},
template:'
{{timestamp | duration}}'
};
+}), angular.module("openshiftConsole").directive("oscFileInput", [ "Logger", function(a) {
+return {
+restrict:"E",
+scope:{
+name:"@",
+model:"=",
+required:"="
+},
+templateUrl:"views/directives/osc-file-input.html",
+link:function(b, c) {
+b.supportsFileUpload = window.File && window.FileReader && window.FileList && window.Blob, b.uploadError = !1, $(c).change(function() {
+var c = $("input[type=file]", this)[0].files[0], d = new FileReader();
+d.onloadend = function() {
+b.$apply(function() {
+b.fileName = c.name, b.model = d.result;
+});
+}, d.onerror = function(c) {
+b.supportsFileUpload = !1, b.uploadError = !0, a.error(c);
+}, d.onerror();
+});
+}
+};
+} ]), angular.module("openshiftConsole").directive("oscFormSection", function() {
+return {
+restrict:"E",
+transclude:!0,
+scope:{
+header:"@",
+about:"@",
+aboutTitle:"@",
+editText:"@",
+expand:"@"
+},
+templateUrl:"views/directives/osc-form-section.html",
+link:function(a, b, c) {
+c.editText || (c.editText = "Edit"), a.expand = c.expand ? !0 :!1, a.toggle = function() {
+a.expand = !a.expand;
+};
+}
+};
+}), angular.module("openshiftConsole").directive("oscImageSummary", function() {
+return {
+restrict:"E",
+scope:{
+resource:"=",
+name:"="
+},
+templateUrl:"views/directives/osc-image-summary.html"
+};
+}), angular.module("openshiftConsole").controller("KeyValuesEntryController", [ "$scope", function(a) {
+a.editing = !1, a.edit = function() {
+a.originalValue = a.value, a.editing = !0;
+}, a.cancel = function() {
+a.value = a.originalValue, a.editing = !1;
+}, a.update = function(b, c, d) {
+c && (d[b] = c, a.editing = !1);
+};
+} ]).controller("KeyValuesController", [ "$scope", function(a) {
+var b = {};
+a.allowDelete = function(c) {
+return "never" === a.deletePolicy ? !1 :"added" === a.deletePolicy ? void 0 !== b[c] :!0;
+}, a.addEntry = function() {
+if (a.key && a.value) {
+var c = a.readonlyKeys.split(",");
+if (-1 !== c.indexOf(a.key)) return;
+b[a.key] = "", a.entries[a.key] = a.value, a.key = null, a.value = null, a.form.$setPristine(), a.form.$setUntouched(), a.form.$setValidity();
+}
+}, a.deleteEntry = function(c) {
+a.entries[c] && (delete a.entries[c], delete b[c]);
+};
+} ]).directive("oscInputValidator", function() {
+var a = {
+always:function() {
+return !0;
+},
+env:function(a, b) {
+var c = /^[A-Za-z_][A-Za-z0-9_]*$/i;
+return void 0 === a || null === a || 0 === a.trim().length ? !0 :c.test(b);
+},
+label:function(a, b) {
+function c(a) {
+return a.length > h ? !1 :g.test(a);
+}
+function d(a) {
+return a.length > f ? !1 :e.test(a);
+}
+var e = /^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$/, f = 63, g = /^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$/, h = 253;
+if (void 0 === a || null === a || 0 === a.trim().length) return !0;
+var i = b.split("/");
+switch (i.length) {
+case 1:
+return d(i[0]);
+
+case 2:
+return c(i[0]) && d(i[1]);
+}
+return !1;
+}
+};
+return {
+require:[ "ngModel", "^oscKeyValues" ],
+restrict:"A",
+link:function(b, c, d, e) {
+var f = e[0], g = e[1];
+"key" === d.oscInputValidator ? f.$validators.oscKeyValid = a[g.scope.keyValidator] :"value" === d.oscInputValidator && (f.$validators.oscValueValid = a[g.scope.valueValidator]);
+}
+};
+}).directive("oscKeyValues", function() {
+return {
+restrict:"E",
+scope:{
+keyTitle:"@",
+entries:"=",
+delimiter:"@",
+editable:"@",
+keyValidator:"@",
+valueValidator:"@",
+deletePolicy:"@",
+readonlyKeys:"@",
+keyValidationTooltip:"@",
+valueValidationTooltip:"@"
+},
+controller:[ "$scope", function(a) {
+this.scope = a;
+} ],
+templateUrl:"views/directives/osc-key-values.html",
+compile:function(a, b) {
+b.delimiter || (b.delimiter = ":"), b.keyTitle || (b.keyTitle = "Name"), b.editable = b.editable && "true" !== b.editable ? !1 :!0, b.keyValidator || (b.keyValidator = "always"), b.valueValidator || (b.valueValidator = "always"), -1 === [ "always", "added", "none" ].indexOf(b.deletePolicy) && (b.deletePolicy = "always"), b.readonlyKeys || (b.readonlyKeys = "");
+}
+};
+}), angular.module("openshiftConsole").directive("oscResourceNameValidator", function() {
+var a = 24, b = /^[a-z]([-a-z0-9]*[a-z0-9])?/i;
+return {
+require:"ngModel",
+link:function(c, d, e, f) {
+f.$validators.oscResourceNameValidator = function(c, d) {
+return f.$isEmpty(c) ? !1 :null === d ? !1 :f.$isEmpty(d.trim()) ? !1 :c.length <= a && b.test(d) && -1 === d.indexOf(" ") ? !0 :!1;
+};
+}
+};
+}), angular.module("openshiftConsole").directive("oscRouting", function() {
+return {
+require:"^form",
+restrict:"E",
+scope:{
+route:"=model",
+uriDisabled:"=",
+uriRequired:"="
+},
+templateUrl:"views/directives/osc-routing.html",
+link:function(a, b, c, d) {
+a.form = d;
+}
+};
}), angular.module("openshiftConsole").directive("podTemplate", function() {
return {
restrict:"E",
@@ -14308,36 +14883,10 @@ template:'
{{id.substring(0, 6)}}'
}), angular.module("openshiftConsole").directive("labels", function() {
return {
restrict:"E",
-templateUrl:"views/_labels.html",
scope:{
labels:"="
-}
-};
-}).directive("labelValidator", function() {
-return {
-restrict:"A",
-require:"ngModel",
-link:function(a, b, c, d) {
-d.$validators.label = function(a, b) {
-function c(a) {
-return a.length > i ? !1 :h.test(a);
-}
-function e(a) {
-return a.length > g ? !1 :f.test(a);
-}
-var f = /^[a-z0-9]([-a-z0-9]*[a-z0-9])?$/, g = 63, h = /^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$/, i = 253;
-if (d.$isEmpty(a)) return !0;
-var j = b.split("/");
-switch (j.length) {
-case 1:
-return e(j[0]);
-
-case 2:
-return c(j[0]) && e(j[1]);
-}
-return !1;
-};
-}
+},
+templateUrl:"views/directives/labels.html"
};
}), angular.module("openshiftConsole").directive("templateOptions", function() {
return {
@@ -14374,6 +14923,37 @@ a.url(c.toString());
});
}
};
+} ]).directive("catalogImage", [ "$location", "Logger", function(a, b) {
+return {
+restrict:"E",
+scope:{
+image:"=",
+imageRepo:"=",
+imageTag:"=",
+project:"=",
+sourceUrl:"="
+},
+templateUrl:"views/catalog/_image.html",
+link:function(c, d) {
+$(".select-image", d).click(function() {
+$(".modal", d).on("hidden.bs.modal", function() {
+c.$apply(function() {
+b.info(c);
+var d = URI.expand("/project/{project}/create/fromimage{?q*}", {
+project:c.project,
+q:{
+imageName:c.imageRepo.metadata.name,
+imageTag:c.imageTag,
+namespace:c.imageRepo.metadata.namespace,
+sourceURL:c.sourceUrl
+}
+});
+a.url(d.toString());
+});
+}).modal("hide");
+});
+}
+};
} ]), angular.module("openshiftConsole").filter("dateRelative", function() {
return function(a) {
return a ? moment(a).fromNow() :a;
@@ -14401,6 +14981,10 @@ return moment(a.metadata.creationTimestamp).diff(moment(b.metadata.creationTimes
};
}), angular.module("openshiftConsole").filter("annotation", function() {
return function(a, b) {
+if (a && a.spec && a.spec.tags && -1 !== b.indexOf(".")) for (var c = b.split("."), d = a.spec.tags, e = 0; e < d.length; ++e) {
+var f = d[e], g = c[0], h = c[1];
+if (g === f.name && f.annotations) return f.annotations[h];
+}
return a && a.metadata && a.metadata.annotations ? a.metadata.annotations[b] :null;
};
}).filter("description", [ "annotationFilter", function(a) {
@@ -14408,9 +14992,10 @@ return function(b) {
return a(b, "description");
};
} ]).filter("tags", [ "annotationFilter", function(a) {
-return function(b) {
-var c = a(b, "tags");
-return c ? c.split(/\s*,\s*/) :[];
+return function(b, c) {
+c = c || "tags";
+var d = a(b, c);
+return d ? d.split(/\s*,\s*/) :[];
};
} ]).filter("label", function() {
return function(a, b) {
@@ -14422,9 +15007,10 @@ var c = a(b, "icon");
return c ? c :"";
};
} ]).filter("iconClass", [ "annotationFilter", function(a) {
-return function(b, c) {
-var d = a(b, "iconClass");
-return d ? d :"template" === c ? "fa fa-bolt" :"";
+return function(b, c, d) {
+d = d || "iconClass";
+var e = a(b, d);
+return e ? e :"template" === c ? "fa fa-bolt" :"image" === c ? "fa fa-cube" :"";
};
} ]).filter("imageName", function() {
return function(a) {
@@ -14432,6 +15018,14 @@ if (!a) return "";
var b, c = a.split("/");
return 3 === c.length ? (b = c[2].split(":"), c[1] + "/" + b[0]) :2 === c.length ? a :1 === c.length ? (b = a.split(":"), b[0]) :void 0;
};
+}).filter("imageEnv", function() {
+return function(a, b) {
+for (var c = a.dockerImageMetadata.Config.Env, d = 0; d < c.length; d++) {
+var e = c[d].split("=");
+if (e[0] === b) return e[1];
+}
+return null;
+};
}).filter("buildForImage", function() {
return function(a, b) {
for (var c = a.dockerImageMetadata.Config.Env, d = 0; d < c.length; d++) {
@@ -14479,7 +15073,15 @@ c = "" == c ? c :c + "/";
var d = c + a.name;
return d += " [" + b + "]";
};
-}), angular.module("openshiftConsole").filter("hashSize", function() {
+}), angular.module("openshiftConsole").filter("underscore", function() {
+return function(a) {
+return a.replace(/\./g, "_");
+};
+}).filter("defaultIfBlank", function() {
+return function(a, b) {
+return null === a ? b :("string" != typeof a && (a = String(a)), 0 === a.trim().length ? b :a);
+};
+}).filter("hashSize", function() {
return function(a) {
return a ? Object.keys(a).length :0;
};
@@ -14575,6 +15177,28 @@ return "http://docs.openshift.org/latest/welcome/index.html";
return function(a) {
return "completed" !== a.status ? a.titles.started :a.hasErrors ? a.titles.failure :a.titles.success;
};
+}).filter("httpHttps", function() {
+return function(a) {
+return a ? "https://" :"http://";
+};
+}).filter("yesNo", function() {
+return function(a) {
+return a ? "Yes" :"No";
+};
+}).filter("valuesIn", function() {
+return function(a, b) {
+var c = b.split(","), d = {};
+return angular.forEach(a, function(a, b) {
+-1 !== c.indexOf(b) && (d[b] = a);
+}), d;
+};
+}).filter("valuesNotIn", function() {
+return function(a, b) {
+var c = b.split(","), d = {};
+return angular.forEach(a, function(a, b) {
+-1 === c.indexOf(b) && (d[b] = a);
+}), d;
+};
});`)
func scripts_scripts_js() ([]byte, error) {
@@ -57759,11 +58383,6 @@ select[multiple].input-lg,textarea.input-lg{height:auto}
.btn-block{display:block;width:100%;padding-left:0;padding-right:0}
.btn-block+.btn-block{margin-top:5px}
input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}
-.fade{opacity:0;-webkit-transition:opacity .15s linear;transition:opacity .15s linear}
-.fade.in{opacity:1}
-.collapse{display:none}
-.collapse.in{display:block}
-.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;transition:height .35s ease}
@font-face{font-family:'Glyphicons Halflings';src:url(../../components/bootstrap/dist/fonts/glyphicons-halflings-regular.eot);src:url(../../components/bootstrap/dist/fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../../components/bootstrap/dist/fonts/glyphicons-halflings-regular.woff) format('woff'),url(../../components/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../../components/bootstrap/dist/fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}
.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}
.glyphicon-asterisk:before{content:"\2a"}
@@ -59643,7 +60262,7 @@ to{transform:rotate(359deg)}}
.tile .tile-table+p{margin-top:3px;font-size:inherit}
.tile.tile-template a.label{font-size:11px}
.tile.tile-project h2{margin:10px 0}
-.tile.tile-status{background-color:#e6ecf1;border-top:5px solid #bfcedb}
+.tile.tile-status{background-color:#e6ecf1;border-top:5px solid #bfcedb;margin-top:40px}
.tile-click{cursor:pointer;position:relative}
.tile-click:hover{-webkit-box-shadow:0 1px 1px rgba(0,0,0,.2);box-shadow:0 1px 1px rgba(0,0,0,.2)}
.tile-click:hover .btn{color:#000!important}
@@ -59688,6 +60307,8 @@ ul.messenger-theme-flat .messenger-message.alert-warning:before{content:"\e60c";
ul.messenger-theme-flat .messenger-message.alert-warning .messenger-message-inner:before{color:#fff;content:"\e608";background-color:transparent}
ul.messenger-theme-flat .messenger-message.alert-success .messenger-message-inner:before{color:#5cb75c;content:"\e602";background-color:transparent}
ul.messenger-theme-flat .messenger-message.alert-info .messenger-message-inner:before{color:#27799c;content:"\e604";background-color:transparent}
+.btn-file{position:relative;overflow:hidden}
+.btn-file input[type=file]{position:absolute;top:0;right:0;min-width:100%;min-height:100%;font-size:100px;text-align:right;filter:alpha(opacity=0);opacity:0;cursor:inherit;display:block}
.pod{padding:10px;border-radius:10px;margin-bottom:5px;display:inline-block;background-color:rgba(204,204,204,.15);border:1px solid rgba(170,170,170,.15)}
.pod.pod-running{background-color:rgba(117,198,247,.15);border:1px solid rgba(66,147,196,.15)}
.pod+.pod{margin-left:5px}
@@ -59812,8 +60433,8 @@ td.visible-print,th.visible-print{display:table-cell!important}}
body,html{height:100%}
.console-os{background-color:#f8f8f8}
.console-os #content-wrap,.console-os #content-wrap .content{height:100%}
-.console-os .container-main{background-color:#f8f8f8;-webkit-flex:1;-moz-flex:1;-ms-flex:1;flex:1}
-.console-os #content-wrap>.container{margin-top:35px}
+.console-os .container-main{background-color:#f8f8f8;padding-bottom:80px;-webkit-flex:1;-moz-flex:1;-ms-flex:1;flex:1}
+.console-os #content-wrap>.container{margin-top:35px;margin-bottom:80px}
.console-os #content-wrap>.container h1{margin-top:10px}
.console-os .navbar{border:none;margin-bottom:0}
.console-os .navbar-pf{background:#34383c}
@@ -59837,43 +60458,52 @@ body,html{height:100%}
@media (min-width:1600px){.console-os .navbar-project .navbar-project-menu{width:380px}
.console-os .navbar-project .navbar-search{margin-left:420px}}
.dl-horizontal.left dt{text-align:left;font-weight:400}
-.create-from-template .template-name{text-align:right}
-.create-from-template .template-name span.fa{font-size:40px}
-@media (min-width:768px){.create-from-template .template-name span.fa{font-size:100px}}
-.create-from-template span.fa.visible-xs-inline{margin-right:10px}
-.flow{display:table;width:100%}
-.flow>.flow-block{display:inline-block}
-.flow>.flow-block .action,.flow>.flow-block.right{font-size:11px;font-weight:400}
-.flow>.flow-block>ul.list-inline{margin-bottom:0}
-.flow>.flow-block>ul.list-inline>li{font-size:11px;text-align:left}
-@media (min-width:767px){.flow>.flow-block{display:table-cell}
-.flow>.flow-block.right{text-align:right}}
+.create-from-image .template-name,.create-from-template .template-name{text-align:right}
+.create-from-image .template-name span.fa,.create-from-template .template-name span.fa{font-size:40px}
+@media (min-width:768px){.create-from-image .template-name span.fa,.create-from-template .template-name span.fa{font-size:100px}}
+.create-from-image span.fa.visible-xs-inline,.create-from-template span.fa.visible-xs-inline{margin-right:10px}
+.create-from-image .flow,.create-from-template .flow{border-top:1px solid rgba(0,0,0,.15);display:table;margin-top:60px;width:100%}
+.create-from-image .flow>.flow-block,.create-from-template .flow>.flow-block{display:inline-block}
+.create-from-image .flow>.flow-block .action,.create-from-image .flow>.flow-block.right,.create-from-template .flow>.flow-block .action,.create-from-template .flow>.flow-block.right{font-size:11px;font-weight:400}
+.create-from-image .flow>.flow-block>ul.list-inline,.create-from-template .flow>.flow-block>ul.list-inline{margin-bottom:0}
+.create-from-image .flow>.flow-block>ul.list-inline>li,.create-from-template .flow>.flow-block>ul.list-inline>li{font-size:11px;text-align:left}
+@media (min-width:767px){.create-from-image .flow>.flow-block,.create-from-template .flow>.flow-block{display:table-cell}
+.create-from-image .flow>.flow-block.right,.create-from-template .flow>.flow-block.right{text-align:right}}
.env-variable-list li:first-child,.label-list li:first-child{padding:6px 0 0}
.env-variable-list li .key,.env-variable-list li .value,.label-list li .key,.label-list li .value{display:inline-block;margin:0;width:44%}
.env-variable-list li .key,.label-list li .key{margin-left:2px}
.env-variable-list li .btn,.label-list li .btn{vertical-align:top}
.modal.modal-create .modal-content{padding:0}
.modal.modal-create .modal-content .modal-header{background-color:transparent}
-.modal.modal-create .modal-content .modal-body .template-icon{text-align:center;font-size:80px;line-height:80px}
-@media (min-width:768px){.modal.modal-create .modal-content .modal-body .template-icon{font-size:130px;line-height:130px}}
+.modal.modal-create .modal-content .modal-body .image-icon,.modal.modal-create .modal-content .modal-body .template-icon{text-align:center;font-size:80px;line-height:80px}
+@media (min-width:768px){.modal.modal-create .modal-content .modal-body .image-icon,.modal.modal-create .modal-content .modal-body .template-icon{font-size:130px;line-height:130px}}
.modal.modal-create .modal-content .modal-footer .btn-block{padding:10px}
@media (min-width:768px){.modal.modal-create .modal-content{padding:25px}}
.label+.label{margin-left:3px}
-.action-inline{margin-left:15px}
+.action-inline{margin-left:5px;font-size:11px}
.action-inline i.fa,.action-inline i.pficon{color:#4d5258;margin-right:5px}
.btn-group-xs>.btn,.btn-xs{padding:0 4px}
.btn-group-lg>.btn,.btn-lg{line-height:1.334}
code{white-space:normal}
-.gutter-top-bottom{padding:15px 0}
-.gutter-top{padding-top:15px}
-.gutter-bottom{padding-bottom:15px}
+.gutter-top-bottom{padding:20px 0}
+.gutter-top-bottom.gutter-top-bottom-2x{padding:40px 0}
+.gutter-top{padding-top:20px}
+.gutter-top.gutter-top-2x{padding-top:40px}
+.gutter-bottom{padding-bottom:20px}
+.gutter-bottom.gutter-bottom-2x{padding-bottom:40px}
select:invalid{box-shadow:none}
.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.well h1:first-child,.well h2:first-child,.well h3:first-child,.well h4:first-child,.well h5:first-child{margin-top:0}
.attention-message{background-color:#79cef2;border:1px solid #138cbf;position:absolute;top:20%;left:50%;transform:translate(-50%,-50%);padding:1em 1em 2em;min-width:85%}
.attention-message h1,.attention-message p{text-align:center}
.learn-more-block{display:block;font-size:11px;font-weight:400}
-.short-id{background-color:#f1f1f1;color:#666}`)
+.short-id{background-color:#f1f1f1;color:#666}
+.input-number{width:60px}
+.fade{opacity:0;-webkit-transition:opacity .2s ease 0s;transition:opacity .2s ease 0s}
+.fade.in{opacity:1}
+.collapse{display:none}
+.collapse.in{display:block}
+.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition:height .1s ease;transition:height .1s ease}`)
func styles_main_css() ([]byte, error) {
return _styles_main_css, nil
@@ -60070,71 +60700,6 @@ func views_deployment_config_metadata_html() ([]byte, error) {
return _views_deployment_config_metadata_html, nil
}
-var _views_labels_html = []byte(`
-
-
-
-
--
-template
-{{ labels.template }}
-
--
-{{ key }}
-{{ value }}
-
-
-
-
`)
-
-func views_labels_html() ([]byte, error) {
- return _views_labels_html, nil
-}
-
var _views_pod_template_html = []byte(`
@@ -60245,13 +60810,13 @@ var _views_project_nav_html = []byte(`
-
+
Create
-
+
Create
@@ -60375,7 +60940,7 @@ var _views_templateopt_html = []byte(`