diff --git a/app/models/cluster.js b/app/models/cluster.js index c5d4e3eb6..a41a8a0ac 100644 --- a/app/models/cluster.js +++ b/app/models/cluster.js @@ -2,7 +2,7 @@ import { get, set, computed, observer } from '@ember/object'; import { on } from '@ember/object/evented'; import { inject as service } from '@ember/service'; import Resource from '@rancher/ember-api-store/models/resource'; -import { hasMany } from '@rancher/ember-api-store/utils/denormalize'; +import { hasMany, reference } from '@rancher/ember-api-store/utils/denormalize'; import ResourceUsage from 'shared/mixins/resource-usage'; import Grafana from 'shared/mixins/grafana'; import { equal, alias } from '@ember/object/computed'; @@ -11,6 +11,7 @@ import C from 'ui/utils/constants'; import { isEmpty } from '@ember/utils'; import moment from 'moment'; const TRUE = 'True'; +const CLUSTER_TEMPLATE_ID_PREFIX = 'cattle-global-data:'; export default Resource.extend(Grafana, ResourceUsage, { globalStore: service(), @@ -29,6 +30,8 @@ export default Resource.extend(Grafana, ResourceUsage, { grafanaDashboardName: 'Cluster', isMonitoringReady: false, _cachedConfig: null, + clusterTemplate: reference('clusterTemplateId'), + clusterTemplateRevision: reference('clusterTemplateRevisionId'), machines: alias('nodes'), roleTemplateBindings: alias('clusterRoleTemplateBindings'), isAKS: equal('driver', 'azureKubernetesService'), @@ -49,6 +52,25 @@ export default Resource.extend(Grafana, ResourceUsage, { } })), + clusterTemplateDisplayName: computed('clusterTemplate.name', 'clusterTemplateId', function() { + return get(this, 'clusterTemplate.displayName') + || get(this, 'clusterTemplateId').replace(CLUSTER_TEMPLATE_ID_PREFIX, ''); + }), + + clusterTemplateRevisionDisplayName: computed('clusterTemplateRevision.name', 'clusterTemplateRevisionId', function() { + return get(this, 'clusterTemplateRevision.displayName') + || get(this, 'clusterTemplateRevisionId').replace(CLUSTER_TEMPLATE_ID_PREFIX, ''); + }), + + isClusterTemplateUpgradeAvailable: computed('clusterTemplate.latestRevision.id', 'clusterTemplateRevision.id', function() { + const latestClusterTemplateRevisionId = get(this, 'clusterTemplate.latestRevision.id'); + const currentClusterTemplateRevisionId = get(this, 'clusterTemplateRevision.id'); + + return latestClusterTemplateRevisionId + && currentClusterTemplateRevisionId + && currentClusterTemplateRevisionId !== latestClusterTemplateRevisionId; + }), + getAltActionDelete: computed('action.remove', function() { // eslint-disable-line return get(this, 'canBulkRemove') ? 'delete' : null; }), diff --git a/app/models/clustertemplate.js b/app/models/clustertemplate.js index 50f1fae13..fc0262b8a 100644 --- a/app/models/clustertemplate.js +++ b/app/models/clustertemplate.js @@ -30,6 +30,12 @@ const ClusterTemplate = Resource.extend({ return isNaN(get(this, 'revisions.length')) ? 0 : get(this, 'revisions.length'); }), + latestRevision: computed('revisions.[]', function() { + return isNaN(get(this, 'revisions.length')) + ? null + : get(this, 'revisions').sortBy('createdTS').get('lastObject'); + }), + displayDefaultRevisionId: computed('revisionsCount', 'revisions.[]', function() { return get(this, 'defaultRevisionId').split(':')[1]; }), diff --git a/app/styles/_rancher.scss b/app/styles/_rancher.scss index 333431b11..4ad8dc45f 100644 --- a/app/styles/_rancher.scss +++ b/app/styles/_rancher.scss @@ -62,6 +62,7 @@ @import "app/styles/components/logging"; @import "app/styles/components/identity-block"; @import "app/styles/components/istio-graph"; +@import "app/styles/components/cluster-template-revision-upgrade-notification"; // Vendor // Pretty much what it says. Vendor specific changes/overrides or includes. diff --git a/app/styles/base/_icons.scss b/app/styles/base/_icons.scss index af8ad711e..11c6adc99 100644 --- a/app/styles/base/_icons.scss +++ b/app/styles/base/_icons.scss @@ -64,7 +64,7 @@ $icon-inverse: #fff !default; .icon-stack { position: relative; display: inline-block; - // width: 2em; + width: 2em; height: 2em; line-height: 2em; vertical-align: middle; diff --git a/app/styles/components/_banners.scss b/app/styles/components/_banners.scss index ddbb3b5a9..942a58fd6 100644 --- a/app/styles/components/_banners.scss +++ b/app/styles/components/_banners.scss @@ -190,5 +190,12 @@ .block .text-small { font-size: 0.7em; } + + span.cluster-template-revision-upgrade-notification { + height: 0px; + display: inline-block; + margin-top: -1.5%; + vertical-align: top; + } } } diff --git a/app/styles/components/_cluster-template-revision-upgrade-notification.scss b/app/styles/components/_cluster-template-revision-upgrade-notification.scss new file mode 100644 index 000000000..ce065c338 --- /dev/null +++ b/app/styles/components/_cluster-template-revision-upgrade-notification.scss @@ -0,0 +1,17 @@ +.cluster-template-revision-upgrade-notification { + .icon-stack { + color: $banner-warning; + + .icon-notification, .icon-circle-o { + color: lighten($warning, 5); + } + + .icon-circle, .icon-circle-o { + font-size: 1.45em; + } + + .icon-notification { + font-size: 0.85em; + } + } +} \ No newline at end of file diff --git a/app/styles/components/_tables.scss b/app/styles/components/_tables.scss index 8d0d0bd4a..bf1ec3d1a 100644 --- a/app/styles/components/_tables.scss +++ b/app/styles/components/_tables.scss @@ -98,9 +98,15 @@ TABLE { text-align: center; } - &.sortable.text-right A { - position: relative; - left: -15px; + &.sortable { + &.text-right A { + position: relative; + left: -15px; + } + + .icon-stack .icon-stack-1x { + width: initial; + } } a { diff --git a/lib/global-admin/addon/clusters/index/route.js b/lib/global-admin/addon/clusters/index/route.js index 431003953..5ff521245 100644 --- a/lib/global-admin/addon/clusters/index/route.js +++ b/lib/global-admin/addon/clusters/index/route.js @@ -1,8 +1,21 @@ import { next } from '@ember/runloop'; +import { hash } from 'rsvp'; +import { inject as service } from '@ember/service'; +import { get } from '@ember/object'; import Route from '@ember/routing/route'; export default Route.extend({ + globalStore: service(), + shortcuts: { 'g': 'toggleGrouping', }, + + afterModel() { + return hash( + get(this, 'globalStore').findAll('clusterTemplateRevision'), + get(this, 'globalStore').findAll('clusterTemplate'), + ); + }, + actions: { toggleGrouping() { let choices = ['list', 'grouped']; diff --git a/lib/global-admin/addon/components/cluster-row/template.hbs b/lib/global-admin/addon/components/cluster-row/template.hbs index 5727e4f36..bb3435694 100644 --- a/lib/global-admin/addon/components/cluster-row/template.hbs +++ b/lib/global-admin/addon/components/cluster-row/template.hbs @@ -34,6 +34,8 @@ {{/tooltip-element}} {{/if}} + + {{cluster-template-revision-upgrade-notification cluster=model}} {{#if model.version.gitVersion}} diff --git a/lib/monitoring/addon/components/cluster-basic-info/template.hbs b/lib/monitoring/addon/components/cluster-basic-info/template.hbs index 43a42a3ed..8d970e7a1 100644 --- a/lib/monitoring/addon/components/cluster-basic-info/template.hbs +++ b/lib/monitoring/addon/components/cluster-basic-info/template.hbs @@ -5,10 +5,16 @@
- + {{cluster.version.gitVersion}}
+
+ + {{cluster.clusterTemplateDisplayName}}/{{cluster.clusterTemplateRevisionDisplayName}} + {{cluster-template-revision-upgrade-notification cluster=cluster}} +
+
{{date-calendar cluster.created}} diff --git a/lib/monitoring/addon/index/route.js b/lib/monitoring/addon/index/route.js index 4d3c7d181..83e6a4376 100644 --- a/lib/monitoring/addon/index/route.js +++ b/lib/monitoring/addon/index/route.js @@ -45,6 +45,13 @@ export default Route.extend({ }); }, + afterModel() { + return hash( + get(this, 'globalStore').findAll('clusterTemplateRevision'), + get(this, 'globalStore').findAll('clusterTemplate'), + ); + }, + setDefaultRoute: on('activate', function() { set(this, `session.${ C.SESSION.CLUSTER_ROUTE }`, 'authenticated.cluster.monitoring.index'); }), diff --git a/lib/shared/addon/components/cluster-template-revision-upgrade-notification/component.js b/lib/shared/addon/components/cluster-template-revision-upgrade-notification/component.js new file mode 100644 index 000000000..b6ece51f1 --- /dev/null +++ b/lib/shared/addon/components/cluster-template-revision-upgrade-notification/component.js @@ -0,0 +1,10 @@ +import Component from '@ember/component'; +import layout from './template'; + +export default Component.extend({ + layout, + tagName: 'span', + classNames: ['cluster-template-revision-upgrade-notification'], + + cluster: null, +}); diff --git a/lib/shared/addon/components/cluster-template-revision-upgrade-notification/template.hbs b/lib/shared/addon/components/cluster-template-revision-upgrade-notification/template.hbs new file mode 100644 index 000000000..94946f13b --- /dev/null +++ b/lib/shared/addon/components/cluster-template-revision-upgrade-notification/template.hbs @@ -0,0 +1,14 @@ +{{#if cluster.isClusterTemplateUpgradeAvailable}} + {{#tooltip-element + type="tooltip-basic" + model=(t 'clusterTemplateRevisionUpgradeNotification.tooltip' revision=cluster.clusterTemplate.latestRevision.displayName) + tooltipTemplate="tooltip-static" + inlineBlock=true + }} + + + + + + {{/tooltip-element}} +{{/if}} \ No newline at end of file diff --git a/lib/shared/app/components/cluster-template-revision-upgrade-notification/component.js b/lib/shared/app/components/cluster-template-revision-upgrade-notification/component.js new file mode 100644 index 000000000..5b0a7ef23 --- /dev/null +++ b/lib/shared/app/components/cluster-template-revision-upgrade-notification/component.js @@ -0,0 +1 @@ +export { default } from 'shared/components/cluster-template-revision-upgrade-notification/component'; \ No newline at end of file diff --git a/translations/en-us.yaml b/translations/en-us.yaml index 02aac35f7..e6f19d5f3 100644 --- a/translations/en-us.yaml +++ b/translations/en-us.yaml @@ -892,6 +892,9 @@ projectsPage: memberNameReq: Name is required for a member memberRoleReq: Role is required for a member +clusterTemplateRevisionUpgradeNotification: + tooltip: Revision {revision} available for upgrade. + clustersPage: header: Clusters newCluster: Add Cluster @@ -903,10 +906,14 @@ clustersPage: new: Add Cluster cluster: label: Cluster Name + templateRevision: + label: Template/Revision provider: label: Provider - version: - label: Version + kubernetesVersion: + label: Kubernetes Version + rkeTemplate: + label: RKE Template nodes: label: Nodes cpu: