Merge pull request #1800 from westlywright/2-oh

2 oh
This commit is contained in:
Vincent Fiduccia 2018-04-06 18:37:41 -07:00 committed by GitHub
commit b6069d80bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 804 additions and 390 deletions

View File

@ -0,0 +1,206 @@
import Controller from '@ember/controller';
import { get, computed } from '@ember/object';
import {
searchFields as containerSearchFields
} from 'shared/components/pod-dots/component';
const podsHeaders = [
{
name: 'expand',
sort: false,
searchField: null,
width: 30
},
{
name: 'state',
sort: ['sortState', 'displayName'],
searchField: 'displayState',
translationKey: 'generic.state',
width: 120
},
{
name: 'name',
sort: ['sortName', 'id'],
searchField: 'displayName',
translationKey: 'generic.name',
},
{
name: 'image',
sort: ['image', 'displayName'],
searchField: 'image',
translationKey: 'generic.image',
},
{
name: 'scale',
sort: ['scale:desc', 'isGlobalScale:desc', 'displayName'],
searchField: null,
translationKey: 'stacksPage.table.scale',
classNames: 'text-center',
width: 100
},
]
const ingressHeaders = [
{
name: 'state',
sort: ['sortState','displayName'],
searchField: 'displayState',
translationKey: 'generic.state',
width: 120
},
{
name: 'name',
sort: ['sortName','id'],
searchField: 'displayName',
translationKey: 'generic.name',
},
{
name: 'created',
sort: ['created','id'],
classNames: 'text-right pr-20',
searchField: 'created',
translationKey: 'generic.created',
},
]
const servicesHeaders = [
{
name: 'state',
sort: ['stack.isDefault:desc','stack.displayName','sortState','displayName'],
searchField: 'displayState',
translationKey: 'generic.state',
width: 120
},
{
name: 'name',
sort: ['stack.isDefault:desc','stack.displayName','displayName','id'],
searchField: 'displayName',
translationKey: 'generic.name',
},
{
name: 'displayType',
sort: ['displayType','displayName','id'],
searchField: 'displayType',
translationKey: 'generic.type',
width: 150,
},
{
name: 'target',
sort: false,
searchField: 'displayTargets',
translationKey: 'dnsPage.table.target',
},
]
const volumesHeaders = [
// {
// name: 'expand',
// sort: false,
// searchField: null,
// width: 30
// },
{
name: 'state',
sort: ['sortState','displayName'],
searchField: 'displayState',
translationKey: 'generic.state',
width: 120
},
{
name: 'name',
sort: ['displayName','id'],
searchField: 'displayName',
translationKey: 'volumesPage.claimName.label',
},
{
name: 'size',
sort: ['sizeBytes'],
search: ['sizeBytes','displaySize'],
translationKey: 'generic.size',
width: 120
},
{
name: 'volume',
sort: ['volume.displayName','displayName','id'],
translationKey: 'volumesPage.volume.label',
searchField: null,
},
{
name: 'storageClass',
sort: ['storageClass.displayName','displayName','id'],
translationKey: 'volumesPage.storageClass.label',
searchField: null,
},
]
const secretsHeaders = [
{
name: 'state',
sort: ['sortState','name','id'],
type: 'string',
searchField: 'displayState',
translationKey: 'generic.state',
width: 125,
},
{
name: 'name',
sort: ['name','id'],
translationKey: 'generic.name',
},
{
name: 'namespace',
translationKey: 'generic.namespace',
searchField: 'namespace.displayName',
sort: ['namespace.displayName','name','id'],
},
{
name: 'keys',
translationKey: 'secretsPage.table.keys',
searchField: 'keys',
sort: ['firstKey','name','id'],
},
{
name: 'created',
translationKey: 'generic.created',
sort: ['created:desc','name','id'],
searchField: false,
type: 'string',
width: 150,
},
]
export default Controller.extend({
// TODO =- expand logic?
expandedInstances: [],
ingressHeaders: ingressHeaders,
servicesHeaders: servicesHeaders,
volumesHeaders: volumesHeaders,
secretsHeaders: secretsHeaders,
ingressSearchText: '',
secretsSearchText: '',
podsHeaders: podsHeaders,
podsSearchText: '',
servicesSearchText: '',
volumesSearchText: '',
sortBy: 'name',
extraSearchFields: ['id:prefix', 'displayIp:ip'],
extraSearchSubFields: containerSearchFields,
actions: {
toggleExpand() {
// ???
},
},
stdOut: computed('model.app.stdOut', function() {
return get(this, 'model.app.status.stdOutput');
}),
stderr: computed('model.app.stdErr', function() {
return get(this, 'model.app.status.stdError');
}),
workloadsAndPods: computed('model.app.workloads', 'model.app.pods', function() {
let out = [];
out = this.get('model.app.pods').filter(obj => !obj.get('workloadId'));
out.pushObjects(this.get('model.app.workloads').slice());
return out;
}),
});

View File

@ -0,0 +1,16 @@
import { get } from '@ember/object';
import { inject as service } from '@ember/service';
import Route from '@ember/routing/route';
import { hash } from 'rsvp';
export default Route.extend({
catalog: service(),
store: service(),
model(params) {
const store = get(this, 'store');
return hash({
app: store.find('app', get(params, 'app_id')),
});
},
});

View File

@ -0,0 +1,243 @@
<section class="header clearfix">
<div class="pull-left">
<h1 class="vertical-middle">
{{t 'appDetailPage.header' appName=model.app.displayName}}
</h1>
</div>
<div class="vertical-middle"></div>
<div class="right-buttons">
{{badge-state model=model.app}}
{{action-menu model=model.app showPrimary=false classNames="ml-10 pull-right" size="sm"}}
</div>
</section>
<section>
{{#accordion-list as |al expandFn|}}
{{#accordion-list-item
title=(t 'appDetailPage.output.title')
detail=(t 'appDetailPage.output.detail')
expandAll=al.expandAll
expand=(action expandFn)
expandOnInit=true
}}
<pre class="log-body" style="margin:0; font-size: 80%; color: whitesmoke;">{{stdOut}}</pre>
{{/accordion-list-item}}
{{#accordion-list-item
title=(t 'appDetailPage.workloads.title')
detail=(t 'appDetailPage.workloads.detail')
expandAll=al.expandAll
expand=(action expandFn)
expandOnInit=true
}}
{{#sortable-table
tableClassNames="double-rows"
classNames="grid sortable-table"
body=workloadsAndPods
searchText=podsSearchText
sortBy=sortBy
bulkActions=true
subRows=true
pagingLabel="pagination.workload"
subSearchField="pods"
extraSearchFields=extraSearchFields
extraSearchSubFields=extraSearchSubFields
headers=podsHeaders as |sortable kind inst dt|}}
{{#if (eq kind "row")}}
{{#if (eq inst.type "pod")}}
{{pod-row
model=inst
dt=dt
showNode=true
expandPlaceholder=true
scalePlaceholder=true
fullColspan=sortable.fullColspan
toggle=(action "toggleExpand" inst.id)
expanded=(array-includes expandedInstances inst.id)
}}
{{else}}
{{workload-row
model=inst
toggle=(action "toggleExpand" inst.id)
expanded=(array-includes expandedInstances inst.id)
searchText=searchText
subMatches=sortable.subMatches
fullColspan=sortable.fullColspan
dt=dt
}}
{{/if}}
{{else if (eq kind "nomatch")}}
<tr><td colspan="{{sortable.fullColspan}}" class="text-center text-muted lacsso pt-20 pb-20">{{t 'containersPage.table.noMatch'}}</td></tr>
{{else if (eq kind "norows")}}
<tr><td colspan="{{sortable.fullColspan}}" class="text-center text-muted lacsso pt-20 pb-20">{{t 'appDetailPage.workloads.nodata'}}</td></tr>
{{/if}}
{{/sortable-table}}
{{!--
--}}
{{/accordion-list-item}}
{{#accordion-list-item
title=(t 'appDetailPage.ingress.title')
detail=(t 'appDetailPage.ingress.detail')
expandAll=al.expandAll
expand=(action expandFn)
expandOnInit=true
}}
<section class="pl-0 pr-0">
{{#sortable-table
classNames="grid sortable-table"
body=model.app.ingress
searchText=ingressSearchText
sortBy=sortBy
bulkActions=true
pagingLabel="pagination.ingress"
subSearchField="instances"
headers=ingressHeaders as |sortable kind inst dt|
}}
{{#if (eq kind "row")}}
<tr class="main-row">
<td class="row-check" valign="middle" style="padding-top: 2px;">
{{check-box nodeId=inst.id}}
</td>
<td data-title="{{dt.state}}" class="state">
{{badge-state model=inst}}
</td>
<td data-title="{{dt.name}}" class="clip">
<a href="{{href-to "ingress" inst.id}}">{{inst.displayName}}</a>
</td>
<td data-title="{{dt.created}}" class="text-right pr-20">
{{date-calendar inst.created}}
</td>
<td data-title="{{dt.actions}}" class="actions">
{{action-menu model=inst}}
</td>
</tr>
{{else if (eq kind "nomatch")}}
<tr><td colspan="{{sortable.fullColspan}}" class="text-center text-muted lacsso pt-20 pb-20">{{t 'ingressPage.table.noMatch'}}</td></tr>
{{else if (eq kind "norows")}}
<tr><td colspan="{{sortable.fullColspan}}" class="text-center text-muted lacsso pt-20 pb-20">{{t 'appDetailPage.ingress.nodata'}}</td></tr>
{{/if}}
{{/sortable-table}}
</section>
{{/accordion-list-item}}
{{#accordion-list-item
title=(t 'appDetailPage.services.title')
detail=(t 'appDetailPage.services.detail')
expandAll=al.expandAll
expand=(action expandFn)
expandOnInit=true
}}
{{#sortable-table
classNames="grid sortable-table"
body=model.app.services
searchText=servicesSearchText
sortBy=sortBy
bulkActions=true
pagingLabel="pagination.dnsRecord"
headers=servicesHeaders as |sortable kind inst dt|
}}
{{#if (eq kind "row")}}
{{dns-row
model=inst
searchText=searchText
fullColspan=sortable.fullColspan
dt=dt
}}
{{else if (eq kind "nomatch")}}
<tr><td colspan="{{sortable.fullColspan}}" class="text-center text-muted pt-20 pb-20">{{t 'dnsPage.noMatch'}}</td></tr>
{{else if (eq kind "norows")}}
<tr><td colspan="{{sortable.fullColspan}}" class="text-center text-muted lacsso pt-20 pb-20">{{t 'appDetailPage.services.nodata'}}</td></tr>
{{/if}}
{{/sortable-table}}
{{/accordion-list-item}}
{{#accordion-list-item
title=(t 'appDetailPage.volumes.title')
detail=(t 'appDetailPage.volumes.detail')
expandAll=al.expandAll
expand=(action expandFn)
expandOnInit=true
}}
<section class="pl-0 pr-0">
{{#sortable-table
classNames="grid sortable-table"
body=model.app.volumes
searchText=volumesSearchText
sortBy=sortBy
bulkActions=true
pagingLabel="pagination.volume"
headers=volumesHeaders as |sortable kind obj dt|
}}
{{#if (eq kind "row")}}
<tr class="main-row">
<td class="row-check" valign="middle" style="padding-top: 2px;">
{{check-box nodeId=obj.id}}
</td>
<td data-title="{{dt.state}}">
{{badge-state model=obj}}
</td>
<td data-title="{{dt.name}}">
<a href="{{href-to "volumes.detail" obj.id}}">{{obj.displayName}}</a>
</td>
<td data-title="{{dt.size}}">
{{obj.displaySize}}
</td>
<td data-title="{{dt.volume}}">
{{#if obj.persistentVolume}}
<a href="{{href-to "authenticated.cluster.storage.persistent-volumes.detail" scope.currentCluster.id obj.persistentVolume.id}}">
{{obj.persistentVolume.displayName}}
</a>
{{else}}
<span class="text-muted">&ndash;</span>
{{/if}}
</td>
<td data-title="{{dt.storageClass}}">
{{#if obj.storageClass}}
<a href="{{href-to "authenticated.cluster.storage.classes.detail" scope.currentCluster.id obj.storageClass.id}}">
{{obj.storageClass.displayName}}
</a>
{{else}}
<span class="text-muted">&ndash;</span>
{{/if}}
</td>
<td data-title="{{dt.actions}}" class="actions">
{{action-menu model=obj}}
</td>
</tr>
{{else if (eq kind "nomatch")}}
<tr><td colspan="{{sortable.fullColspan}}" class="text-center text-muted lacsso pt-20 pb-20">{{t 'volumesPage.noMatch'}}</td></tr>
{{else if (eq kind "norows")}}
<tr><td colspan="{{sortable.fullColspan}}" class="text-center text-muted lacsso pt-20 pb-20">{{t 'appDetailPage.volumes.nodata'}}</td></tr>
{{/if}}
{{/sortable-table}}
</section>
{{/accordion-list-item}}
{{#accordion-list-item
title=(t 'appDetailPage.secrets.title')
detail=(t 'appDetailPage.secrets.detail')
expandAll=al.expandAll
expand=(action expandFn)
expandOnInit=true
}}
{{#sortable-table
classNames="grid sortable-table"
body=model.app.secrets
sortBy=sortBy
bulkActions=true
searchText=secretsSearchText
headers=secretsHeaders as |sortable kind row dt|
}}
{{#if (eq kind "row")}}
{{secret-row model=row dt=dt}}
{{else if (eq kind "nomatch")}}
<tr><td colspan="{{sortable.fullColspan}}" class="text-center text-muted pt-20 pb-20">{{t 'secretsPage.index.noMatch'}}</td></tr>
{{else if (eq kind "norows")}}
<tr><td colspan="{{sortable.fullColspan}}" class="text-center text-muted lacsso pt-20 pb-20">{{t 'appDetailPage.secrets.nodata'}}</td></tr>
{{/if}}
{{/sortable-table}}
{{/accordion-list-item}}
{{/accordion-list}}
</section>

View File

@ -1,5 +1,5 @@
import Resource from 'ember-api-store/models/resource'; import Resource from 'ember-api-store/models/resource';
import { hasMany } from 'ember-api-store/utils/denormalize'; import { hasMany, reference } from 'ember-api-store/utils/denormalize';
import { computed, get } from '@ember/object'; import { computed, get } from '@ember/object';
import { parseHelmExternalId } from 'ui/utils/parse-externalid'; import { parseHelmExternalId } from 'ui/utils/parse-externalid';
import StateCounts from 'ui/mixins/state-counts'; import StateCounts from 'ui/mixins/state-counts';
@ -8,7 +8,20 @@ import { inject as service } from '@ember/service';
const App = Resource.extend(StateCounts, { const App = Resource.extend(StateCounts, {
catalog: service(), catalog: service(),
router: service(), router: service(),
// pods: hasMany('id', 'pod', 'appId'),
// services: hasMany('id', 'service', 'appId'),
// workloads: hasMany('id', 'workload', 'appId'),
// secrets: hasMany('id', 'secret', 'appId'),
// ingress: hasMany('id', 'ingress', 'appId'),
// volumes: hasMany('id', 'persistentVolumeClaim', 'appId'),
pods: hasMany('installNamespace', 'pod', 'namespaceId'), pods: hasMany('installNamespace', 'pod', 'namespaceId'),
services: hasMany('installNamespace', 'service', 'namespaceId'),
workloads: hasMany('installNamespace', 'workload', 'namespaceId'),
secrets: hasMany('installNamespace', 'secret', 'namespaceId'),
ingress: hasMany('installNamespace', 'ingress', 'namespaceId'),
volumes: hasMany('installNamespace', 'persistentVolumeClaim', 'namespaceId'),
namespace: reference('namespaceId', 'namespace', 'clusterStore'),
//workloads on pod
init() { init() {
this._super(...arguments); this._super(...arguments);

View File

@ -150,11 +150,13 @@ Router.map(function() {
// Catalog // Catalog
this.route('apps-tab', {path: '/apps', resetNamespace: true}, function() { this.route('apps-tab', {path: '/apps', resetNamespace: true}, function() {
this.route('index', {path: '/'}); this.route('index', {path: '/'});
this.route('detail', {path: '/:app_id'});
this.route('catalog-tab', {path: '/catalog', resetNamespace: true}, function() { this.route('catalog-tab', {path: '/catalog', resetNamespace: true}, function() {
this.route('index', {path: '/'}); this.route('index', {path: '/'});
this.route('launch', {path: '/:template'}); this.route('launch', {path: '/:template'});
}); });
}); });
// Resources // Resources

View File

@ -4,7 +4,7 @@ import Controller from '@ember/controller';
import Errors from 'ui/utils/errors'; import Errors from 'ui/utils/errors';
import C from 'ui/utils/constants'; import C from 'ui/utils/constants';
import { alias } from '@ember/object/computed'; import { alias } from '@ember/object/computed';
import { computed, observer } from '@ember/object'; import { get, set, computed, observer } from '@ember/object';
var PLAIN_PORT = 389; var PLAIN_PORT = 389;
var TLS_PORT = 636; var TLS_PORT = 636;
@ -31,44 +31,44 @@ export default Controller.extend({
init() { init() {
this._super(...arguments); this._super(...arguments);
if (this.get('adConfig')){ if (get(this, 'adConfig')){
this.tlsChanged(); this.tlsChanged();
} }
}, },
createDisabled: computed('username.length','password.length', function() { createDisabled: computed('username.length','password.length', function() {
return !this.get('username.length') || !this.get('password.length'); return !get(this, 'username.length') || !get(this, 'password.length');
}), }),
numUsers: computed('adConfig.allowedIdentities.@each.externalIdType','userType','groupType', function() { numUsers: computed('adConfig.allowedIdentities.@each.externalIdType','userType','groupType', function() {
return (this.get('adConfig.allowedIdentities')||[]).filterBy('externalIdType', this.get('userType')).get('length'); return ( get(this, 'adConfig.allowedPrincipalIds') || [] ).filter(principal => principal.includes(C.PROJECT.TYPE_ACTIVE_DIRECTORY_USER)).get('length');
}), }),
numGroups: computed('adConfig.allowedIdentities.@each.externalIdType','userType','groupType', function() { numGroups: computed('adConfig.allowedIdentities.@each.externalIdType','userType','groupType', function() {
return (this.get('adConfig.allowedIdentities')||[]).filterBy('externalIdType', this.get('groupType')).get('length'); return ( get(this, 'adConfig.allowedPrincipalIds') || [] ).filter(principal => principal.includes(C.PROJECT.TYPE_ACTIVE_DIRECTORY_GROUP)).get('length');
}), }),
configServers: computed('adConfig.servers', { configServers: computed('adConfig.servers', {
get() { get() {
return (this.get('adConfig.servers')||[]).join(','); return (get(this, 'adConfig.servers')||[]).join(',');
}, },
set(key, value) { set(key, value) {
this.set('adConfig.servers', value.split(',')); set(this, 'adConfig.servers', value.split(','));
return value; return value;
} }
}), }),
tlsChanged: observer('adConfig.tls', function() { tlsChanged: observer('adConfig.tls', function() {
var on = (this.get('adConfig.tls')||false); var on = (get(this, 'adConfig.tls')||false);
var port = parseInt(this.get('adConfig.port'),10); var port = parseInt(get(this, 'adConfig.port'),10);
if ( on && port === PLAIN_PORT ) if ( on && port === PLAIN_PORT )
{ {
this.set('adConfig.port', TLS_PORT); set(this, 'adConfig.port', TLS_PORT);
} }
else if ( !on /* && port === TLS_PORT */ ) // TODO 2.0 else if ( !on /* && port === TLS_PORT */ ) // TODO 2.0
{ {
this.set('adConfig.port', PLAIN_PORT); set(this, 'adConfig.port', PLAIN_PORT);
this.set('adConfig.tls', false); set(this, 'adConfig.tls', false);
} }
}), }),
@ -76,7 +76,7 @@ export default Controller.extend({
test: function() { test: function() {
this.send('clearError'); this.send('clearError');
var model = this.get('adConfig'); var model = get(this, 'adConfig');
model.setProperties({ model.setProperties({
accessMode: 'unrestricted', accessMode: 'unrestricted',
}); });
@ -84,16 +84,16 @@ export default Controller.extend({
var errors = model.validationErrors(); var errors = model.validationErrors();
if ( errors.get('length') ) if ( errors.get('length') )
{ {
this.set('errors', errors); set(this, 'errors', errors);
} }
else else
{ {
this.set('testing', true); set(this, 'testing', true);
model.doAction('testAndApply', { model.doAction('testAndApply', {
activeDirectoryConfig: model, activeDirectoryConfig: model,
enabled: true, enabled: true,
username: this.get('username'), username: get(this, 'username'),
password: this.get('password'), password: get(this, 'password'),
}).then( () => { }).then( () => {
this.send('waitAndRefresh'); this.send('waitAndRefresh');
}).catch((err) => { }).catch((err) => {
@ -111,25 +111,25 @@ export default Controller.extend({
}, },
promptDisable: function() { promptDisable: function() {
this.set('confirmDisable', true); set(this, 'confirmDisable', true);
later(this, function() { later(this, function() {
this.set('confirmDisable', false); set(this, 'confirmDisable', false);
}, 10000); }, 10000);
}, },
gotError: function(err) { gotError: function(err) {
this.set('errors', [Errors.stringify(err)]); set(this, 'errors', [Errors.stringify(err)]);
this.set('testing', false); set(this, 'testing', false);
}, },
clearError: function() { clearError: function() {
this.set('errors', null); set(this, 'errors', null);
}, },
disable: function() { disable: function() {
this.send('clearError'); this.send('clearError');
var model = this.get('adConfig'); var model = get(this, 'adConfig');
model.setProperties({ model.setProperties({
enabled: false, enabled: false,
}); });
@ -140,7 +140,7 @@ export default Controller.extend({
}).catch((err) => { }).catch((err) => {
this.send('gotError', err); this.send('gotError', err);
}).finally(() => { }).finally(() => {
this.set('confirmDisable', false); set(this, 'confirmDisable', false);
}); });
}, },
}, },

View File

@ -4,6 +4,7 @@ import { once, later } from '@ember/runloop';
import { alias } from '@ember/object/computed'; import { alias } from '@ember/object/computed';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import Controller from '@ember/controller'; import Controller from '@ember/controller';
import C from 'ui/utils/constants';
export default Controller.extend({ export default Controller.extend({
github: service(), github: service(),
@ -52,13 +53,12 @@ export default Controller.extend({
}), }),
numUsers: computed('githubConfig.allowedPrincipals.@each.externalIdType','wasRestricted', function() { numUsers: computed('githubConfig.allowedPrincipals.@each.externalIdType','wasRestricted', function() {
return 3; //TODO return ( get(this, 'githubConfig.allowedPrincipalIds') || []).filter(principal => principal.includes(C.PROJECT.TYPE_GITHUB_USER)).get('length');
// return get(this, 'githubConfig.principals').filterBy('externalIdType',C.PROJECT.TYPE_GITHUB_USER).get('length');
}), }),
numOrgs: computed('githubConfig.allowedPrincipals.@each.externalIdType','wasRestricted',function() { numOrgs: computed('githubConfig.allowedPrincipals.@each.externalIdType','wasRestricted',function() {
return 4; //TODO
// return get(this, 'githubConfig.principals').filterBy('externalIdType',C.PROJECT.TYPE_GITHUB_ORG).get('length'); return ( get(this, 'githubConfig.allowedPrincipalIds') || []).filter(principal => principal.includes(C.PROJECT.TYPE_GITHUB_ORG)).get('length');
}), }),
destinationUrl: computed(function() { destinationUrl: computed(function() {

View File

@ -46,7 +46,7 @@
<div class="row"> <div class="row">
<div class="col span-4"> <div class="col span-4">
<h3>{{t 'ldap.accessEnabled.general.header'}}</h3> <h3>{{t 'ldap.accessEnabled.general.header'}}</h3>
<div><b>{{t 'ldap.accessEnabled.general.server'}} </b> <span class="text-muted">{{adConfig.server}}:{{adConfig.port}}</span></div> <div><b>{{t 'ldap.accessEnabled.general.server'}} </b> <span class="text-muted">{{adConfig.servers.firstObject}}:{{adConfig.port}}</span></div>
<div><b>{{t 'ldap.accessEnabled.general.tls'}} </b> <span class="text-muted">{{if adConfig.tls "Yes" "No"}}</span></div> <div><b>{{t 'ldap.accessEnabled.general.tls'}} </b> <span class="text-muted">{{if adConfig.tls "Yes" "No"}}</span></div>
<div><b>{{t 'ldap.accessEnabled.general.serviceAccount'}} </b> <span class="text-muted">{{adConfig.serviceAccountUsername}}</span></div> <div><b>{{t 'ldap.accessEnabled.general.serviceAccount'}} </b> <span class="text-muted">{{adConfig.serviceAccountUsername}}</span></div>
{{#unless isOpenLdap}} {{#unless isOpenLdap}}

View File

@ -1,110 +0,0 @@
import { get, set, computed } from '@ember/object';
import { next } from '@ember/runloop';
import { inject as service } from '@ember/service';
import Component from '@ember/component';
import C from 'ui/utils/constants';
export default Component.extend({
access: service(),
cookies: service(),
isCaas: computed('app.mode', function() {
return this.get('app.mode') === 'caas' ? true : false;
}),
waiting: null,
username: null,
rememberUsername: false,
password: null,
shown: false,
provider: null,
readableProvider: null,
actions: {
showLocal() {
this.toggleProperty('shown');
next(this, 'focusSomething');
},
authenticate: function() {
const username = get(this, 'username');
let password = get(this, 'password');
const remember = get(this, 'rememberUsername');
if (password && get(this, 'provider') === 'local') {
password = password.trim();
}
const code = {
username: username,
password: password,
};
if ( remember ) {
if (get(this, 'provider') === 'local') {
get(this, 'cookies').setWithOptions(C.COOKIE.USERNAME, username, {expire: 365, secure: 'auto'});
} else {
get(this, 'cookies').setWithOptions(`${get(this, 'provider').toUpperCase()}-USERNAME`, username, {expire: 365, secure: 'auto'});
}
} else {
get(this, 'cookies').remove(C.COOKIE.USERNAME);
}
set(this, 'password', '');
if ( get(this,'access.providers') ) {
this.sendAction('action', get(this, 'provider'), code);
}
}
},
init() {
this._super(...arguments);
var username = null;
if (get(this, 'provider') === 'local') {
username = get(this, `cookies.${C.COOKIE.USERNAME}`);
} else {
username = get(this, `cookies.${get(this, 'provider').toUpperCase()}-USERNAME`);
}
if ( username ) {
set(this, 'username', username);
set(this, 'rememberUsername', true);
}
if (get(this, 'provider')) {
let pv = null;
switch(get(this, 'provider')) {
case 'activedirectory':
pv = 'Active Directory';
break;
case 'local':
default:
pv = 'Local Auth';
break;
}
set(this, 'readableProvider', pv);
// console.log(this.get('provider'));
}
},
focusSomething() {
if ( this.isDestroyed || this.isDestroying ) {
return;
}
let elem = this.$('#login-username-ad');
if ( get(this, 'username') ) {
elem = this.$('#login-password-ad');
}
if ( elem && elem[0] ) {
elem[0].focus();
}
},
didInsertElement() {
next(this, 'focusSomething');
},
});

View File

@ -1,42 +0,0 @@
{{#if shown}}
<form {{action "authenticate" on="submit"}} class="text-left mt-10">
<div class="mt-20">
<div>
{{#if isCaas}}
<label class="acc-label">{{t 'loginUserPass.caasLabel'}}</label>
{{else}}
<label class="acc-label">{{t 'loginUserPass.userLabel'}}</label>
{{/if}}
<div class="pull-right">
<label class="acc-label">{{t 'loginUserPass.remember'}} {{input type="checkbox" checked=rememberUsername}}</label>
</div>
</div>
<div>
{{input type="text" id="login-username-ad" autocomplete="username" class="form-control login-user" value=username placeholder=(t 'loginUserPass.userPlaceholder')}}
</div>
</div>
<div class="pt-15 pb-15">
<label class="acc-label">{{t 'loginUserPass.passwordLabel'}}</label>
<div>
{{input id="login-password-ad" autocomplete="password" type="password" class="form-control login-pass" value=password}}
</div>
</div>
<p class="text-center">
<button disabled={{waiting}} class="btn bg-primary" {{action "authenticate"}}>
{{#if waiting}}
<i class="icon icon-spinner icon-spin"></i> {{t 'loginUserPass.loggingInLabel'}}
{{else}}
{{t 'loginUserPass.loginLabel' provider=readableProvider}}
{{/if}}
</button>
</p>
</form>
{{else}}
{{#if (eq provider 'local')}}
<a class="link-text text-small mt-10 hand" {{action 'showLocal'}}>{{t 'loginUserPass.local'}}</a>
{{else}}
<a class="btn bg-primary" {{action 'showLocal'}}> <i class="icon icon-key"></i> {{t 'loginUserPass.provider' kind=readableProvider}}</a>
{{/if}}
{{/if}}

View File

@ -108,4 +108,9 @@ export default Component.extend({
didInsertElement() { didInsertElement() {
next(this, 'focusSomething'); next(this, 'focusSomething');
}, },
willDestroyElement() {
set(this, 'shown', false);
},
}); });

View File

@ -12,14 +12,14 @@
</div> </div>
</div> </div>
<div> <div>
{{input type="text" id="login-username" autocomplete="username" class="form-control login-user" value=username placeholder=(t 'loginUserPass.userPlaceholder')}} {{input type="text" id=(concat "login-username-" provider) autocomplete="username" class="form-control login-user" value=username placeholder=(t 'loginUserPass.userPlaceholder')}}
</div> </div>
</div> </div>
<div class="pt-15 pb-15"> <div class="pt-15 pb-15">
<label class="acc-label">{{t 'loginUserPass.passwordLabel'}}</label> <label class="acc-label">{{t 'loginUserPass.passwordLabel'}}</label>
<div> <div>
{{input id="login-password" autocomplete="password" type="password" class="form-control login-pass" value=password}} {{input id=(concat "login-password-" provider) autocomplete="password" type="password" class="form-control login-pass" value=password}}
</div> </div>
</div> </div>

View File

@ -24,6 +24,9 @@ export default Controller.extend({
isForbidden: equal('errorCode', '403'), isForbidden: equal('errorCode', '403'),
waiting: false, waiting: false,
adWaiting: false,
localWaiting: false,
shibbolethWaiting: false,
errorMsg: null, errorMsg: null,
errorCode: null, errorCode: null,
resetPassword: false, resetPassword: false,
@ -76,7 +79,6 @@ export default Controller.extend({
}, },
actions: { actions: {
started() { started() {
setProperties(this, { setProperties(this, {
'waiting': true, 'waiting': true,
@ -84,6 +86,27 @@ export default Controller.extend({
}); });
}, },
waiting(provider) {
// setProperties(this, {
// 'waiting': true,
// 'errorMsg': null,
// });
set(this, 'errorMsg', null);
switch (provider) {
case 'local':
this.toggleProperty('localWaiting');
break;
case 'activedirectory':
this.toggleProperty('adWaiting');
break;
case 'shibboleth':
this.toggleProperty('shibbolethWaiting');
break;
default:
break;
}
},
complete(success) { complete(success) {
if (success) { if (success) {
this.shouldSetServerUrl().then((proceed) => { this.shouldSetServerUrl().then((proceed) => {
@ -102,7 +125,7 @@ export default Controller.extend({
}, },
authenticate(provider, code) { authenticate(provider, code) {
this.send('started'); this.send('waiting', provider);
later(() => { later(() => {
get(this, 'access').login(provider, code).then((user) => { get(this, 'access').login(provider, code).then((user) => {
@ -118,9 +141,10 @@ export default Controller.extend({
get(this, 'access').set('userCode', null); get(this, 'access').set('userCode', null);
get(this, 'access').set('firstLogin', false); get(this, 'access').set('firstLogin', false);
this.send('complete', true); this.send('complete', true);
this.send('waiting', provider);
} }
}).catch((err) => { }).catch((err) => {
set(this, 'waiting', false); this.send('waiting', provider);
if ( err && err.status === 401 ) { if ( err && err.status === 401 ) {
let key = 'loginPage.error.authFailed' let key = 'loginPage.error.authFailed'

View File

@ -54,8 +54,13 @@ export default Route.extend({
resetController(controller, isExisting /*, transition*/ ) { resetController(controller, isExisting /*, transition*/ ) {
if (isExisting) { if (isExisting) {
controller.set('changePassword', false); controller.setProperties({
controller.set('waiting',false); changePassword: false,
waiting: false,
adWaiting: false,
shibbolethWaiting: false,
localWaiting: false,
})
} }
} }
}); });

View File

@ -16,7 +16,7 @@
<br/> <br/>
{{login-shibboleth {{login-shibboleth
action="started" action="started"
waiting=waiting waiting=shibbolethWaiting
}} }}
{{/if}} {{/if}}
@ -25,23 +25,23 @@
{{/if}} {{/if}}
{{#if isActiveDirectory}} {{#if isActiveDirectory}}
{{login-ad {{login-user-pass
classNames="row"
action="authenticate" action="authenticate"
waiting=waiting classNames="row"
shown=true
provider="activedirectory" provider="activedirectory"
shown=true
waiting=adWaiting
}} }}
{{/if}} {{/if}}
{{#if isLocal}} {{#if isLocal}}
{{login-user-pass {{login-user-pass
classNames="row"
action="authenticate" action="authenticate"
waiting=waiting classNames="row"
shown=onlyLocal
onlyLocal=onlyLocal onlyLocal=onlyLocal
provider="local" provider="local"
shown=onlyLocal
waiting=localWaiting
}} }}
{{/if}} {{/if}}

View File

@ -5,7 +5,7 @@ import C from 'shared/utils/constants';
import EmberObject from '@ember/object' import EmberObject from '@ember/object'
import { get } from '@ember/object'; import { get } from '@ember/object';
import { /* parseExternalId, */ parseHelmExternalId } from 'ui/utils/parse-externalid'; import { /* parseExternalId, */ parseHelmExternalId } from 'ui/utils/parse-externalid';
import { all } from 'rsvp'; import { all, allSettled } from 'rsvp';
const RANCHER_VERSION = 'rancherVersion'; const RANCHER_VERSION = 'rancherVersion';
const DEFAULT_BASE = 'kubernetes'; const DEFAULT_BASE = 'kubernetes';
@ -47,7 +47,7 @@ export default Service.extend({
deps.push(this.fetchTemplate(extInfo.templateId, false)); deps.push(this.fetchTemplate(extInfo.templateId, false));
}); });
return all(deps); return allSettled(deps);
}, },
fetchCatalogs(opts) { fetchCatalogs(opts) {

View File

@ -9,6 +9,7 @@ export default Component.extend({
questions: alias('selectedTemplate.questions'), questions: alias('selectedTemplate.questions'),
pasteOrUpload: false, pasteOrUpload: false,
accept : '.yml, .yaml', accept : '.yml, .yaml',
showHeader: true,
_boundChange : null, _boundChange : null,
didInsertElement() { didInsertElement() {
this.set('_boundChange', (event) => { this.change(event); }); this.set('_boundChange', (event) => { this.change(event); });

View File

@ -3,8 +3,13 @@
<button class="btn btn-sm bg-primary" {{action 'showPaste'}}>{{t 'generic.paste'}} <span class="icon icon-copy"></span></button> <button class="btn btn-sm bg-primary" {{action 'showPaste'}}>{{t 'generic.paste'}} <span class="icon icon-copy"></span></button>
<button class="btn btn-sm bg-primary" {{action 'upload'}}>{{t 'generic.upload'}} <span class="icon icon-upload"></span></button> <button class="btn btn-sm bg-primary" {{action 'upload'}}>{{t 'generic.upload'}} <span class="icon icon-upload"></span></button>
</div> </div>
<div>
{{#if showHeader}}
<h4 class="mb-0">{{t 'inputAnswers.config'}}</h4> <h4 class="mb-0">{{t 'inputAnswers.config'}}</h4>
<span class="protip">{{t 'inputAnswers.protip'}}</span> <span class="protip">{{t 'inputAnswers.protip'}}</span>
{{/if}}
&nbsp;
</div>
<div class="mt-20"> <div class="mt-20">
{{#if pasteOrUpload}} {{#if pasteOrUpload}}
{{textarea-autogrow {{textarea-autogrow

View File

@ -5,9 +5,19 @@
<div class="stack-info-row box"> <div class="stack-info-row box">
<div class="stack-info-top row"> <div class="stack-info-top row">
<div class="col"> <div class="col">
{{#link-to "apps-tab.detail" model.id}}
{{model.displayName}} {{model.displayName}}
{{/link-to}}
</div> </div>
<div class="col text-right pull-right"> <div class="col text-right pull-right">
{{#upgrade-btn model=model classNames="btn-xs " as |btn|}}
{{#if btn.model.externalIdInfo.version}}
{{t (concat 'upgradeBtn.status.' btn.upgradeStatus)}}
<span class="text-small">({{btn.latestVersion}})</span>
{{else}}
{{t (concat 'upgradeBtn.status.' btn.upgradeStatus)}}
{{/if}}
{{/upgrade-btn}}
{{badge-state model=model classNames="btn-xs"}} {{badge-state model=model classNames="btn-xs"}}
{{action-menu model=model classNames="inline-block"}} {{action-menu model=model classNames="inline-block"}}
</div> </div>
@ -25,17 +35,15 @@
{{model.pods.length}} {{model.pods.length}}
</small> </small>
</div> </div>
{{#upgrade-btn model=model classNames="btn-sm" as |btn|}} <div class="col">
{{model.externalIdInfo.version}} {{#if model.workloads}}
{{#if btn.model.externalIdInfo.version}} <div class="clip text-small">
{{t (concat 'upgradeBtn.status.' btn.upgradeStatus)}} {{#each model.workloads as |workload|}}
<span class="text-small">({{btn.latestVersion}})</span> {{workload.displayEndpoints}}
{{else}} {{/each}}
{{t (concat 'upgradeBtn.status.' btn.upgradeStatus)}} </div>
{{/if}} {{/if}}
{{/upgrade-btn}} </div>
</div> </div>
</div> </div>
</div> </div>
{{#if expanded}}
{{/if}}

View File

@ -121,7 +121,7 @@ export default Component.extend(NewOrEdit, {
value: file.name value: file.name
} }
}); });
files.addObject({label: 'answers', value: 'answers'}); files.addObject({label: 'answers.yaml', value: 'answers'});
return files.sortBy('label'); return files.sortBy('label');
}), }),

View File

@ -8,42 +8,20 @@
</section> </section>
{{/if}} {{/if}}
<section class="{{sectionClass}} row"> <div class="accordion"> {{!-- container-section --}}
<div class="col span-3"> <div class="accordion-header"> {{!-- header-section --}}
<div class="title">
<span class="m-0 ">{{t 'newCatalog.appInfo'}}</span>
</div>
</div>
<div class="accordion-content row"> {{!-- content-section --}}
<div class="col span-12 text-center">
{{#if templateResource.links.icon}} {{#if templateResource.links.icon}}
<img src={{templateResource.links.icon}} alt={{templateResource.name}} class="mr-20" style="height:75px;max-width: 100%;"> <img src={{templateResource.links.icon}} alt={{templateResource.name}} class="mr-20" style="height:75px;max-width: 100%;">
{{/if}} {{/if}}
<div>
<small><strong>{{t 'newCatalog.catalog'}}</strong> <span class="text-capitalize">{{templateResource.catalogId}}</span></small>
</div>
<div>
<small><strong>{{t 'newCatalog.kind'}}</strong> <span class="text-capitalize">{{templateKind}}</span></small>
</div>
<div>
<small><strong>{{t 'newCatalog.category'}}</strong> {{join-array templateResource.categoryArray}}</small>
</div>
{{#if (eq templateResource.certifiedType 'rancher')}}
<small><strong>{{t 'newCatalog.support'}} </strong>{{t 'newCatalog.official'}}</small>
{{else}}
<small><strong>{{t 'newCatalog.support'}} </strong>{{t 'newCatalog.maintainedBy'}}</small>
{{/if}}
{{#if templateResource.maintainer}}
<div>
<small><strong>{{t 'newCatalog.maintainer'}}</strong> {{templateResource.maintainer}}</small>
</div>
{{/if}}
{{#if templateResource.license}}
<div>
<small><strong>{{t 'newCatalog.license'}}</strong> {{templateResource.license}}</small>
</div>
{{/if}}
{{#if templateResource.links.project}}
<small class="force-wrap"><strong>{{t 'newCatalog.url'}}</strong> <a href="{{templateResource.cleanProjectUrl}}" target="_blank">{{templateResource.cleanProjectUrl}}</a></small>
{{/if}}
</div> </div>
{{#if readmeContent}} {{#if readmeContent}}
<div class="col span-9" style="overflow-y: auto; max-height: 500px;"> <div class="col span-12" style="overflow-y: auto; max-height: 500px;">
{{marked-down markdown=readmeContent}} {{marked-down markdown=readmeContent}}
</div> </div>
{{else}} {{else}}
@ -52,34 +30,30 @@
</h2> </h2>
<p>{{templateResource.description}}</p> <p>{{templateResource.description}}</p>
{{/if}} {{/if}}
</section> </div>
<section class="{{sectionClass}}"> </div>
<h4>{{t 'newCatalog.newApp'}}</h4>
<div class="accordion">
<div class="accordion-header">
<div class="title">
<span class="m-0 ">{{t 'newCatalog.newApp'}}</span>
</div>
</div>
<div class="accordion-content row">
<div class="row">
<div class="col span-6">
{{form-name-description {{form-name-description
model=catalogApp model=catalogApp
nameRequired=true nameRequired=true
descriptionShow=false descriptionShow=false
nameDisabled=showName nameDisabled=showName
bothColClass="col span-12"
colClass="col span-12"
}} }}
</div>
{{#if showName}} <div class="col span-6" style="padding-top: 6px;"> {{!-- matches styles of form-name-description --}}
{{#advanced-section}} <label for="" class="acc-label">{{t 'newCatalog.templateVersion'}}</label>
<hr class="mt-20 mb-20"/>
{{form-namespace
namespace=primaryResource
mode='reuse'
errors=namespaceErrors
}}
{{/advanced-section}}
{{/if}}
</section>
<section class="{{sectionClass}}">
<h4>{{t 'newCatalog.templateVersion'}}</h4>
<div class="row">
<div class="col span-6">
{{new-select {{new-select
classNames="form-control" classNames="form-control"
content=sortedVersions content=sortedVersions
@ -93,26 +67,54 @@
<p class="text-info">{{t (if editing selectVersionUpgrade selectVersionAdd)}}</p> <p class="text-info">{{t (if editing selectVersionUpgrade selectVersionAdd)}}</p>
</div> </div>
</div> </div>
</section> <div class="col span-12">
{{#if showName}}
{{#advanced-section}}
<hr class="mt-20 mb-20"/>
<section class="{{sectionClass}}"> {{form-namespace
{{#if getTemplate.isRunning}} namespace=primaryResource
mode='reuse'
errors=namespaceErrors
}}
{{/advanced-section}}
{{/if}}
</div>
</div>
</div>
{{#if getTemplate.isRunning}}
<section class="row">
<div class="text-center"> <div class="text-center">
<i class="icon icon-spinner icon-spin" style="font-size:36px;"></i> <i class="icon icon-spinner icon-spin" style="font-size:36px;"></i>
</div> </div>
</section>
{{else}}
<div class="accordion">
<div class="accordion-header">
<div class="title">
{{#if (or (eq templateKind 'native') selectedTemplateModel.questions)}}
<span class="m-0 block">{{t 'inputAnswers.config'}}</span>
<span class="help-block">{{t 'inputAnswers.protip'}}</span>
{{else}}
<span class="m-0 block">{{t 'newCatalog.helm.label'}}</span>
<span class="help-block">{{t 'newCatalog.helm.protip'}}</span>
{{/if}} {{/if}}
</div>
</div>
<div class="accordion-content row">
{{#if selectedTemplateModel}} {{#if selectedTemplateModel}}
{{#if (eq templateKind 'native')}} {{#if (eq templateKind 'native')}}
{{input-answers {{input-answers
selectedTemplate=selectedTemplateModel selectedTemplate=selectedTemplateModel
showHeader=false
}} }}
{{else}} {{else}}
<h4>{{t 'newCatalog.helm.label'}}</h4>
<div class="span-12"> <div class="span-12">
{{#if selectedTemplateModel.questions}} {{#if selectedTemplateModel.questions}}
{{input-answers {{input-answers
selectedTemplate=selectedTemplateModel selectedTemplate=selectedTemplateModel
showHeader=false
}} }}
{{else}} {{else}}
{{form-key-value {{form-key-value
@ -130,9 +132,10 @@
{{/if}} {{/if}}
</div> </div>
{{/if}} {{/if}}
{{/if}} {{/if}}
</section> </div>
</div>
{{/if}}
{{#if (and selectedTemplateModel (not getTemplate.isRunning))}} {{#if (and selectedTemplateModel (not getTemplate.isRunning))}}
{{#if showPreview}} {{#if showPreview}}

View File

@ -1,6 +1,6 @@
{{#if hasBlock}} {{#if hasBlock}}
{{ yield this}} {{yield this}}
{{ else }} {{else}}
{{#if currentVersion}} {{#if currentVersion}}
{{#tooltip-element type="tooltip-basic" model=currentVersion tooltipTemplate='tooltip-static' aria-describedby="tooltip-base" tooltipFor="upgrade"}} {{#tooltip-element type="tooltip-basic" model=currentVersion tooltipTemplate='tooltip-static' aria-describedby="tooltip-base" tooltipFor="upgrade"}}
{{t (concat 'upgradeBtn.status.' upgradeStatus)}} {{t (concat 'upgradeBtn.status.' upgradeStatus)}}

View File

@ -44,7 +44,7 @@ export default Mixin.create({
color: computed('upgradeStatus', function() { color: computed('upgradeStatus', function() {
switch ( get(this, 'upgradeStatus') ) { switch ( get(this, 'upgradeStatus') ) {
case NONE: case NONE:
return 'hide'; return 'none';
case CURRENT: case CURRENT:
case LOADING: case LOADING:
return 'bg-default'; return 'bg-default';
@ -101,7 +101,8 @@ export default Mixin.create({
let state = get(this, 'model.state'); let state = get(this, 'model.state');
let info = get(this, 'model.externalIdInfo'); let info = get(this, 'model.externalIdInfo');
let catalogTemplate = get(this, 'model.catalogTemplate'); let catalogTemplate = get(this, 'model.catalogTemplate');
let upgradeVersions={}; let upgradeVersions = {};
let allVersions = {};
if ( state === 'upgraded' ) { if ( state === 'upgraded' ) {
set(this, 'upgradeStatus', UPGRADED); set(this, 'upgradeStatus', UPGRADED);
@ -119,7 +120,14 @@ export default Mixin.create({
set(this, 'upgradeStatus', NONE); set(this, 'upgradeStatus', NONE);
} }
upgradeVersions = parseUpgradeVersions(catalogTemplate.get('versionLinks'), info.version, get(this, 'model')); if ( catalogTemplate ) {
upgradeVersions = parseUpgradeVersions(
get(catalogTemplate, 'versionLinks'),
get(info, 'version'),
get(this, 'model')
);
get(catalogTemplate, 'versionLinks');
}
if (Object.keys(upgradeVersions).length >= 1) { if (Object.keys(upgradeVersions).length >= 1) {
setProperties(this, { setProperties(this, {
@ -127,7 +135,10 @@ export default Mixin.create({
latestVersion: Object.keys(upgradeVersions)[Object.keys(upgradeVersions).length-1], latestVersion: Object.keys(upgradeVersions)[Object.keys(upgradeVersions).length-1],
}); });
} else { } else {
set(this, 'upgradeStatus', NONE); setProperties(this, {
upgradeStatus: CURRENT,
latestVersion: get(info, 'version'),
});
} }
// console.log('upgradeVersions', upgradeVersions); // console.log('upgradeVersions', upgradeVersions);
@ -149,7 +160,7 @@ export default Mixin.create({
} }
setProperties(this, { setProperties(this, {
allVersions: catalogTemplate.get('versionLinks'), allVersions: allVersions,
upgradeVersions: upgradeVersions upgradeVersions: upgradeVersions
}); });
return; return;

View File

@ -189,6 +189,8 @@ var C = {
PROJECT: { PROJECT: {
TYPE_RANCHER: 'local', TYPE_RANCHER: 'local',
TYPE_ACTIVE_DIRECTORY_USER: 'activedirectory_user',
TYPE_ACTIVE_DIRECTORY_GROUP: 'activedirectory_group',
TYPE_AZURE_USER: 'azuread_user', TYPE_AZURE_USER: 'azuread_user',
TYPE_AZURE_GROUP: 'azuread_group', TYPE_AZURE_GROUP: 'azuread_group',
TYPE_GITHUB_USER: 'github_user', TYPE_GITHUB_USER: 'github_user',

View File

@ -167,6 +167,32 @@ accountsPage:
modal: modal:
password: Change Password password: Change Password
appDetailPage:
header: "App: {appName}"
output:
title: Launch Output
detail: TBD
workloads:
title: Workloads
detail: Workloads created for this application.
nodata: No workloads were created for this application.
ingress:
title: Ingress Rules
detail: Ingress rules created for this application.
nodata: No ingress rules were created for this application.
services:
title: Services
detail: Services created with this application
nodata: No services were created for this application.
volumes:
title: Volumes
detail: Persistant Volume claims created with this application
nodata: No volume claims were made for this application.
secrets:
title: Secrets
detail: Secrets associated with this application
nodata: This application has no secrets
podSecurityPoliciesPage: podSecurityPoliciesPage:
index: index:
header: Pod Security Policies header: Pod Security Policies
@ -311,8 +337,6 @@ authPage:
buttonText: buttonText:
pre: Authenticate with IDP pre: Authenticate with IDP
post: Waiting to hear back from IDP post: Waiting to hear back from IDP
providerName: providerName:
shibboleth: Shibboleth shibboleth: Shibboleth
root: root:
@ -323,33 +347,32 @@ authPage:
header: header:
enabled: enabled:
label: "{github} is enabled" label: "{github} is enabled"
required: "{appName} is configured to allow access to authorized users and organizations." # required: "{appName} is configured to allow access to authorized users and organizations."
restricted: "{appName} is configured to allow access to project members, authorized users and organizations." # restricted: "{appName} is configured to allow access to project members, authorized users and organizations."
# required: | required: |
# {appName} is configured to allow access to {orgs, plural, {appName} is configured to allow access to {orgs, plural,
# =0 {no organizations} =0 {no organizations}
# =1 {# organization} =1 {# organization}
# other {# organizations} other {# organizations}
# } and {users, plural, } and {users, plural,
# =0 {no users} =0 {no users}
# =1 {# user} =1 {# user}
# other {# users} other {# users}
# }. }.
# restricted: | restricted: |
# {appName} is configured to allow access to project members, {orgs, plural, {appName} is configured to allow access to project members, {orgs, plural,
# =0 {no organizations} =0 {no organizations}
# =1 {# organization} =1 {# organization}
# other {# organizations} other {# organizations}
# } and {users, plural, } and {users, plural,
# =0 {no users} =0 {no users}
# =1 {# user} =1 {# user}
# other {# users} other {# users}
# }. }.
# unrestricted: "{appName} is configured to allow access to any {github} user." # unrestricted: "{appName} is configured to allow access to any {github} user."
disabled: disabled:
label: GitHub is not configured label: GitHub is not configured
warning: "{appName} can be configured to restrict access to a set of GitHub users and organization members." warning: "{appName} can be configured to restrict access to a set of GitHub users and organization members."
authenticated: authenticated:
header: header:
text: Authentication text: Authentication
@ -398,7 +421,6 @@ authPage:
buttonText: buttonText:
pre: Authenticate with GitHub pre: Authenticate with GitHub
post: Waiting to hear back from GitHub post: Waiting to hear back from GitHub
azuread: azuread:
header: header:
enabled: 'Azure AD Authentication is <b>enabled</b>' enabled: 'Azure AD Authentication is <b>enabled</b>'
@ -439,8 +461,6 @@ authPage:
label: Login Password label: Login Password
pre: Authenticate with Azure pre: Authenticate with Azure
post: Waiting to hear back from Azure post: Waiting to hear back from Azure
localAuth: localAuth:
header: header:
enabled: 'Local Authentication is enabled' enabled: 'Local Authentication is enabled'
@ -652,6 +672,7 @@ ingressPage:
header: 'Ingress: {name}' header: 'Ingress: {name}'
table: table:
noMatch: No ingresses match the current search. noMatch: No ingresses match the current search.
noData: You do not have any ingress rules yet.
containerPage: containerPage:
header: 'Container: {name}' header: 'Container: {name}'
portsTab: portsTab:
@ -4162,6 +4183,7 @@ newCatalog:
maintainer: "Maintainer:" maintainer: "Maintainer:"
newNamespace: New Namespace newNamespace: New Namespace
newApp: New Application newApp: New Application
appInfo: Application Read Me
noConfig: This template has no configuration options noConfig: This template has no configuration options
official: Officially Certified official: Officially Certified
preview: Preview preview: Preview
@ -4945,7 +4967,7 @@ upgradeBtn:
version: version:
current: 'Current' current: 'Current'
status: status:
none: 'None' none: 'Upgrade: None'
loading: 'Checking upgrades...' loading: 'Checking upgrades...'
current: 'Up to date' current: 'Up to date'
available: 'Upgrade available' available: 'Upgrade available'