@rancher/shell 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/assets/translations/en-us.yaml +8 -2
  2. package/assets/translations/zh-hans.yaml +158 -25
  3. package/components/auth/RoleDetailEdit.vue +14 -1
  4. package/components/form/ResourceTabs/index.vue +27 -18
  5. package/components/formatter/ClusterLink.vue +13 -0
  6. package/components/formatter/PodImages.vue +11 -1
  7. package/components/formatter/RKETemplateName.vue +37 -0
  8. package/config/types.js +3 -1
  9. package/creators/app/tsconfig.json +6 -1
  10. package/creators/pkg/init +3 -0
  11. package/creators/pkg/tsconfig.json +7 -2
  12. package/detail/provisioning.cattle.io.cluster.vue +10 -0
  13. package/edit/cloudcredential.vue +7 -1
  14. package/edit/networking.k8s.io.ingress/index.vue +2 -1
  15. package/edit/persistentvolumeclaim.vue +32 -2
  16. package/edit/provisioning.cattle.io.cluster/MachinePool.vue +1 -1
  17. package/edit/provisioning.cattle.io.cluster/index.vue +1 -1
  18. package/edit/provisioning.cattle.io.cluster/rke2.vue +21 -6
  19. package/edit/workload/types/Deployment.vue +1 -1
  20. package/edit/workload/types/Generic.vue +1 -1
  21. package/list/provisioning.cattle.io.cluster.vue +6 -0
  22. package/mixins/brand.js +3 -4
  23. package/mixins/create-edit-view/impl.js +0 -8
  24. package/models/chart.js +1 -1
  25. package/models/cluster/node.js +12 -1
  26. package/models/management.cattle.io.globalrole.js +0 -19
  27. package/models/management.cattle.io.roletemplate.js +2 -21
  28. package/models/provisioning.cattle.io.cluster.js +67 -0
  29. package/models/service.js +5 -1
  30. package/nuxt.config.js +3 -3
  31. package/package.json +1 -1
  32. package/pages/c/_cluster/apps/charts/install.vue +0 -6
  33. package/plugins/console.js +10 -5
  34. package/plugins/dashboard-store/actions.js +8 -3
  35. package/plugins/dashboard-store/getters.js +7 -2
  36. package/plugins/steve/steve-description-class.js +32 -0
  37. package/rancher-components/Banner/Banner.vue +2 -2
  38. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +0 -2
  39. package/rancher-components/Form/LabeledInput/LabeledInput.vue +2 -0
  40. package/rancher-components/Form/Radio/RadioButton.vue +14 -1
  41. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.test.ts +107 -0
  42. package/{components/form → rancher-components/Form/ToggleSwitch}/ToggleSwitch.vue +18 -8
  43. package/rancher-components/Form/ToggleSwitch/index.ts +1 -0
  44. package/rancher-components/Form/index.ts +1 -0
  45. package/scripts/test-plugins-build.sh +18 -1
  46. package/types/{index.d.ts → rancher/index.d.ts} +0 -0
  47. package/plugins/lookup.js +0 -50
  48. package/yarn-error.log +0 -196
@@ -36,6 +36,17 @@ export default class ProvCluster extends SteveModel {
36
36
  },
37
37
  ].filter(x => !!x.content);
38
38
 
