Merge pull request #1771 from vincent99/master

Namespaced secrets
This commit is contained in:
Vincent Fiduccia 2018-03-29 16:12:04 -07:00 committed by GitHub
commit d9382f2d3c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 347 additions and 533 deletions

View File

@ -27,14 +27,6 @@
</div>
{{/if}}
{{#unless model.cluster.isActive}}
{{banner-message color='bg-error' message=(t 'clusterDashboard.notActive' state=model.cluster.displayState)}}
{{#if model.cluster.showTransitioningMessage}}
<div class="{{model.cluster.stateColor}}">{{model.cluster.transitioningMessage}}</div>
{{/if}}
{{/unless}}
<section class="text-center">
{{cluster-dashboard nodes=currentClusterNodes}}
</section>

View File

@ -0,0 +1,9 @@
{{#unless model.isActive}}
{{#banner-message color='bg-error m-0'}}
<p>{{t 'clusterDashboard.notActive' state=model.displayState htmlSafe=true}}</p>
{{#if model.showTransitioningMessage}}
<p class="{{model.stateColor}}">{{uc-first model.transitioningMessage}}</p>
{{/if}}
{{/banner-message}}
{{/unless}}
{{outlet}}

View File

@ -52,7 +52,7 @@ export default Route.extend(Preload,{
actions: {
toggleGrouping() {
let choices = ['none','service','stack'];
let choices = ['none','node','workload','namespace'];
let cur = this.get('controller.group');
let neu = choices[((choices.indexOf(cur)+1) % choices.length)];
next(() => {

View File

@ -190,40 +190,38 @@ export default Route.extend(Preload, {
}
},
_gotoRoute(name, withProjectId=true) {
_gotoRoute(name, scope='global') {
// Don't go to routes if in a form page, because you can easily not be on an input
// and leave the page accidentally.
if ( $('FORM').length > 0 ) {
return;
}
if ( withProjectId ) {
this.transitionTo(name, get(this,'scope.currentProject.id'));
} else {
const clusterId = get(this, 'scope.currentCluster.id');
const projectId = get(this, 'scope.currentProject.id');
if ( scope === 'cluster' && clusterId ) {
this.transitionTo(name, clusterId);
} else if ( scope === 'project' && projectId ) {
this.transitionTo(name, projectId);
} else if ( scope === 'global' ) {
this.transitionTo(name);
}
},
actions: {
changeTheme() {
var userTheme = get(this,'userTheme');
var currentTheme = userTheme.getTheme();
_gotoMembers() {
const clusterId = get(this, 'scope.currentCluster.id');
const projectId = get(this, 'scope.currentProject.id');
switch (currentTheme) {
case 'ui-light':
userTheme.setTheme('ui-dark');
break;
case 'ui-dark':
userTheme.setTheme('ui-auto');
break;
case 'ui-auto':
userTheme.setTheme('ui-light');
break;
default:
break;
if ( projectId ) {
this._gotoRoute('authenticated.project.security.members.index', 'project');
} else if ( clusterId ) {
this._gotoRoute('authenticated.cluster.security.members.index', 'cluster');
}
},
actions: {
error(err,transition) {
// Unauthorized error, send back to login screen
if ( err && err.status === 401 )
@ -273,25 +271,11 @@ export default Route.extend(Preload, {
console.log('Switch finished');
},
gotoA() { this._gotoRoute('apps-tab.index'); },
gotoB() { this._gotoRoute('ingresses.index'); },
gotoD() { this._gotoRoute('authenticated.project.dns.index'); },
gotoE() { this._gotoRoute('global-admin.clusters.index', false); },
gotoH() { this._gotoRoute('hosts.index'); },
gotoK() { this._gotoRoute('authenticated.project.apikeys'); },
gotoV() { this._gotoRoute('volumes.index'); },
gotoW() { this._gotoRoute('workloads.index'); },
help() {
get(this,'modalService').toggleModal('modal-shortcuts');
},
gotoP() {
if ( get(this,'access.admin') ) {
this._gotoRoute('global-admin.processes', false);
}
},
// Special
nextTab() {
if ( $('.tab-nav').length ) {
let cur = $('.tab-nav .active');
@ -307,15 +291,6 @@ export default Route.extend(Preload, {
}
},
neu() {
let elem = $('.right-buttons a:last')[0];
if ( elem ) {
event.stopPropagation();
event.preventDefault();
elem.click();
}
},
search(event) {
let elem = $("INPUT[type='search']")[0];
if ( elem ) {
@ -328,23 +303,69 @@ export default Route.extend(Preload, {
delete() {
$('.bulk-actions .icon-trash').closest('a').click();
},
// Global
gotoc() { this._gotoRoute('global-admin.clusters.index','global'); },
gotou() { this._gotoRoute('global-admin.accounts.index','global'); },
gotoK() { this._gotoRoute('authenticated.apikeys','global'); },
gotoP() { this._gotoRoute('authenticated.prefs','global'); },
gotoT() { this._gotoRoute('authenticated.node-templates','global'); },
// Cluster or Project
gotom() { this._gotoMembers(); },
// Cluster
gotod() { this._gotoRoute('authenticated.cluster.index','cluster'); },
goton() { this._gotoRoute('authenticated.cluster.nodes','cluster'); },
gotop() { this._gotoRoute('authenticated.cluster.projects','cluster'); },
gotoV() { this._gotoRoute('authenticated.cluster.storage.persistent-volumes','cluster'); },
doom() {
const clusterId = get(this, 'scope.currentCluster.id');
if ( clusterId ) {
this.get('modalService').toggleModal('modal-kubectl');
}
},
// Project
gotow() { this._gotoRoute('containers','project'); },
gotob() { this._gotoRoute('ingresses','project'); },
gotos() { this._gotoRoute('authenticated.project.dns','project'); },
gotov() { this._gotoRoute('volumes','project'); },
gotoa() { this._gotoRoute('apps-tab.index','project'); },
},
shortcuts: {
'a': 'gotoA',
'b': 'gotoB',
'd': 'gotoD',
'e': 'gotoE',
'h': 'gotoH',
// Global
'c': 'gotoc',
'u': 'gotou',
'shift+k': 'gotoK',
'n': 'neu',
'p': 'gotoP',
'shift+p': 'gotoP',
'shift+t': 'gotoT',
// Cluster or Proejct
'shift+`': 'doom',
'm': 'gotom',
// Cluster
'd': 'gotod',
'n': 'goton',
'p': 'gotop',
'shift+v': 'gotoV',
// Project
'w': 'gotow',
'b': 'gotob',
's': 'gotos',
'v': 'gotov',
'a': 'gotoa',
// Other
// 'g': Defined in subroutes
't': 'nextTab',
'v': 'gotoV',
'w': 'gotoW',
'/': 'search',
'shift+/': 'help',
'shift+t': 'changeTheme',
'backspace': 'delete',
'delete': 'delete',
},

View File

@ -16,12 +16,10 @@
<td>&nbsp;</td>
<td data-title="{{t 'formSecrets.kind.label'}}">
{{#if editing}}
{{searchable-select
classNames="form-control"
optionValuePath="id"
optionLabelPath="label"
content=allSecrets
{{schema/input-secret
namespace=namespace
value=secret.sourceName
valueKey='name'
}}
{{else}}
{{#if secret}}

View File

@ -18,7 +18,7 @@
{{/if}}
{{#if model.container.showTransitioningMessage}}
<div class="{{model.container.stateColor}}"><p>{{model.container.transitioningMessage}}</p></div>
<div class="{{model.container.stateColor}}"><p>{{uc-first model.container.transitioningMessage}}</p></div>
{{/if}}
<section>

View File

@ -28,6 +28,15 @@ const rootNav = [
localizedLabel: 'nav.infra.tab',
ctx: [getProjectId],
submenu: [
{
id: 'tools-alerts',
localizedLabel: 'nav.tools.alerts',
icon: 'icon icon-alert',
route: 'authenticated.project.alert',
resource: [],
ctx: [getProjectId],
resourceScope: 'global',
},
{
id: 'infra-certificates',
localizedLabel: 'nav.infra.certificates',
@ -46,6 +55,15 @@ const rootNav = [
resource: ["configmap"],
resourceScope: 'project',
},
{
id: 'tools-logging',
localizedLabel: 'nav.tools.logging',
icon: 'icon icon-files',
route: 'authenticated.project.logging',
resourceScope: 'global',
resource: [],
ctx: [getProjectId],
},
{
id: 'infra-registries',
localizedLabel: 'nav.infra.registries',
@ -64,13 +82,6 @@ const rootNav = [
resource: ["namespacedsecret", "secret"],
resourceScope: 'project',
},
// {
// id: 'infra-hooks',
// localizedLabel: 'nav.infra.hooks',
// icon: 'icon icon-link',
// route: 'authenticated.project.hooks',
// ctx: [getProjectId],
// },
],
},
{
@ -83,32 +94,7 @@ const rootNav = [
resourceScope: 'global',
ctx: [getProjectId],
},
{
scope: 'project',
id: 'project-tools',
localizedLabel: 'nav.tools.tab',
ctx: [getProjectId],
submenu: [
{
id: 'tools-alerts',
localizedLabel: 'nav.tools.alerts',
// icon: 'icon icon-key',
route: 'authenticated.project.alert',
resource: [],
ctx: [getProjectId],
resourceScope: 'global',
},
{
id: 'tools-logging',
localizedLabel: 'nav.tools.logging',
// icon: 'icon icon-key',
route: 'authenticated.project.logging',
resourceScope: 'global',
resource: [],
ctx: [getProjectId],
},
],
},
// Cluster
{
scope: 'cluster',

View File

@ -1,7 +1,6 @@
import Resource from 'ember-api-store/models/resource';
import PolledResource from 'ui/mixins/cattle-polled-resource';
var AzureKubernetesServiceConfig = Resource.extend(PolledResource, {
var AzureKubernetesServiceConfig = Resource.extend({
type: 'azureKubernetesServiceConfig',
reservedKeys: [],

View File

@ -1,11 +1,10 @@
import PolledResource from 'ui/mixins/cattle-polled-resource';
import Resource from 'ember-api-store/models/resource';
import { inject as service } from '@ember/service';
import { computed } from '@ember/object';
import { get } from '@ember/object';
import { ucFirst } from 'shared/utils/util';
var Catalog = Resource.extend(PolledResource, {
var Catalog = Resource.extend({
modalService: service('modal'),
actions: {

View File

@ -1,7 +1,6 @@
import Resource from 'ember-api-store/models/resource';
import PolledResource from 'ui/mixins/cattle-polled-resource';
var GoogleKubernetesEngineConfig = Resource.extend(PolledResource, {
var GoogleKubernetesEngineConfig = Resource.extend({
type: 'googleKubernetesEngineConfig',
reservedKeys: [],

View File

@ -1,8 +1,7 @@
import { inject as service } from '@ember/service';
import Resource from 'ember-api-store/models/resource';
import PolledResource from 'ui/mixins/cattle-polled-resource';
var PodSecurityPolicyTemplate = Resource.extend(PolledResource, {
var PodSecurityPolicyTemplate = Resource.extend({
type: 'podSecurityPolicyTemplate',
router: service(),

View File

@ -1,7 +1,6 @@
import Resource from 'ember-api-store/models/resource';
import PolledResource from 'ui/mixins/cattle-polled-resource';
var RancherKubernetesEngineConfig = Resource.extend(PolledResource, {
var RancherKubernetesEngineConfig = Resource.extend({
type: 'rancherKubernetesEngineConfig',
reservedKeys: [],

View File

@ -185,9 +185,6 @@ var Workload = Resource.extend(DisplayImage, StateCounts, EndpointPorts, {
// { label: 'action.logs', icon: 'icon icon-file', action: 'logs', enabled: !!a.logs, altAction: 'popoutLogs' },
{ divider: true },
{ label: 'action.pause', icon: 'icon icon-pause', action: 'pause', enabled: !!a.pause, bulkable: true},
{ label: 'action.start', icon: 'icon icon-play', action: 'start', enabled: !!a.activate, bulkable: true},
{ label: 'action.restart', icon: 'icon icon-refresh', action: 'restart', enabled: !!a.restart, bulkable: true },
{ label: 'action.stop', icon: 'icon icon-stop', action: 'promptStop', enabled: !!a.deactivate, altAction: 'stop', bulkable: true},
{ divider: true },
{ label: 'action.remove', icon: 'icon icon-trash', action: 'promptDelete', enabled: !!l.remove, altAction: 'delete', bulkable: true},
{ divider: true },

View File

@ -165,8 +165,6 @@ fieldset[disabled] .btn {
//button group
[class^='btn-group'] {
@include no-inline-space;
position: relative;
display: inline-block;
vertical-align: middle;

View File

@ -9,6 +9,6 @@
</div>
{{#if model.showTransitioningMessage}}
<div class="text-small bg-body p-5 mt-5 force-wrap {{model.stateColor}}">{{model.transitioningMessage}}</div>
<div class="text-small bg-body p-5 mt-5 force-wrap {{model.stateColor}}">{{uc-first model.transitioningMessage}}</div>
{{/if}}
</div>

View File

@ -23,7 +23,7 @@
{{/if}}
{{#if service.showTransitioningMessage}}
<div class="{{service.stateColor}}"><p>{{service.transitioningMessage}}</p></div>
<div class="{{service.stateColor}}"><p>{{uc-first service.transitioningMessage}}</p></div>
{{/if}}
<section>

View File

@ -17,8 +17,8 @@ export default Ember.Component.extend(ThrottledResize, {
// URL or instance+cmd
url: null,
instance: null,
command: null,
alternateLabel: alternateLabel,
showProtip: true,
contenteditable: false,
@ -57,6 +57,15 @@ export default Ember.Component.extend(ThrottledResize, {
next(this, 'exec');
},
didInitAttrs() {
if ( !get(this, 'displayName') ) {
const instance = get(this,'instance');
if ( instance ) {
set(this, 'displayName', get(instance, 'displayName'));
}
}
},
exec: function () {
let url = get(this, 'url');

View File

@ -1,5 +1,5 @@
<h2 class='mb-10'>
<i class="icon icon-terminal"></i> {{t 'modalShell.title' instanceName=instance.displayName}}
<i class="icon icon-terminal"></i> {{t 'modalShell.title' instanceName=displayName}}
<div class="console-status text-muted pull-right">{{t (concat 'containerShell.status.' status) error=error}}</div>
</h2>
{{#if showProtip}}

View File

@ -9,7 +9,7 @@
</div>
{{#if model.showTransitioningMessage}}
<div class="subpod-detail clip {{if model.isError 'text-danger'}}">
{{model.transitioningMessage}}
{{uc-first model.transitioningMessage}}
</div>
{{else}}
<div class="subpod-detail">

View File

@ -95,7 +95,7 @@ export default Component.extend({
scaleModeChanged: observer('scaleMode', function() {
var scaleMode = get(this,'scaleMode');
if ( !scaleMode ) {
if ( !scaleMode || scaleMode === 'sidekick' ) {
return;
}

View File

@ -12,7 +12,6 @@ import { get, set, computed } from '@ember/object';
export default Component.extend({
layout,
secrets: null,
_allSecrets: null,
intl: service(),
store: service('store'),
statusClass: null,
@ -30,15 +29,11 @@ export default Component.extend({
},
init() {
set(this, '_allSecrets', get(this, 'store').all('secret'));
get(this, 'store').find('secret');
this._super(...arguments);
if (!get(this, 'secrets') ) {
set(this, 'secrets', [])
}
},
status: computed('secrets.@each.secretId','errors.length', function() {

View File

@ -20,7 +20,7 @@
{{#each secrets as |secret|}}
{{form-secrets-row
secret=secret
secrets=_allSecrets
namespace=namespace
editing=editing
remove=(action "removeSecret")
}}

View File

@ -44,19 +44,7 @@
</div>
<div class="row box mt-20">
<div class="col span-4">
<label class="acc-label">{{t 'formSecurity.runAsNonRoot.label'}}</label>
{{#input-or-display editable=editing value=instance.runAsNonRoot}}
<div class="radio">
<label>{{radio-button selection=instance.runAsNonRoot value=false}} {{t 'formSecurity.runAsNonRoot.disable'}}</label>
</div>
<div class="radio">
<label>{{radio-button selection=instance.runAsNonRoot value=true}} {{t 'formSecurity.runAsNonRoot.enable'}}</label>
</div>
{{/input-or-display}}
</div>
{{#unless isSidekick}}
<div class="col span-4">
<div class="col span-6">
<label class="acc-label">{{t 'formSecurity.pidMode.label'}}</label>
{{#input-or-display editable=editing value=service.pid}}
<div class="radio">
@ -67,7 +55,7 @@
</div>
{{/input-or-display}}
</div>
<div class="col span-4">
<div class="col span-6">
<label class="acc-label">{{t 'formSecurity.ipcMode.label'}}</label>
{{#input-or-display editable=editing value=service.ipc}}
<div class="radio">
@ -78,10 +66,20 @@
</div>
{{/input-or-display}}
</div>
{{/unless}}
</div>
<div class="row box mt-20">
<div class="col span-4">
<div class="col span-6">
<label class="acc-label">{{t 'formSecurity.runAsNonRoot.label'}}</label>
{{#input-or-display editable=editing value=instance.runAsNonRoot}}
<div class="radio">
<label>{{radio-button selection=instance.runAsNonRoot value=false}} {{t 'formSecurity.runAsNonRoot.disable'}}</label>
</div>
<div class="radio">
<label>{{radio-button selection=instance.runAsNonRoot value=true}} {{t 'formSecurity.runAsNonRoot.enable'}}</label>
</div>
{{/input-or-display}}
</div>
<div class="col span-6">
<label class="acc-label">{{t 'formSecurity.readOnlyRootFilesystem.label'}}</label>
{{#input-or-display editable=editing value=instance.readOnly}}
<div class="radio">

View File

@ -87,17 +87,6 @@
<hr class="mt-30 mb-30" />
{{#accordion-list as | al expandFn | }}
{{container/form-volumes
launchConfig=launchConfig
workload=service
namespace=namespace
errors=volumeErrors
registerHook=(action "registerHook")
expandAll=al.expandAll
expandFn=expandFn
expandOnInit=true
}}
{{#accordion-list-item
title=(t 'newContainer.environment.label')
detail=(t 'newContainer.environment.detail' appName=settings.appName)
@ -120,6 +109,7 @@
}}
<hr class="mt-30 mb-30" />
{{container/form-secrets
namespace=namespace
classNames="accordion-wrapper"
secrets=launchConfig.environmentFrom
errors=secretErrors
@ -192,6 +182,16 @@
</div>
{{/accordion-list-item}}
{{container/form-volumes
launchConfig=launchConfig
workload=service
namespace=namespace
errors=volumeErrors
registerHook=(action "registerHook")
expandAll=al.expandAll
expandFn=expandFn
}}
{{#unless isSidekick}}
{{container/form-upgrade
workload=service

View File

@ -10,28 +10,24 @@
<li>{{#link-to-external "authenticated.project.dns.index" scope.currentProject.id}}{{t 'nav.containers.dns'}}{{/link-to-external}}</li>
{{/if}}
<li>{{#link-to-external "volumes.index" scope.currentProject.id}}{{t 'nav.containers.volumes'}}{{/link-to-external}}</li>
<li>{{#link-to-external "authenticated.project.pipeline.pipelines" scope.currentProject.id}}Pipeline{{/link-to-external}}</li>
<li>{{#link-to-external "authenticated.project.pipeline.pipelines" scope.currentProject.id}}{{t 'nav.containers.pipelines'}}{{/link-to-external}}</li>
</ul>
<div class="right-buttons">
{{#if showGroup}}
<div class="btn-group p-0 mr-10">
{{#tooltip-element type="tooltip-basic" model=(t 'nav.group.none') tooltipTemplate='tooltip-static' aria-describedby="tooltip-base" tooltipFor="tooltipLink"}}
{{#link-to (query-params group="none") classNames="btn btn-sm bg-default"}}<i class="icon icon-container"></i>{{/link-to}}
{{/tooltip-element}}
{{#tooltip-element type="tooltip-basic" model=(t 'nav.group.workload') tooltipTemplate='tooltip-static' aria-describedby="tooltip-base" tooltipFor="tooltipLink"}}
{{#link-to (query-params group="workload") classNames="btn btn-sm bg-default"}}<i class="icon icon-service"></i>{{/link-to}}
{{/tooltip-element}}
{{#tooltip-element type="tooltip-basic" model=(t 'nav.group.namespaceWorkload') tooltipTemplate='tooltip-static' aria-describedby="tooltip-base" tooltipFor="tooltipLink"}}
{{#link-to (query-params group="namespace") classNames="btn btn-sm bg-default"}}<i class="icon icon-list-nested"></i>{{/link-to}}
{{/tooltip-element}}
{{#tooltip-element type="tooltip-basic" model=(t 'nav.group.node') tooltipTemplate='tooltip-static' aria-describedby="tooltip-base" tooltipFor="tooltipLink"}}
{{#link-to (query-params group="node") classNames="btn btn-sm bg-default"}}<i class="icon icon-hosts"></i>{{/link-to}}
{{/tooltip-element}}
{{~#tooltip-element type="tooltip-basic" model=(t 'nav.group.none') tooltipTemplate='tooltip-static' aria-describedby="tooltip-base" tooltipFor="tooltipLink"~}}
{{~#link-to (query-params group="none") classNames="btn btn-sm bg-default"~}}<i class="icon icon-container"></i>{{~/link-to~}}
{{~/tooltip-element~}}
{{~#tooltip-element type="tooltip-basic" model=(t 'nav.group.node') tooltipTemplate='tooltip-static' aria-describedby="tooltip-base" tooltipFor="tooltipLink"~}}
{{~#link-to (query-params group="node") classNames="btn btn-sm bg-default"~}}<i class="icon icon-hosts"></i>{{~/link-to~}}
{{~/tooltip-element~}}
{{~#tooltip-element type="tooltip-basic" model=(t 'nav.group.workload') tooltipTemplate='tooltip-static' aria-describedby="tooltip-base" tooltipFor="tooltipLink"~}}
{{~#link-to (query-params group="workload") classNames="btn btn-sm bg-default"~}}<i class="icon icon-service"></i>{{~/link-to~}}
{{~/tooltip-element~}}
{{~#tooltip-element type="tooltip-basic" model=(t 'nav.group.namespaceWorkload') tooltipTemplate='tooltip-static' aria-describedby="tooltip-base" tooltipFor="tooltipLink"~}}
{{~#link-to (query-params group="namespace") classNames="btn btn-sm bg-default"~}}<i class="icon icon-list-nested"></i>{{~/link-to~}}
{{~/tooltip-element~}}
</div>
{{/if}}

View File

@ -8,7 +8,7 @@
<td data-title="{{dt.name}}" class="clip">
<a href="{{href-to "authenticated.project.dns.detail" model.id}}">{{model.displayName}}</a>
{{#if model.showTransitioningMessage}}
<div class="clip text-small {{model.stateColor}}">{{model.transitioningMessage}}</div>
<div class="clip text-small {{model.stateColor}}">{{uc-first model.transitioningMessage}}</div>
{{else if (and (not-eq model.recordType "clusterIp") model.clusterIp)}}
<div class="text-small text-muted">{{t 'dnsPage.table.clusterIpDetail' ip=model.clusterIp}}</div>
{{/if}}

View File

@ -3,7 +3,7 @@
<td colspan="{{leftColspan}}"></td>
{{/if}}
<td class="pb-5" colspan="{{mainColspan}}">
<small class="{{model.stateColor}}" >{{model.transitioningMessage}}</small>
<small class="{{model.stateColor}}">{{uc-first model.transitioningMessage}}</small>
</td>
{{#if rightColspan}}
<td colspan="{{rightColspan}}"></td>

View File

@ -1,5 +1,7 @@
{{container-shell
url=url
showProtip=false
displayName=scope.currentCluster.displayName
dismiss=(action "cancel")
disconncted=(action "cancel")
}}

View File

@ -11,7 +11,7 @@ let DEFAULT_TIME = 400;
export default Component.extend(ModalBase, {
layout,
prefs : service(),
classNames: ['generic', 'medium-modal'],
classNames: ['generic', 'medium-modal','p-0'],
settings: service(),
access: service(),

View File

@ -27,90 +27,45 @@
<h1 class="text-mono text-center pt-20">{{t 'modalShortcuts.title.pl'}}</h1>
{{/if}}
<div class="row">
<div class="row m-20">
<div class="col span-6">
<h3 class="m-0">{{t 'modalShortcuts.navigation'}}</h3>
<hr class="mt-5 mb-15">
<p class="m-5">
<span width="90"><code>a</code></span>
<span>{{t 'modalShortcuts.a'}}</span>
</p>
<p class="m-5">
<span><code>b</code></span>
<span>{{t 'modalShortcuts.b'}}</span>
</p>
<p class="m-5">
<span><code>c</code></span>
<span>{{t 'modalShortcuts.c'}}</span>
</p>
<p class="m-5">
<span><code>d</code></span>
<span>{{t 'modalShortcuts.d'}}</span>
</p>
<p class="m-5">
<span><code>e</code></span>
<span>{{t 'modalShortcuts.e'}}</span>
</p>
<p class="m-5">
<span><code>h</code></span>
<span>{{t 'modalShortcuts.h'}}</span>
</p>
<p class="m-5">
<span><code>Shift-K</code></span>
<span>{{t 'modalShortcuts.k'}}</span>
</p>
<p class="m-5">
<span><code>v</code></span>
<span>{{t 'modalShortcuts.v'}}</span>
</p>
</div>
<div class="col span-6">
<h3 class="m-0">{{t 'modalShortcuts.other'}}</h3>
<hr class="mt-5 mb-15">
<p class="m-5">
<span width="90"><code>g</code></span>
<span>{{t 'modalShortcuts.g'}}</span>
</p>
<p class="m-5">
<span width="90"><code>n</code></span>
<span>{{t 'modalShortcuts.n'}}</span>
</p>
<!--
<p class="m-5">
<span><code>s</code></span>
<span>{{t 'modalShortcuts.s'}}</span>
</p>
-->
<p class="m-5">
<span width="90"><code>t</code></span>
<span>{{t 'modalShortcuts.t'}}</span>
</p>
<p class="m-5">
<span width="90"><code>Shift-T</code></span>
<span>{{t 'modalShortcuts.theme' currentTheme=currentTheme}}</span>
</p>
<p class="m-5">
<span><code>/</code></span>
<span>{{t 'modalShortcuts.slash'}}</span>
</p>
<p class="m-5">
<span><code>?</code></span>
<span>{{t 'modalShortcuts.question'}}</span>
</p>
<h3 class="m-0">{{t 'modalShortcuts.global'}}</h3>
<hr class="mt-5 mb-5">
{{shortcut-key key='c' label='nav.admin.clusters'}}
{{shortcut-key key='u' label='nav.admin.accounts'}}
{{shortcut-key key='K' modifier='shift' label='nav.api.link'}}
{{shortcut-key key='P' modifier='shift' label='nav.userPreferences.link'}}
{{shortcut-key key='T' modifier='shift' label='nav.nodeTemplates.link'}}
{{#if isAdmin}}
<div class="mt-20">
<h3 class="m-0">{{t 'modalShortcuts.admin'}}</h3>
<hr class="mt-5 mb-15">
<p class="m-5">
<span width="90"><code>p</code></span>
<span>{{t 'modalShortcuts.p'}}</span>
</p>
<h3 class="m-0 mt-20">{{t 'modalShortcuts.project'}}</h3>
<hr class="mt-5 mb-5">
{{shortcut-key key='a' label='nav.apps.tab'}}
{{shortcut-key key='b' label='nav.containers.ingresses'}}
{{shortcut-key key='m' label='nav.cluster.members'}}
{{shortcut-key key='s' label='nav.containers.dns'}}
{{shortcut-key key='w' label='nav.containers.containers'}}
{{shortcut-key key='v' label='nav.containers.volumes'}}
</div>
{{/if}}
<div class="col span-6">
<h3 class="m-0">{{t 'modalShortcuts.cluster'}}</h3>
<hr class="mt-5 mb-5">
{{shortcut-key key='d' label='nav.cluster.dashboard'}}
{{shortcut-key key='m' label='nav.cluster.members'}}
{{shortcut-key key='n' label='nav.cluster.nodes'}}
{{shortcut-key key='p' label='nav.cluster.projects'}}
{{shortcut-key key='V' modifier='shift' label='nav.cluster.storage.volumes'}}
<h3 class="m-0 mt-20">{{t 'modalShortcuts.other'}}</h3>
<hr class="mt-5 mb-5">
{{shortcut-key key='delete' label='modalShortcuts.delete'}}
{{shortcut-key key='g' label='modalShortcuts.viewGroup'}}
{{shortcut-key key='t' label='modalShortcuts.nextTab'}}
{{shortcut-key key='/' label='modalShortcuts.search'}}
{{shortcut-key key='~' modifier='shift' label='modalShortcuts.shell'}}
{{shortcut-key key='?' modifier='shift' label='modalShortcuts.shortcuts'}}
</div>
</div>
<div class="footer-actions">
<div class="footer-actions pb-20">
<button {{action "cancel"}} class="btn bg-primary">{{t 'generic.closeModal'}}</button>
</div>

View File

@ -33,7 +33,7 @@
{{#if model.showTransitioningMessage}}
<tr class="group-row group-row-error">
<td colspan="{{fullColspan}}" class="text-small {{model.stateColor}}">
{{model.transitioningMessage}}
{{uc-first model.transitioningMessage}}
</td>
</tr>
{{/if}}

View File

@ -32,7 +32,7 @@
{{#if model.showTransitioningMessage}}
<tr class="group-row group-row-error">
<td colspan="{{fullColspan}}" class="text-small {{model.stateColor}}">
{{model.transitioningMessage}}
{{uc-first model.transitioningMessage}}
</td>
</tr>
{{/if}}

View File

@ -9,7 +9,7 @@
<td data-title="{{dt.name}}" class="clip">
{{model.displayName}}
{{#if model.showTransitioningMessage}}
<div class="clip text-small {{model.stateColor}}">{{model.transitioningMessage}}</div>
<div class="clip text-small {{model.stateColor}}">{{uc-first model.transitioningMessage}}</div>
{{/if}}
</td>

View File

@ -18,7 +18,7 @@
<td data-title="{{dt.name}}" class="clip">
<a href="{{href-to "container" model.id}}">{{model.displayName}}</a>
{{#if model.showTransitioningMessage}}
<div class="clip text-small {{model.stateColor}}" >{{model.transitioningMessage}}</div>
<div class="clip text-small {{model.stateColor}}">{{uc-first model.transitioningMessage}}</div>
{{else if model.displayEndpoints}}
<div class="clip text-small">
{{model.displayEndpoints}}

View File

@ -1,66 +1,100 @@
import { isArray } from '@ember/array';
import { get, set, computed, observer} from '@ember/object';
import { inject as service } from '@ember/service';
import Component from '@ember/component';
import layout from './template';
export default Component.extend({
layout,
selected: null, // Selected secret ID
intl: service(),
// Inputs
namespace: null,
selectClass: 'form-control',
exclude: null, // ID or array of IDs to exclude from list
valueKey: 'name', // What to set the value as.. 'name' or 'id'
// For use as a catalog question
field: null, // Read default from a schema resourceField
value: null, // name or id output string
allSecrets: null,
selected: null, // Selected secret ID
projectSecrets: null,
namespaceSecrets: null,
init() {
this._super(...arguments);
this.set('allSecrets', this.get('store').all('secret'));
set(this,'projectSecrets', get(this,'store').all('secret'));
set(this,'namespaceSecrets', get(this,'store').all('namespacedSecret'));
let def = this.get('field.default');
if ( def && !this.get('selected') ) {
let def = get(this,'field.default');
if ( def && !get(this,'selected') ) {
var exact;
this.get('allSecrets').forEach((secret) => {
if ( def === secret.get('name') )
{
exact = secret.get('id');
get(this,'projectSecrets').forEach((secret) => {
if ( def === get(secret,'name') ) {
exact = get(secret,'id');
}
});
this.set('selected', exact || null);
const namespaceId = get(this,'namespace.id');
if ( !exact && namespaceId ) {
get(this,'namespaceSecrets').forEach((secret) => {
if ( def === get(secret,'name') && get(secret,'namespaceId') === namespaceId) {
exact = get(secret,'id');
}
});
}
set(this,'selected', exact || null);
}
},
filtered: function() {
let list = this.get('allSecrets');
filtered: computed('projectSecrets.[]','namespaceSecrets.[]','namespace.id', function() {
const intl = get(this,'intl');
let exclude = this.get('exclude');
let out = get(this, 'projectSecrets').map((secret) => {
return {
label: get(secret, 'name'),
value: get(secret, 'id'),
group: intl.t('generic.project'),
};
});
const namespaceId = get(this, 'namespace.id');
if ( namespaceId ) {
get(this, 'namespaceSecrets').filterBy('namespaceId', namespaceId).forEach((secret) => {
out.push({
label: get(secret, 'name'),
value: get(secret, 'id'),
group: intl.t('generic.namespace'),
});
});
}
let exclude = get(this,'exclude');
if ( exclude ) {
if ( !isArray(exclude) ) {
exclude = [exclude];
}
list = list.filter(x => !exclude.includes(x.id));
out = out.filter(x => !exclude.includes(x.value));
}
return list.sortBy('name','id');
}.property('allSecrets.[]','exclude.[]'),
return out.sortBy('group','label');
}),
selectedChanged: function() {
let id = this.get('selected');
selectedChanged: observer('selected', function() {
let id = get(this,'selected');
let str = null;
if ( id ) {
let secret = this.get('store').getById('secret',id);
let secret = get(this,'store').getById('secret',id);
if ( secret ) {
str = secret.get(this.get('valueKey'));
str = get(secret,get(this,'valueKey'));
}
}
this.set('value', str);
}.observes('selected'),
set(this,'value', str);
}),
});

View File

@ -1,6 +1,7 @@
<select class="{{selectClass}}" onchange={{action (mut selected) value="target.value"}}>
<option selected={{eq selected null}}>{{t 'schema.inputSecret.prompt'}}</option>
{{#each filtered as |secret|}}
<option selected={{eq selected secret.id}} value={{secret.id}}>{{secret.name}}</option>
{{/each}}
</select>
{{new-select
classNames="form-control"
content=filtered
prompt="schema.inputSecret.prompt"
localizedPrompt=true
value=seleted
}}

View File

@ -0,0 +1,7 @@
import Component from '@ember/component';
import layout from './template';
export default Component.extend({
layout,
tagName: '',
});

View File

@ -0,0 +1,10 @@
<p class="m-5">
<span class="mr-10">
{{~#if modifier ~}}
<code>{{t (concat 'modalShortcuts.' modifier) key=key}}</code>
{{~else~}}
<code>{{key}}</code>
{{~/if~}}
</span>
{{t label}}
</p>

View File

@ -1 +1 @@
{{yield}}
{{~yield~}}

View File

@ -11,7 +11,7 @@
<td data-title="{{dt.name}}" class="clip">
<a href="{{href-to "workload" model.id}}">{{model.displayName}} <i class="{{model.activeIcon}}"></i></a>
{{#if model.showTransitioningMessage}}
<div class="clip text-small {{model.stateColor}}">{{model.transitioningMessage}}</div>
<div class="clip text-small {{model.stateColor}}">{{uc-first model.transitioningMessage}}</div>
{{else if model.displayEndpoints}}
<div class="clip text-small">
{{model.displayEndpoints}}

View File

@ -1,136 +0,0 @@
import Mixin from '@ember/object/mixin';
import Util from 'ui/utils/util';
import C from 'ui/utils/constants';
import { computed, observer } from '@ember/object';
export default Mixin.create({
reservedKeys: ['delayTimer','pollTimer'],
replaceWith: function() {
this._super.apply(this,arguments);
this.transitioningChanged();
},
// ember-api-store hook
wasAdded: function() {
this.transitioningChanged();
},
// ember-api-store hook
wasRemoved: function() {
this.transitioningChanged();
},
delayTimer: null,
clearDelay: function() {
clearTimeout(this.get('delayTimer'));
this.set('delayTimer', null);
},
pollTimer: null,
clearPoll: function() {
clearTimeout(this.get('pollTimer'));
this.set('pollTimer', null);
},
needsPolling: computed('transitioning','state', function() {
return ( this.get('transitioning') === 'yes' ) ||
( this.get('state') === 'requested' );
}),
remove: function() {
return this._super().finally(() => {
this.reload();
});
},
transitioningChanged: observer('transitioning', function() {
var delay = this.constructor.pollTransitioningDelay;
var interval = this.constructor.pollTransitioningInterval;
// This resource doesn't want polling
if ( !delay || !interval )
{
return;
}
// This resource isn't transitioning or isn't in the store
if ( !this.get('needsPolling') || !this.isInStore() )
{
this.clearPoll();
this.clearDelay();
return;
}
// We're already polling or waiting, just let that one finish
if ( this.get('delayTimer') )
{
return;
}
this.set('delayTimer', setTimeout(function() {
this.transitioningPoll();
}.bind(this), Util.timerFuzz(delay)));
}),
reloadOpts: computed(function() {
return null;
}),
transitioningPoll: function() {
this.clearPoll();
if ( !this.get('needsPolling') || !this.isInStore() )
{
return;
}
//console.log('Polling', this.toString());
this.reload(this.get('reloadOpts')).then(() => {
//console.log('Poll Finished', this.toString());
if ( this.get('needsPolling') )
{
let interval = this.constructor.pollTransitioningInterval;
let factor = this.constructor.pollTransitioningIntervalFactor;
if ( factor )
{
interval *= factor;
}
let max = this.constructor.pollTransitioningIntervalMax;
if ( max )
{
interval = Math.min(max,interval);
}
//console.log('Rescheduling', this.toString());
this.set('pollTimer', setTimeout(function() {
//console.log('2 expired', this.toString());
this.transitioningPoll();
}.bind(this), Util.timerFuzz(interval)));
}
else
{
// If not transitioning anymore, stop polling
this.clearPoll();
this.clearDelay();
}
}).catch(() => {
// If reloading fails, stop polling
this.clearPoll();
// but leave delay set so that it doesn't restart, (don't clearDelay())
});
},
stateChanged: observer('state', function() {
// Get rid of things that are removed
if ( C.REMOVEDISH_STATES.includes(this.state) ) {
try {
this.clearPoll();
this.clearDelay();
this.get('store')._remove(this.get('type'), this);
} catch (e) {
}
}
}),
});

View File

@ -1,46 +0,0 @@
import { cancel, later } from '@ember/runloop';
import { inject as service } from '@ember/service';
import Mixin from '@ember/object/mixin';
export default Mixin.create({
pollInterval: 2000,
growl: service(),
pollTimer: null,
setupController() {
this._super(...arguments);
this.scheduleTimer();
},
deactivate() {
this.cancelTimer();
},
scheduleTimer() {
cancel(this.get('pollTimer'));
this.set('pollTimer', later(() => {
let controller = this.controller;
let qp = {};
(controller.get('queryParams')||[]).forEach((param) => {
qp[param] = controller.get(param);
});
this.model(qp).then((model) => {
this.controller.set('model', model);
if ( this.get('pollTimer') ) {
this.scheduleTimer();
}
}).catch((err) => {
this.get('growl').fromError(err);
});
}, this.get('pollInterval')));
},
cancelTimer() {
cancel(this.get('pollTimer'));
// This prevents scheduleTimer from rescheduling if deactivate happened at just the wrong time.
this.set('pollTimer', null);
}
});

View File

@ -0,0 +1 @@
export { default } from 'shared/components/shortcut-key/component';

View File

@ -1 +0,0 @@
export { default } from 'shared/mixins/cattle-polled-resource';

View File

@ -1 +0,0 @@
export { default } from 'shared/mixins/polled-model';

View File

@ -66,6 +66,7 @@ generic:
paste: Paste
port: Port
ports: Ports
project: Project
remove: Remove
role: Role
random: Random
@ -724,7 +725,7 @@ clusterDashboard:
scheduler: Scheduler
controllerManager: Controller Manager
value: Value
notActive: This cluster is currently {state}. Some operations like managing namespaces will not be available until it is Active.
notActive: This cluster is currently in <b>{state}</b> state. Areas that require communicating with the cluster will be unavailable until it becomes Active.
noNodes: There are no nodes.
alert:
node: "Alert: Node {node} is not active."
@ -4261,25 +4262,18 @@ modalShortcuts:
title:
rancher: "WELCOME TO WARP ZONE!"
pl: Keyboard Shortcuts
navigation: Page Navigation
a: Apps
b: Balancers
c: Containers
d: DNS
e: Manage Clusters & Environments
h: Nodes
k: API Keys
global: Global
cluster: Cluster
project: Project
other: Other Stuff
g: Toggle view grouping
n: "Add a new [the current resource]"
s: Toggle show system
t: Next page tab
v: Volumes
slash: Focus the search field
question: You are here
admin: Only For Admins
p: Processes
theme: Cycle theme ({currentTheme})
shift: Shift-{key}
delete: Delete selected (in table views)
shell: Shell
viewGroup: Toggle view grouping
nextTab: Next page tab
search: Focus the search field
shortcuts: You are here
modalWelcome:
header: Welcome to {appName}!
@ -5237,7 +5231,7 @@ nav:
containers: Workloads
dns: Service Discovery
volumes: Volumes
k8s: Advanced
pipelines: Pipelines
deploy: Deploy
addContainer: Add Container
addBalancer: Add Balancer