39
+ // RKE Template details
40
+ const rkeTemplate = this.rkeTemplate;
41
+
42
+ if (rkeTemplate) {
43
+ out.push({
44
+ label: this.t('cluster.detail.rkeTemplate'),
45
+ formatter: 'RKETemplateName',
46
+ content: rkeTemplate,
47
+ });
48
+ }
49
+
39
50
  if (!this.machineProvider) {
40
51
  out.splice(1, 1);
41
52
 
@@ -597,6 +608,62 @@ export default class ProvCluster extends SteveModel {
597
608
  return this._stateObj;
598
609
  }
599
610
 
611
+ get rkeTemplate() {
612
+ if (!this.isRke1 || !this.mgmt) {
613
+ // Not an RKE! cluster or no management cluster available
614
+ return false;
615
+ }
616
+
617
+ if (!this.mgmt.spec?.clusterTemplateRevisionName) {
618
+ // Cluster does not use an RKE template
619
+ return false;
620
+ }
621
+
622
+ const clusterTemplateName = this.mgmt.spec.clusterTemplateName.replace(':', '/');
623
+ const clusterTemplateRevisionName = this.mgmt.spec.clusterTemplateRevisionName.replace(':', '/');
624
+ const template = this.$rootGetters['management/all'](MANAGEMENT.RKE_TEMPLATE).find(t => t.id === clusterTemplateName);
625
+ const revision = this.$rootGetters['management/all'](MANAGEMENT.RKE_TEMPLATE_REVISION).find(t => t.spec.enabled && t.id === clusterTemplateRevisionName);
626
+
627
+ if (!template || !revision) {
628
+ return false;
629
+ }
630
+
631
+ return {
632
+ displayName: `${ template.spec?.displayName }/${ revision.spec?.displayName }`,
633
+ upgrade: this.rkeTemplateUpgrade,
634
+ template,
635
+ revision,
636
+ };
637
+ }
638
+
639
+ get rkeTemplateUpgrade() {
640
+ if (!this.isRke1 || !this.mgmt) {
641
+ // Not an RKE! cluster or no management cluster available
642
+ return false;
643
+ }
644
+
645
+ if (!this.mgmt.spec?.clusterTemplateRevisionName) {
646
+ // Cluster does not use an RKE template
647
+ return false;
648
+ }
649
+
650
+ const clusterTemplateRevisionName = this.mgmt.spec.clusterTemplateRevisionName.replace(':', '/');
651
+
652
+ // Get all of the template revisions for this template
653
+ const revisions = this.$rootGetters['management/all'](MANAGEMENT.RKE_TEMPLATE_REVISION).filter(t => t.spec.enabled && t.spec.clusterTemplateName === this.mgmt.spec.clusterTemplateName);
654
+
655
+ if (revisions.length <= 1) {
656
+ // Only one template revision
657
+ return false;
658
+ }
659
+
660
+ revisions.sort((a, b) => {
661
+ return parseInt(a.metadata.resourceVersion, 10) - parseInt(b.metadata.resourceVersion, 10);
662
+ }).reverse();
663
+
664
+ return revisions[0].id !== clusterTemplateRevisionName ? revisions[0].spec?.displayName : false;
665
+ }
666
+
600
667
  get _stateObj() {
601
668
  if (!this.isRke2) {
602
669
  return this.mgmt?.stateObj || this.metadata?.state;
package/models/service.js CHANGED
@@ -136,7 +136,11 @@ export default class extends SteveModel {
136
136
  let pods = [];
137
137
 
138
138
  if (podRelationship) {
139
- pods = await this.$dispatch('cluster/findMatching', { type: POD, selector: podRelationship.selector }, { root: true });
139
+ pods = await this.$dispatch('cluster/findMatching', {
140
+ type: POD,
141
+ selector: podRelationship.selector,
142
+ namespace: this.namespace
143
+ }, { root: true });
140
144
  }
141
145
 
142
146
  return pods;
package/nuxt.config.js CHANGED
@@ -305,8 +305,9 @@ export default function(dir, _appConfig) {
305
305
  },
306
306
 
307
307
  router: {
308
- base: routerBasePath,
309
- middleware: ['i18n'],
308
+ base: routerBasePath,
309
+ middleware: ['i18n'],
310
+ prefetchLinks: false
310
311
  },
311
312
 
312
313
  alias: {
@@ -570,7 +571,6 @@ export default function(dir, _appConfig) {
570
571
  path.join(NUXT_SHELL, 'plugins/global-formatters'),
571
572
  path.join(NUXT_SHELL, 'plugins/trim-whitespace'),
572
573
  { src: path.join(NUXT_SHELL, 'plugins/extend-router') },
573
- { src: path.join(NUXT_SHELL, 'plugins/lookup'), ssr: false },
574
574
  { src: path.join(NUXT_SHELL, 'plugins/console'), ssr: false },
575
575
  { src: path.join(NUXT_SHELL, 'plugins/int-number'), ssr: false },
576
576
  { src: path.join(NUXT_SHELL, 'plugins/nuxt-client-init'), ssr: false },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rancher/shell",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Rancher Dashboard Shell",
5
5
  "repository": "https://github.com/rancherlabs/dashboard",
6
6
  "license": "Apache-2.0",
@@ -740,12 +740,6 @@ export default {
740
740
 
741
741
  window.scrollTop = 0;
742
742
 
743
- // For easy access debugging...
744
- if ( typeof window !== 'undefined' ) {
745
- window.v = this.value;
746
- window.c = this;
747
- }
748
-
749
743
  this.preFormYamlOption = this.valuesComponent || this.hasQuestions ? VALUES_STATE.FORM : VALUES_STATE.YAML;
750
744
  },
751
745
 
@@ -1,12 +1,17 @@
1
1
  /* eslint-disable no-console */
2
- export default (context) => {
3
- const logTypes = ['log', 'error', 'info', 'warn'];
2
+ export default () => {
3
+ const logTypes = ['warn', 'error'];
4
4
  const MAX_LOGS_STORED = 400;
5
5
 
6
- console.logLog = console.log.bind(console);
7
- console.errorLog = console.error.bind(console);
8
- console.infoLog = console.info.bind(console);
6
+ if (!process.env.dev) {
7
+ console.logLog = console.log.bind(console);
8
+ console.infoLog = console.info.bind(console);
9
+ logTypes.push('log');
10
+ logTypes.push('info');
11
+ }
12
+
9
13
  console.warnLog = console.warn.bind(console);
14
+ console.errorLog = console.error.bind(console);
10
15
  console.logs = [];
11
16
 
12
17
  logTypes.forEach((type) => {
@@ -319,7 +319,12 @@ export default {
319
319
  return all;
320
320
  },
321
321
 
322
- async findMatching(ctx, { type, selector, opt }) {
322
+ async findMatching(ctx, {
323
+ type,
324
+ selector,
325
+ opt,
326
+ namespace
327
+ }) {
323
328
  const {
324
329
  getters, commit, dispatch, rootGetters
325
330
  } = ctx;
@@ -332,7 +337,7 @@ export default {
332
337
  commit('registerType', type);
333
338
  }
334
339
  if ( opt.force !== true && getters['haveSelector'](type, selector) ) {
335
- return getters.matching( type, selector );
340
+ return getters.matching( type, selector, namespace );
336
341
  }
337
342
 
338
343
  const typeOptions = rootGetters['type-map/optionsFor'](type);
@@ -366,7 +371,7 @@ export default {
366
371
  });
367
372
  }
368
373
 
369
- return getters.matching( type, selector );
374
+ return getters.matching( type, selector, namespace );
370
375
  },
371
376
 
372
377
  // opt:
@@ -25,8 +25,13 @@ export default {
25
25
  return state.types[type].list;
26
26
  },
27
27
 
28
- matching: (state, getters) => (type, selector) => {
29
- const all = getters['all'](type);
28
+ matching: (state, getters) => (type, selector, namespace) => {
29
+ let all = getters['all'](type);
30
+
31
+ // Filter first by namespace if one is provided, since this is efficient
32
+ if (namespace) {
33
+ all = all.filter(obj => obj.namespace === namespace);
34
+ }
30
35
 
31
36
  return all.filter((obj) => {
32
37
  return matches(obj, selector);
@@ -0,0 +1,32 @@
1
+ import SteveModel from './steve-class';
2
+
3
+ /**
4
+ * SteveModel that supports the description being in the root 'description' property.
5
+ */
6
+ export default class SteveDescriptionModel extends SteveModel {
7
+ // Preserve description
8
+ constructor(data, ctx, rehydrateNamespace = null, setClone = false) {
9
+ const _description = data.description;
10
+
11
+ super(data, ctx, rehydrateNamespace, setClone);
12
+ this.description = _description;
13
+ }
14
+
15
+ get description() {
16
+ return this._description;
17
+ }
18
+
19
+ set description(value) {
20
+ this._description = value;
21
+ }
22
+
23
+ // Ensure when we clone that we preserve the desription
24
+ toJSON() {
25
+ const data = super.toJSON();
26
+
27
+ data.description = this.description;
28
+ delete data._description;
29
+
30
+ return data;
31
+ }
32
+ }
@@ -131,8 +131,8 @@ export default Vue.extend({
131
131
  }
132
132
 
133
133
  &.secondary {
134
- background: var(--secondary-banner-bg);
135
- border-left: solid $left-border-size var(--secondary);
134
+ background: var(--default-banner-bg);
135
+ border-left: solid $left-border-size var(--default);
136
136
  color: var(--body-text);
137
137
  }
138
138
 
@@ -1,8 +1,6 @@
1
1
 
2
2
  import { mount } from '@vue/test-utils';
3
3
  import { LabeledInput } from './index';
4
- import Vue from 'vue';
5
- import Vuex from 'vuex';
6
4
 
7
5
  describe('component: LabeledInput', () => {
8
6
  it('should emit input only once', () => {
@@ -126,6 +126,8 @@ export default (
126
126
  if (this.hasTooltip) {
127
127
  return this.tooltipKey ? this.t(this.tooltipKey) : this.tooltip
128
128
  }
129
+
130
+ return undefined;
129
131
  },
130
132
 
131
133
  /**
@@ -88,7 +88,14 @@ export default Vue.extend({
88
88
  muteLabel(): boolean {
89
89
  // Don't mute the label if the mode is view and the button is checked
90
90
  return this.disabled && !(this.mode === _VIEW && this.isChecked);
91
- }
91
+ },
92
+
93
+ /**
94
+ * Determines if the description slot is in use.
95
+ */
96
+ hasDescriptionSlot(): boolean {
97
+ return !!this.$slots.description;
98
+ },
92
99
  },
93
100
 
94
101
  watch: {
@@ -155,6 +162,12 @@ export default Vue.extend({
155
162
  {{ description }}
156
163
  </template>
157
164
  </div>
165
+ <div
166
+ v-else-if="hasDescriptionSlot"
167
+ class="radio-button-outer-container-description"
168
+ >
169
+ <slot name="description" />
170
+ </div>
158
171
  </div>
159
172
  </label>
160
173
  </template>
@@ -0,0 +1,107 @@
1
+ import { shallowMount } from '@vue/test-utils';
2
+ import { ToggleSwitch } from './index';
3
+
4
+ describe('ToggleSwitch.vue', () => {
5
+ it('renders falsy by default', () => {
6
+ const wrapper = shallowMount(ToggleSwitch);
7
+
8
+ const toggleInput = wrapper.find('input[type="checkbox"]').element as HTMLInputElement
9
+
10
+ expect(toggleInput.checked).toBeFalsy();
11
+ });
12
+
13
+ it('renders a true value', () => {
14
+ const wrapper = shallowMount(
15
+ ToggleSwitch,
16
+ {
17
+ propsData: {
18
+ value: true
19
+ }
20
+ });
21
+
22
+ const toggleInput = wrapper.find('input[type="checkbox"]').element as HTMLInputElement
23
+
24
+ expect(toggleInput.checked).toBe(true);
25
+ });
26
+
27
+ it('updates from falsy to truthy when props change', async () => {
28
+ const wrapper = shallowMount(ToggleSwitch);
29
+
30
+ const toggleInput = wrapper.find('input[type="checkbox"]').element as HTMLInputElement
31
+
32
+ expect(toggleInput.checked).toBe(false);
33
+
34
+ await wrapper.setProps({ value: true });
35
+
36
+ expect(toggleInput.checked).toBe(true);
37
+ });
38
+
39
+ it('emits an input event with a true value', async () => {
40
+ const wrapper = shallowMount(ToggleSwitch);
41
+
42
+ (wrapper.vm as any).toggle(true);
43
+
44
+ await wrapper.vm.$nextTick();
45
+
46
+ expect(wrapper.emitted().input?.length).toBe(1);
47
+ expect(wrapper.emitted().input?.[0][0]).toBe(true);
48
+
49
+ });
50
+
51
+ it('emits an input event with a false value', async () => {
52
+ const wrapper = shallowMount(
53
+ ToggleSwitch,
54
+ {
55
+ propsData: {
56
+ value: true
57
+ }
58
+ }
59
+ );
60
+
61
+ (wrapper.vm as any).toggle(false);
62
+
63
+ await wrapper.vm.$nextTick();
64
+
65
+ expect(wrapper.emitted().input?.length).toBe(1);
66
+ expect(wrapper.emitted().input?.[0][0]).toBe(false);
67
+ })
68
+
69
+ it('emits an input event with a custom onValue', async () => {
70
+ const onValue = 'THE TRUTH';
71
+
72
+ const wrapper = shallowMount(
73
+ ToggleSwitch,
74
+ {
75
+ propsData: {
76
+ onValue,
77
+ }
78
+ });
79
+
80
+ (wrapper.vm as any).toggle(true);
81
+
82
+ await wrapper.vm.$nextTick();
83
+
84
+ expect(wrapper.emitted().input?.length).toBe(1);
85
+ expect(wrapper.emitted().input?.[0][0]).toBe(onValue);
86
+ });
87
+
88
+ it('emits an input event with a custom offValue', async () => {
89
+ const offValue = 'NOT THE TRUTH';
90
+
91
+ const wrapper = shallowMount(
92
+ ToggleSwitch,
93
+ {
94
+ propsData: {
95
+ value: true,
96
+ offValue,
97
+ }
98
+ });
99
+
100
+ (wrapper.vm as any).toggle(false);
101
+
102
+ await wrapper.vm.$nextTick();
103
+
104
+ expect(wrapper.emitted().input?.length).toBe(1);
105
+ expect(wrapper.emitted().input?.[0][0]).toBe(offValue);
106
+ })
107
+ });
@@ -1,18 +1,19 @@
1
- <script>
2
- export default {
1
+ <script lang="ts">
2
+ import Vue from 'vue';
3
+ export default Vue.extend({
3
4
  props: {
4
5
  value: {
5
- type: null,
6
+ type: [Boolean, String, Number],
6
7
  default: false
7
8
  },
8
9
 
9
10
  offValue: {
10
- type: null,
11
+ type: [Boolean, String, Number],
11
12
  default: false,
12
13
  },
13
14
 
14
15
  onValue: {
15
- type: null,
16
+ type: [Boolean, String, Number],
16
17
  default: true,
17
18
  },
18
19
 
@@ -27,16 +28,25 @@ export default {
27
28
  },
28
29
  },
29
30
  data() {
30
- return { state: this.value === this.onValue };
31
+ return { state: false as boolean | string | number };
31
32
  },
32
33
 
33
34
  methods: {
34
- toggle(neu) {
35
+ toggle(neu: boolean | string | number) {
35
36
  this.state = neu === null ? !this.state : neu;
36
37
  this.$emit('input', this.state ? this.onValue : this.offValue);
37
38
  }
39
+ },
40
+
41
+ watch: {
42
+ value: {
43
+ handler() {
44
+ this.state = this.value === this.onValue;
45
+ },
46
+ immediate: true
47
+ }
38
48
  }
39
- };
49
+ });
40
50
  </script>
41
51
 
42
52
  <template>
@@ -0,0 +1 @@
1
+ export { default as ToggleSwitch } from './ToggleSwitch.vue';
@@ -2,3 +2,4 @@ export * from './Checkbox';
2
2
  export * from './LabeledInput';
3
3
  export * from './Radio';
4
4
  export * from './TextArea';
5
+ export * from './ToggleSwitch';
@@ -42,6 +42,12 @@ if [ $SKIP_SETUP == "false" ]; then
42
42
  sleep 10
43
43
 
44
44
  echo "Configuring Verdaccio user"
45
+
46
+ # Remove existing admin if already there
47
+ if [ -f ~/.config/verdaccio/htpasswd ]; then
48
+ sed -i '/^admin:/d' ~/.config/verdaccio/htpasswd
49
+ fi
50
+
45
51
  curl -XPUT -H "Content-type: application/json" -d '{ "name": "admin", "password": "admin" }' 'http://localhost:4873/-/user/admin' > login.json
46
52
  TOKEN=$(jq -r .token login.json)
47
53
  rm login.json
@@ -54,9 +60,20 @@ EOF
54
60
  fi
55
61
  fi
56
62
 
57
- rm -rf ~/.local/share/verdaccio/storage/@rancher/*
63
+ if [ -d ~/.local/share/verdaccio/storage/@rancher ]; then
64
+ rm -rf ~/.local/share/verdaccio/storage/@rancher/*
65
+ else
66
+ rm -rf ~/.config/verdaccio/storage/@rancher/*
67
+ fi
58
68
 
59
69
  export YARN_REGISTRY=http://localhost:4873
70
+ export NEXT_TELEMETRY_DISABLED=1
71
+
72
+ # We need to patch the version number of the shell, otherwise if we are running
73
+ # with the currently published version, things will fail as those versions
74
+ # are already published and Verdaccio will check, since it is a read-through cache
75
+ sed -i.bak -e "s/\"version\": \"[0-9]*.[0-9]*.[0-9]*\",/\"version\": \"7.7.7\",/g" ${SHELL_DIR}/package.json
76
+ rm ${SHELL_DIR}/package.json.bak
60
77
 
61
78
  # Publish shell
62
79
  echo "Publishing shell packages to local registry"
File without changes
package/plugins/lookup.js DELETED
@@ -1,50 +0,0 @@
1
- import { get, set } from '@shell/utils/object';
2
- import $ from 'jquery';
3
-
4
- const DEFAULT_NS = 'cluster';
5
-
6
- export default (context) => {
7
- window.$ = $;
8
- window.get = get;
9
- window.set = set;
10
-
11
- window.s = context.store;
12
-
13
- window.schemaName = (type, namespace = DEFAULT_NS) => {
14
- return context.store.getters[`${ namespace }/schemaName`](type);
15
- };
16
-
17
- window.schemaFor = (type, namespace = DEFAULT_NS) => {
18
- return context.store.getters[`${ namespace }/schemaFor`](type, true);
19
- };
20
-
21
- window.all = (type, namespace = DEFAULT_NS) => {
22
- const realType = window.schemaName(type, namespace);
23
-
24
- return context.store.getters[`${ namespace }/all`](realType);
25
- };
26
-
27
- window.byId = (type, id, namespace = DEFAULT_NS) => {
28
- const realType = window.schemaName(type, namespace);
29
-
30
- return context.store.getters[`${ namespace }/byId`](realType, id);
31
- };
32
-
33
- window.find = (type, id, namespace = DEFAULT_NS) => {
34
- const realType = window.schemaName(type, namespace);
35
-
36
- return context.store.dispatch(`${ namespace }/find`, {
37
- type: realType,
38
- id
39
- });
40
- };
41
-
42
- window.findAll = (type, namespace = DEFAULT_NS) => {
43
- const realType = window.schemaName(type, namespace);
44
-
45
- return context.store.dispatch(`${ namespace }/findAll`, { type: realType });
46
- };
47
-
48
- console.log('# Welcome to warp zone'); // eslint-disable-line no-console
49
- console.log("# Try schemaFor('pod'), schemaName('pod'), all('pod'), byId('pod','abc'), await find('pod','abc'), await findAll('pod')"); // eslint-disable-line no-console
50
- };