@rancher/shell 0.3.4 → 0.3.6
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.
- package/assets/images/providers/outscale.svg +19 -0
- package/assets/styles/app.scss +1 -1
- package/assets/styles/base/_basic.scss +18 -0
- package/assets/styles/base/_mixins.scss +0 -11
- package/assets/styles/base/_variables.scss +2 -4
- package/assets/styles/fonts/_fontstack.scss +11 -11
- package/assets/styles/global/_button.scss +12 -2
- package/assets/styles/vendor/vue-js-modal.scss +3 -3
- package/assets/translations/en-us.yaml +113 -22
- package/assets/translations/zh-hans.yaml +113 -24
- package/babel.config.js +13 -0
- package/chart/gatekeeper.vue +78 -0
- package/chart/istio.vue +135 -112
- package/chart/logging/index.vue +13 -4
- package/chart/monitoring/index.vue +15 -5
- package/chart/monitoring/steps/uninstall-v1.vue +2 -2
- package/chart/rancher-backup/index.vue +10 -3
- package/cloud-credential/aws.vue +1 -1
- package/cloud-credential/digitalocean.vue +1 -1
- package/cloud-credential/gcp.vue +1 -1
- package/cloud-credential/generic.vue +2 -2
- package/cloud-credential/linode.vue +1 -1
- package/cloud-credential/pnap.vue +1 -1
- package/components/ActionMenu.vue +3 -4
- package/components/AssignTo.vue +1 -1
- package/components/AsyncButton.vue +1 -1
- package/components/BannerGraphic.vue +1 -1
- package/components/BrandImage.vue +1 -4
- package/components/ButtonDropdown.vue +2 -3
- package/components/Carousel.vue +85 -37
- package/components/ChartPsp.vue +76 -0
- package/components/CruResource.vue +6 -2
- package/components/DashboardMetrics.vue +12 -10
- package/components/DetailText.vue +1 -1
- package/components/DisableAuthProviderModal.vue +1 -1
- package/components/EmberPage.vue +1 -1
- package/components/EtcdInfoBanner.vue +12 -7
- package/components/ExplorerMembers.vue +101 -6
- package/components/ExplorerProjectsNamespaces.vue +46 -3
- package/components/FileDiff.vue +6 -7
- package/components/GrafanaDashboard.vue +27 -23
- package/components/LazyImage.vue +10 -12
- package/components/LogItem.vue +1 -1
- package/components/Markdown.vue +1 -1
- package/components/PromptRemove.vue +2 -2
- package/components/PromptRestore.vue +1 -1
- package/components/ResourceDetail/Masthead.vue +16 -0
- package/components/ResourceDetail/index.vue +21 -4
- package/components/ResourceList/index.vue +1 -1
- package/components/ResourceTable.vue +4 -1
- package/components/SingleClusterInfo.vue +2 -2
- package/components/SortableTable/THead.vue +1 -1
- package/components/SortableTable/index.vue +28 -13
- package/components/SortableTable/selection.js +58 -50
- package/components/Wizard.vue +4 -2
- package/components/__tests__/AsyncButton.test.ts +3 -1
- package/components/__tests__/ChartPsp.test.ts +75 -0
- package/components/__tests__/CruResource.test.ts +3 -1
- package/components/auth/Principal.vue +1 -1
- package/components/auth/RoleDetailEdit.vue +2 -2
- package/components/fleet/FleetBundles.vue +3 -1
- package/components/fleet/FleetClusters.vue +1 -2
- package/components/fleet/FleetIntro.vue +9 -1
- package/components/fleet/FleetNoWorkspaces.vue +62 -0
- package/components/fleet/FleetSummary.vue +7 -1
- package/components/form/HookOption.vue +14 -10
- package/components/form/LabeledSelect.vue +14 -11
- package/components/form/Labels.vue +32 -27
- package/components/form/MatchExpressions.vue +19 -4
- package/components/form/Members/ClusterPermissionsEditor.vue +32 -7
- package/components/form/NameNsDescription.vue +32 -46
- package/components/form/ProjectMemberEditor.vue +46 -21
- package/components/form/ResourceSelector.vue +1 -1
- package/components/form/SecretSelector.vue +5 -1
- package/components/form/ServiceNameSelect.vue +1 -1
- package/components/form/SimpleSecretSelector.vue +9 -9
- package/components/form/Tolerations.vue +4 -1
- package/components/form/ValueFromResource.vue +14 -9
- package/components/form/WorkloadPorts.vue +2 -2
- package/components/form/__tests__/LabeledSelect.test.ts +138 -0
- package/components/form/__tests__/NameNsDescription.ts +59 -0
- package/components/formatter/InternalExternalIP.vue +6 -0
- package/components/formatter/InvolvedObjectLink.vue +54 -0
- package/components/formatter/Link.vue +20 -4
- package/components/formatter/LinkName.vue +6 -1
- package/components/formatter/ServiceTargets.vue +1 -1
- package/components/formatter/WorkloadHealthScale.vue +8 -2
- package/components/nav/Group.vue +2 -2
- package/components/nav/NamespaceFilter.vue +23 -11
- package/components/nav/TopLevelMenu.vue +2 -4
- package/components/nav/Type.vue +1 -1
- package/components/nav/WorkspaceSwitcher.vue +46 -5
- package/components/nuxt/nuxt-build-indicator.vue +143 -0
- package/components/nuxt/nuxt-child.js +122 -0
- package/components/nuxt/nuxt-error.vue +98 -0
- package/components/nuxt/nuxt-link.client.js +98 -0
- package/components/nuxt/nuxt-link.server.js +16 -0
- package/components/nuxt/nuxt-loading.vue +154 -0
- package/components/nuxt/nuxt.js +101 -0
- package/config/labels-annotations.js +17 -0
- package/config/middleware.js +12 -0
- package/config/product/auth.js +3 -2
- package/config/product/explorer.js +34 -6
- package/config/product/fleet.js +2 -0
- package/config/query-params.js +1 -0
- package/config/router.js +414 -0
- package/config/store.js +181 -0
- package/config/table-headers.js +54 -12
- package/config/types.js +18 -8
- package/config/uiplugins.js +30 -0
- package/content/docs/en-us/whats-new.md +10 -0
- package/content/docs/zh-hans/whats-new.md +11 -1
- package/core/plugin-routes.ts +23 -0
- package/core/plugin.ts +4 -2
- package/core/types.ts +258 -1
- package/creators/app/app.package.json +2 -1
- package/creators/app/files/.eslintrc.js +1 -1
- package/creators/app/files/babel.config.js +1 -18
- package/creators/app/files/tsconfig.json +0 -1
- package/creators/app/files/vue.config.js +6 -0
- package/creators/app/init +5 -5
- package/creators/pkg/files/.github/workflows/build-extension.yml +110 -0
- package/creators/pkg/files/tsconfig.json +0 -1
- package/creators/pkg/init +35 -4
- package/creators/pkg/pkg.package.json +3 -3
- package/creators/update/init +1 -1
- package/detail/constraints.gatekeeper.sh.constraint.vue +34 -17
- package/detail/fleet.cattle.io.clustergroup.vue +7 -1
- package/detail/fleet.cattle.io.gitrepo.vue +19 -11
- package/detail/harvesterhci.io.management.cluster.vue +3 -3
- package/detail/provisioning.cattle.io.cluster.vue +54 -12
- package/detail/workload/index.vue +3 -3
- package/dialog/AddClusterMemberDialog.vue +1 -1
- package/dialog/AddProjectMemberDialog.vue +2 -2
- package/dialog/AddonConfigConfirmationDialog.vue +27 -15
- package/dialog/DiagnosticTimingsDialog.vue +1 -1
- package/dialog/ForceMachineRemoveDialog.vue +1 -1
- package/dialog/GenericPrompt.vue +18 -6
- package/dialog/RotateEncryptionKeyDialog.vue +1 -1
- package/dialog/SaveAsRKETemplateDialog.vue +1 -1
- package/dialog/ScaleMachineDownDialog.vue +1 -1
- package/edit/auth/github.vue +8 -8
- package/edit/auth/googleoauth.vue +5 -5
- package/edit/auth/ldap/index.vue +1 -1
- package/edit/auth/oidc.vue +1 -1
- package/edit/auth/saml.vue +1 -1
- package/edit/cis.cattle.io.clusterscan.vue +1 -1
- package/edit/fleet.cattle.io.clustergroup.vue +6 -4
- package/edit/fleet.cattle.io.gitrepo.vue +32 -4
- package/edit/helm.cattle.io.projecthelmchart.vue +5 -1
- package/edit/logging.banzaicloud.io.output/index.vue +18 -5
- package/edit/logging.banzaicloud.io.output/providers/loki.vue +1 -0
- package/edit/management.cattle.io.fleetworkspace.vue +141 -6
- package/edit/management.cattle.io.podsecurityadmissionconfigurationtemplate.vue +4 -1
- package/edit/management.cattle.io.setting.vue +1 -1
- package/edit/monitoring.coreos.com.alertmanagerconfig/types/webhook.vue +2 -2
- package/edit/monitoring.coreos.com.receiver/tls.vue +18 -18
- package/edit/monitoring.coreos.com.receiver/types/webhook.banner.vue +4 -4
- package/edit/monitoring.coreos.com.receiver/types/webhook.vue +1 -1
- package/edit/namespace.vue +14 -10
- package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +126 -45
- package/edit/networking.k8s.io.networkpolicy/index.vue +1 -1
- package/edit/provisioning.cattle.io.cluster/MachinePool.vue +21 -4
- package/edit/provisioning.cattle.io.cluster/RegistryConfigs.vue +1 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +202 -2
- package/edit/provisioning.cattle.io.cluster/import.vue +23 -25
- package/edit/provisioning.cattle.io.cluster/rke2.vue +344 -102
- package/edit/resources.cattle.io.backup.vue +1 -1
- package/edit/service.vue +1 -1
- package/edit/storage.k8s.io.storageclass/provisioners/driver.harvesterhci.io.vue +2 -2
- package/edit/workload/__tests__/Job.test.ts +3 -1
- package/edit/workload/index.vue +8 -3
- package/edit/workload/mixins/workload.js +22 -7
- package/edit/workload/storage/Mount.vue +3 -3
- package/initialize/App.js +206 -0
- package/initialize/client.js +863 -0
- package/initialize/index.js +364 -0
- package/layouts/default.vue +7 -3
- package/layouts/standalone.vue +13 -0
- package/list/catalog.cattle.io.clusterrepo.vue +1 -0
- package/list/fleet.cattle.io.bundle.vue +6 -3
- package/list/fleet.cattle.io.clusterregistrationtoken.vue +3 -1
- package/list/fleet.cattle.io.gitrepo.vue +44 -5
- package/list/management.cattle.io.fleetworkspace.vue +45 -0
- package/list/node.vue +69 -16
- package/list/provisioning.cattle.io.cluster.vue +30 -1
- package/list/rbac.authorization.k8s.io.clusterrolebinding.vue +48 -0
- package/list/workload.vue +6 -4
- package/machine-config/azure.vue +97 -38
- package/middleware/authenticated.js +34 -0
- package/mixins/chart.js +101 -2
- package/mixins/fetch.client.js +95 -0
- package/mixins/fetch.server.js +73 -0
- package/mixins/labeled-form-element.ts +2 -2
- package/mixins/resource-fetch.js +2 -2
- package/models/apps.statefulset.js +28 -0
- package/models/cluster/node.js +23 -2
- package/models/cluster.x-k8s.io.machine.js +4 -2
- package/models/clusterroletemplatebinding.js +7 -0
- package/models/constraints.gatekeeper.sh.constraint.js +46 -0
- package/models/fleet.cattle.io.cluster.js +19 -10
- package/models/fleet.cattle.io.gitrepo.js +7 -2
- package/models/management.cattle.io.cluster.js +1 -1
- package/models/management.cattle.io.fleetworkspace.js +12 -0
- package/models/management.cattle.io.gitreporestriction.js +5 -0
- package/models/management.cattle.io.podsecurityadmissionconfigurationtemplate.js +3 -0
- package/models/pod.js +4 -0
- package/models/provisioning.cattle.io.cluster.js +7 -5
- package/models/rbac.authorization.k8s.io.clusterrolebinding.js +16 -0
- package/models/rbac.authorization.k8s.io.rolebinding.js +16 -0
- package/package.json +13 -21
- package/pages/auth/setup.vue +2 -2
- package/pages/c/_cluster/apps/charts/__tests__/install.helper.test.ts +33 -0
- package/pages/c/_cluster/apps/charts/chart.vue +4 -4
- package/pages/c/_cluster/apps/charts/install.helpers.js +26 -0
- package/pages/c/_cluster/apps/charts/install.vue +98 -102
- package/pages/c/_cluster/explorer/EventsTable.vue +5 -19
- package/pages/c/_cluster/explorer/index.vue +29 -25
- package/pages/c/_cluster/explorer/tools/index.vue +8 -8
- package/pages/c/_cluster/fleet/index.vue +95 -34
- package/pages/c/_cluster/gatekeeper/index.vue +1 -1
- package/pages/c/_cluster/istio/index.vue +5 -5
- package/pages/c/_cluster/manager/cloudCredential/index.vue +1 -1
- package/pages/c/_cluster/monitoring/index.vue +7 -0
- package/pages/c/_cluster/uiplugins/InstallDialog.vue +8 -8
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +20 -7
- package/pages/c/_cluster/uiplugins/index.vue +49 -17
- package/pages/diagnostic.vue +32 -25
- package/pages/home.vue +9 -4
- package/pages/index.vue +10 -1
- package/pages/rio/mesh.vue +1 -2
- package/pkg/tsconfig.json +0 -1
- package/plugins/clean-html-directive.js +34 -0
- package/plugins/dashboard-store/actions.js +32 -9
- package/plugins/dashboard-store/index.js +1 -1
- package/plugins/dashboard-store/mutations.js +5 -2
- package/plugins/dashboard-store/resource-class.js +8 -1
- package/plugins/plugin.js +0 -14
- package/plugins/portal-vue.js +4 -0
- package/plugins/steve/mutations.js +3 -2
- package/plugins/steve/steve-description-class.js +5 -1
- package/plugins/steve/subscribe.js +63 -54
- package/plugins/steve-create-worker.js +14 -0
- package/promptRemove/management.cattle.io.globalrole.vue +2 -2
- package/promptRemove/management.cattle.io.project.vue +2 -2
- package/promptRemove/management.cattle.io.roletemplate.vue +2 -2
- package/promptRemove/pod.vue +1 -1
- package/public/index.html +65 -0
- package/rancher-components/components/Banner/Banner.test.ts +7 -1
- package/rancher-components/components/Banner/Banner.vue +2 -1
- package/rancher-components/components/Form/Checkbox/Checkbox.vue +2 -0
- package/rancher-components/components/Form/Radio/RadioButton.test.ts +31 -0
- package/rancher-components/components/Form/Radio/RadioButton.vue +14 -3
- package/scripts/build-pkg.sh +1 -0
- package/scripts/clean +6 -0
- package/scripts/extension/bundle +58 -0
- package/scripts/extension/helmpatch +89 -0
- package/scripts/extension/publish +333 -0
- package/scripts/serve-pkgs +6 -2
- package/scripts/test-plugins-build.sh +4 -0
- package/store/__tests__/index.test.ts +110 -0
- package/store/index.js +145 -58
- package/store/type-map.js +6 -2
- package/tsconfig.default.json +36 -0
- package/tsconfig.json +23 -0
- package/types/rancher/index.d.ts +2 -0
- package/types/shell/index.d.ts +466 -320
- package/utils/__tests__/grafana.test.ts +44 -0
- package/utils/__tests__/string.test.ts +12 -0
- package/utils/auth.js +65 -0
- package/utils/axios.js +190 -0
- package/utils/cookie-universal-nuxt.js +10 -0
- package/utils/dom.js +15 -0
- package/utils/grafana.js +35 -16
- package/utils/monitoring.js +2 -1
- package/utils/nuxt.js +659 -0
- package/utils/position.js +5 -8
- package/utils/router.scrollBehavior.js +80 -0
- package/utils/select.js +1 -3
- package/utils/socket.js +1 -0
- package/utils/string.js +13 -0
- package/utils/time.js +9 -0
- package/vue.config.js +690 -0
- package/chart/rancher-alerting-drivers.vue +0 -53
- package/chart/rancher-gatekeeper.vue +0 -37
- package/creators/app/files/nuxt.config.js +0 -6
- package/models/management.cattle.io.podsecurityadmissionconfigurationtemplate.ts +0 -4
- package/nuxt.config.js +0 -798
- package/plugins/dashboard-store/extensions.js +0 -22
|
@@ -21,6 +21,7 @@ import Socket, {
|
|
|
21
21
|
EVENT_DISCONNECT_ERROR,
|
|
22
22
|
NO_WATCH,
|
|
23
23
|
NO_SCHEMA,
|
|
24
|
+
REVISION_TOO_OLD
|
|
24
25
|
} from '@shell/utils/socket';
|
|
25
26
|
import { normalizeType } from '@shell/plugins/dashboard-store/normalize';
|
|
26
27
|
import day from 'dayjs';
|
|
@@ -29,8 +30,6 @@ import { escapeHtml } from '@shell/utils/string';
|
|
|
29
30
|
import { keyForSubscribe } from '@shell/plugins/steve/resourceWatcher';
|
|
30
31
|
import { waitFor } from '@shell/utils/async';
|
|
31
32
|
|
|
32
|
-
// eslint-disable-next-line
|
|
33
|
-
import storeWorker from './worker/index.js';
|
|
34
33
|
import { BLANK_CLUSTER } from '@shell/store/index.js';
|
|
35
34
|
|
|
36
35
|
// minimum length of time a disconnect notification is shown
|
|
@@ -97,7 +96,7 @@ export async function createWorker(store, ctx) {
|
|
|
97
96
|
|
|
98
97
|
if (!store.$workers[storeName]) {
|
|
99
98
|
const workerMode = advancedWorker ? 'advanced' : 'basic';
|
|
100
|
-
const worker =
|
|
99
|
+
const worker = store.steveCreateWorker(workerMode);
|
|
101
100
|
|
|
102
101
|
store.$workers[storeName] = worker;
|
|
103
102
|
|
|
@@ -307,8 +306,13 @@ const sharedActions = {
|
|
|
307
306
|
}
|
|
308
307
|
|
|
309
308
|
// If socket is in error don't try to watch.... unless we `force` it
|
|
310
|
-
|
|
311
|
-
|
|
309
|
+
const inError = getters.inError(params);
|
|
310
|
+
|
|
311
|
+
if ( !stop && !force && inError ) {
|
|
312
|
+
// REVISION_TOO_OLD is a temporary state and will be handled when `resyncWatch` completes
|
|
313
|
+
if (inError !== REVISION_TOO_OLD) {
|
|
314
|
+
console.error(`Aborting Watch Request [${ getters.storeName }]. Watcher in error (${ inError })`, JSON.stringify(params)); // eslint-disable-line no-console
|
|
315
|
+
}
|
|
312
316
|
|
|
313
317
|
return;
|
|
314
318
|
}
|
|
@@ -316,7 +320,10 @@ const sharedActions = {
|
|
|
316
320
|
if ( !stop && getters.watchStarted({
|
|
317
321
|
type, id, selector, namespace
|
|
318
322
|
}) ) {
|
|
319
|
-
|
|
323
|
+
// eslint-disable-next-line no-console
|
|
324
|
+
state.debugSocket && console.debug(`Already Watching [${ getters.storeName }]`, {
|
|
325
|
+
type, id, selector, namespace
|
|
326
|
+
});
|
|
320
327
|
|
|
321
328
|
return;
|
|
322
329
|
}
|
|
@@ -347,7 +354,7 @@ const sharedActions = {
|
|
|
347
354
|
msg.selector = selector;
|
|
348
355
|
}
|
|
349
356
|
|
|
350
|
-
const worker = this.$workers[getters.storeName] || {};
|
|
357
|
+
const worker = this.$workers?.[getters.storeName] || {};
|
|
351
358
|
|
|
352
359
|
if (worker.mode === 'advanced') {
|
|
353
360
|
if ( force ) {
|
|
@@ -362,12 +369,17 @@ const sharedActions = {
|
|
|
362
369
|
return dispatch('send', msg);
|
|
363
370
|
},
|
|
364
371
|
|
|
365
|
-
unwatch(ctx,
|
|
372
|
+
unwatch(ctx, {
|
|
373
|
+
type, id, namespace, selector
|
|
374
|
+
}) {
|
|
366
375
|
const { commit, getters, dispatch } = ctx;
|
|
367
376
|
|
|
368
377
|
if (getters['schemaFor'](type)) {
|
|
369
378
|
const obj = {
|
|
370
379
|
type,
|
|
380
|
+
id,
|
|
381
|
+
namespace,
|
|
382
|
+
selector,
|
|
371
383
|
stop: true, // Stops the watch on a type
|
|
372
384
|
};
|
|
373
385
|
|
|
@@ -675,14 +687,30 @@ const defaultActions = {
|
|
|
675
687
|
/**
|
|
676
688
|
* Steve only event
|
|
677
689
|
*/
|
|
678
|
-
'ws.resource.start'({
|
|
690
|
+
'ws.resource.start'({
|
|
691
|
+
state, getters, commit, dispatch
|
|
692
|
+
}, msg) {
|
|
679
693
|
state.debugSocket && console.info(`Resource start: [${ getters.storeName }]`, msg); // eslint-disable-line no-console
|
|
680
|
-
|
|
694
|
+
|
|
695
|
+
const newWatch = {
|
|
681
696
|
type: msg.resourceType,
|
|
682
697
|
namespace: msg.namespace,
|
|
683
698
|
id: msg.id,
|
|
684
699
|
selector: msg.selector
|
|
700
|
+
};
|
|
701
|
+
|
|
702
|
+
state.started.filter((entry) => {
|
|
703
|
+
if (
|
|
704
|
+
entry.type === newWatch.type &&
|
|
705
|
+
entry.namespace !== newWatch.namespace
|
|
706
|
+
) {
|
|
707
|
+
return true;
|
|
708
|
+
}
|
|
709
|
+
}).forEach((entry) => {
|
|
710
|
+
dispatch('unwatch', entry);
|
|
685
711
|
});
|
|
712
|
+
|
|
713
|
+
commit('setWatchStarted', newWatch);
|
|
686
714
|
},
|
|
687
715
|
|
|
688
716
|
'ws.resource.error'({ getters, commit, dispatch }, msg) {
|
|
@@ -695,6 +723,10 @@ const defaultActions = {
|
|
|
695
723
|
} else if ( err.includes('failed to find schema') ) {
|
|
696
724
|
commit('setInError', { type: msg.resourceType, reason: NO_SCHEMA });
|
|
697
725
|
} else if ( err.includes('too old') ) {
|
|
726
|
+
// Set an error for (all) subs of this type. This..
|
|
727
|
+
// 1) blocks attempts by resource.stop to resub (as type is in error)
|
|
728
|
+
// 2) will be cleared when resyncWatch --> watch (with force) --> resource.start completes
|
|
729
|
+
commit('setInError', { type: msg.resourceType, reason: REVISION_TOO_OLD });
|
|
698
730
|
dispatch('resyncWatch', msg);
|
|
699
731
|
}
|
|
700
732
|
},
|
|
@@ -702,12 +734,15 @@ const defaultActions = {
|
|
|
702
734
|
/**
|
|
703
735
|
* Steve only event
|
|
704
736
|
*
|
|
705
|
-
* Steve
|
|
706
|
-
* - We have requested that the resource watch should be stopped and we receive this event as confirmation
|
|
707
|
-
* - Steve tells us that the resource
|
|
708
|
-
*
|
|
737
|
+
* Steve has stopped watching this resource. This happens for a couple of reasons
|
|
738
|
+
* - We have requested that the resource watch should be stopped (and we receive this event as confirmation)
|
|
739
|
+
* - Steve tells us that the resource watch has been stopped. Possible reasons
|
|
740
|
+
* - The rancher <--> k8s socket closed (happens every ~30 mins on mgmt socket)
|
|
741
|
+
* - Permissions has changed for the subscribed resource, so rancher closes socket
|
|
709
742
|
*/
|
|
710
|
-
'ws.resource.stop'({
|
|
743
|
+
'ws.resource.stop'({
|
|
744
|
+
state, getters, commit, dispatch
|
|
745
|
+
}, msg) {
|
|
711
746
|
const type = msg.resourceType;
|
|
712
747
|
const obj = {
|
|
713
748
|
type,
|
|
@@ -716,51 +751,23 @@ const defaultActions = {
|
|
|
716
751
|
selector: msg.selector
|
|
717
752
|
};
|
|
718
753
|
|
|
719
|
-
|
|
754
|
+
state.debugSocket && console.info(`Resource Stop [${ getters.storeName }]`, type, msg); // eslint-disable-line no-console
|
|
755
|
+
|
|
756
|
+
if (!type) {
|
|
757
|
+
console.error(`Resource Stop [${ getters.storeName }]. Received resource.stop with an empty resourceType, aborting`, msg); // eslint-disable-line no-console
|
|
758
|
+
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
720
761
|
|
|
721
762
|
// If we're trying to watch this event, attempt to re-watch
|
|
722
763
|
if ( getters['schemaFor'](type) && getters['watchStarted'](obj) ) {
|
|
723
764
|
commit('setWatchStopped', obj);
|
|
724
|
-
|
|
725
|
-
// In summary, we need to re-watch but with a reliable `revision` (to avoid `too old` message kicking off a full re-fetch of all
|
|
726
|
-
// resources). To get a reliable `revision` go out and fetch the latest for that resource type, in theory our local cache should be
|
|
727
|
-
// up to date with that revision.
|
|
728
|
-
|
|
729
|
-
const revisionExisting = getters.nextResourceVersion(type, obj.id);
|
|
730
|
-
|
|
731
|
-
let revisionLatest;
|
|
732
|
-
|
|
733
|
-
if (revisionExisting) {
|
|
734
|
-
// Attempt to fetch the latest revision at the time the resource watch was stopped, in theory our local cache should be up to
|
|
735
|
-
// date with this
|
|
736
|
-
// Ideally we shouldn't need to fetch here and supply `0`, `-1` or `null` to start watching from the latest revision, however steve
|
|
737
|
-
// will send the current state of each resource via a `resource.created` event.
|
|
738
|
-
const opt = { limit: 1 };
|
|
739
|
-
|
|
740
|
-
opt.url = getters.urlFor(type, null, opt);
|
|
741
|
-
revisionLatest = dispatch('request', { opt, type } )
|
|
742
|
-
.then(res => res.revision)
|
|
743
|
-
.catch((err) => {
|
|
744
|
-
// For some reason we can't fetch a reasonable revision, so force a re-fetch
|
|
745
|
-
console.warn(`Resource error retrieving resourceVersion, forcing re-fetch`, type, ':', err); // eslint-disable-line no-console
|
|
746
|
-
dispatch('resyncWatch', msg);
|
|
747
|
-
throw err;
|
|
748
|
-
});
|
|
749
|
-
} else {
|
|
750
|
-
// Some v1 resource types don't have revisions (either at the collection or resource level), so we avoided making an API request
|
|
751
|
-
// for them
|
|
752
|
-
revisionLatest = Promise.resolve(null); // Null to ensure we don't go through `nextResourceVersion` again
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
setTimeout(() => {
|
|
756
|
-
// Delay a bit so that immediate start/error/stop causes
|
|
757
|
-
// only a slow infinite loop instead of a tight one.
|
|
758
|
-
revisionLatest.then(revision => dispatch('watch', { ...obj, revision }));
|
|
759
|
-
}, 5000);
|
|
765
|
+
dispatch('watch', obj);
|
|
760
766
|
}
|
|
761
767
|
},
|
|
762
768
|
|
|
763
769
|
'ws.resource.create'(ctx, msg) {
|
|
770
|
+
ctx.state.debugSocket && console.info(`Resource Create [${ ctx.getters.storeName }]`, msg.resourceType, msg); // eslint-disable-line no-console
|
|
764
771
|
queueChange(ctx, msg, true, 'Create');
|
|
765
772
|
},
|
|
766
773
|
|
|
@@ -811,6 +818,8 @@ const defaultActions = {
|
|
|
811
818
|
const data = msg.data;
|
|
812
819
|
const type = data.type;
|
|
813
820
|
|
|
821
|
+
ctx.state.debugSocket && console.info(`Resource Remove [${ ctx.getters.storeName }]`, type, msg); // eslint-disable-line no-console
|
|
822
|
+
|
|
814
823
|
if (type === SCHEMA) {
|
|
815
824
|
const worker = (this.$workers || {})[ctx.getters.storeName];
|
|
816
825
|
|
|
@@ -914,8 +923,8 @@ const defaultMutations = {
|
|
|
914
923
|
* Getters that cover cases 1 & 2 (see file description)
|
|
915
924
|
*/
|
|
916
925
|
const defaultGetters = {
|
|
917
|
-
|
|
918
|
-
return
|
|
926
|
+
inError: state => (obj) => {
|
|
927
|
+
return state.inError[keyForSubscribe(obj)];
|
|
919
928
|
},
|
|
920
929
|
|
|
921
930
|
watchStarted: state => (obj) => {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import createWorker from './steve/worker/index.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Link the steve worker creator to the store
|
|
5
|
+
*
|
|
6
|
+
* This is done to allow disassociate (no import chain) access to the steve worker file.
|
|
7
|
+
* Without this third party plugins (in given scenarios / harvester) will fail to build
|
|
8
|
+
* due to missing web worker specific build config
|
|
9
|
+
*
|
|
10
|
+
* Note - When rancher/steve store is spun out in to a extension this also needs to move out
|
|
11
|
+
*/
|
|
12
|
+
export default function({ store }) {
|
|
13
|
+
store.steveCreateWorker = createWorker;
|
|
14
|
+
}
|
|
@@ -23,14 +23,14 @@ export default {
|
|
|
23
23
|
<div>
|
|
24
24
|
<template>
|
|
25
25
|
{{ t('promptRemove.attemptingToRemove', { type }) }} <span
|
|
26
|
-
v-html="resourceNames(names, plusMore, t)"
|
|
26
|
+
v-clean-html="resourceNames(names, plusMore, t)"
|
|
27
27
|
/>
|
|
28
28
|
</template>
|
|
29
29
|
<div
|
|
30
30
|
v-if="info"
|
|
31
31
|
class="text info mb-10 mt-20"
|
|
32
32
|
>
|
|
33
|
-
<span v-html="info" />
|
|
33
|
+
<span v-clean-html="info" />
|
|
34
34
|
</div>
|
|
35
35
|
<div
|
|
36
36
|
v-if="warning"
|
|
@@ -100,8 +100,8 @@ export default {
|
|
|
100
100
|
<template v-if="!canSeeProjectlessNamespaces">
|
|
101
101
|
<span class="delete-warning"> {{ t('promptRemove.willDeleteAssociatedNamespaces') }}</span> <br>
|
|
102
102
|
<div
|
|
103
|
+
v-clean-html="resourceNames(names, plusMore, t)"
|
|
103
104
|
class="mt-10"
|
|
104
|
-
v-html="resourceNames(names, plusMore, t)"
|
|
105
105
|
/>
|
|
106
106
|
</template>
|
|
107
107
|
</div>
|
|
@@ -114,7 +114,7 @@ export default {
|
|
|
114
114
|
:label="t('promptRemove.deleteAssociatedNamespaces')"
|
|
115
115
|
/>
|
|
116
116
|
<div class="mt-10 ml-20">
|
|
117
|
-
<span v-html="resourceNames(names, plusMore, t)" />
|
|
117
|
+
<span v-clean-html="resourceNames(names, plusMore, t)" />
|
|
118
118
|
</div>
|
|
119
119
|
</div>
|
|
120
120
|
</div>
|
|
@@ -23,14 +23,14 @@ export default {
|
|
|
23
23
|
<div>
|
|
24
24
|
<template>
|
|
25
25
|
{{ t('promptRemove.attemptingToRemove', { type }) }} <span
|
|
26
|
-
v-html="resourceNames(names, plusMore, t)"
|
|
26
|
+
v-clean-html="resourceNames(names, plusMore, t)"
|
|
27
27
|
/>
|
|
28
28
|
</template>
|
|
29
29
|
<div
|
|
30
30
|
v-if="info"
|
|
31
31
|
class="text info mb-10 mt-20"
|
|
32
32
|
>
|
|
33
|
-
<span v-html="info" />
|
|
33
|
+
<span v-clean-html="info" />
|
|
34
34
|
</div>
|
|
35
35
|
<div
|
|
36
36
|
v-if="warning"
|
package/promptRemove/pod.vue
CHANGED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="utf-8">
|
|
6
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
7
|
+
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
|
8
|
+
<link rel="shortcut icon" type="image/x-icon" href="/public/favicon.png">
|
|
9
|
+
<title>Rancher</title>
|
|
10
|
+
</head>
|
|
11
|
+
|
|
12
|
+
<body>
|
|
13
|
+
<div id="app">
|
|
14
|
+
<script>
|
|
15
|
+
(() => {
|
|
16
|
+
const isDark = document.cookie.includes('R_PCS=dark');
|
|
17
|
+
const color = isDark ? '#1b1c21' : '#FFF';
|
|
18
|
+
const style = document.createElement('style');
|
|
19
|
+
style.innerHTML = ':root { --loading-bg-color: ' + color + ';}';
|
|
20
|
+
document.getElementsByTagName('head')[0].prepend(style);
|
|
21
|
+
})();
|
|
22
|
+
</script>
|
|
23
|
+
<style>
|
|
24
|
+
.initial-load-spinner-container {
|
|
25
|
+
align-items: center;
|
|
26
|
+
background-color: var(--loading-bg-color);
|
|
27
|
+
display: flex;
|
|
28
|
+
justify-content: center;
|
|
29
|
+
height: 100vh;
|
|
30
|
+
left: 0;
|
|
31
|
+
position: absolute;
|
|
32
|
+
top: 0;
|
|
33
|
+
width: 100vw;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.initial-load-spinner {
|
|
37
|
+
animation: initial-load-animate 1s infinite linear;
|
|
38
|
+
background-color: var(--loading-bg-color);
|
|
39
|
+
box-sizing: border-box;
|
|
40
|
+
border: 5px solid #008ACF;
|
|
41
|
+
border-radius: 50%;
|
|
42
|
+
border-top-color: #00B2E2;
|
|
43
|
+
display: inline-block;
|
|
44
|
+
height: 80px;
|
|
45
|
+
margin: 0 auto;
|
|
46
|
+
width: 80px;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@keyframes initial-load-animate {
|
|
50
|
+
0% {
|
|
51
|
+
transform: rotate(0deg);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
100% {
|
|
55
|
+
transform: rotate(359deg);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
</style>
|
|
59
|
+
<div class="initial-load-spinner-container">
|
|
60
|
+
<i class="initial-load-spinner"></i>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
</body>
|
|
64
|
+
|
|
65
|
+
</html>
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { mount } from '@vue/test-utils';
|
|
2
2
|
import { Banner } from './index';
|
|
3
|
+
import { cleanHtmlDirective } from '@shell/plugins/clean-html-directive';
|
|
3
4
|
|
|
4
5
|
describe('component: Banner', () => {
|
|
5
6
|
it('should display text based on label', () => {
|
|
6
7
|
const label = 'test';
|
|
7
|
-
const wrapper = mount(
|
|
8
|
+
const wrapper = mount(
|
|
9
|
+
Banner,
|
|
10
|
+
{
|
|
11
|
+
directives: { cleanHtmlDirective },
|
|
12
|
+
propsData: { label }
|
|
13
|
+
});
|
|
8
14
|
|
|
9
15
|
const element = wrapper.find('span').element;
|
|
10
16
|
|
|
@@ -95,7 +95,7 @@ export default Vue.extend({
|
|
|
95
95
|
<span v-else-if="messageLabel">{{ messageLabel }}</span>
|
|
96
96
|
<span
|
|
97
97
|
v-else
|
|
98
|
-
v-html="nlToBr(label)"
|
|
98
|
+
v-clean-html="nlToBr(label)"
|
|
99
99
|
/>
|
|
100
100
|
</slot>
|
|
101
101
|
<div
|
|
@@ -164,6 +164,7 @@ $icon-size: 24px;
|
|
|
164
164
|
width: 100%;
|
|
165
165
|
border-left: solid $left-border-size transparent;
|
|
166
166
|
display: flex;
|
|
167
|
+
gap: 3px;
|
|
167
168
|
|
|
168
169
|
.primary & {
|
|
169
170
|
background: var(--primary);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { shallowMount } from '@vue/test-utils';
|
|
2
|
+
import { RadioButton } from './index';
|
|
3
|
+
import { cleanHtmlDirective } from '@shell/plugins/clean-html-directive';
|
|
4
|
+
|
|
5
|
+
describe('RadioButton.vue', () => {
|
|
6
|
+
it('renders label slot contents', () => {
|
|
7
|
+
const wrapper = shallowMount(RadioButton, { slots: { label: 'Test Label' } });
|
|
8
|
+
|
|
9
|
+
expect(wrapper.find('.radio-label').text()).toBe('Test Label');
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('renders label prop contents', () => {
|
|
13
|
+
const wrapper = shallowMount(
|
|
14
|
+
RadioButton,
|
|
15
|
+
{
|
|
16
|
+
directives: { cleanHtmlDirective },
|
|
17
|
+
propsData: { label: 'Test Label' }
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
expect(wrapper.find('.radio-label').text()).toBe('Test Label');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('renders slot contents when both slot and label prop are provided', () => {
|
|
24
|
+
const wrapper = shallowMount(RadioButton, {
|
|
25
|
+
slots: { label: 'Test Label - Slot' },
|
|
26
|
+
propsData: { label: 'Test Label - Props' },
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
expect(wrapper.find('.radio-label').text()).toBe('Test Label - Slot');
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -96,6 +96,10 @@ export default Vue.extend({
|
|
|
96
96
|
hasDescriptionSlot(): boolean {
|
|
97
97
|
return !!this.$slots.description;
|
|
98
98
|
},
|
|
99
|
+
|
|
100
|
+
hasLabelSlot(): boolean {
|
|
101
|
+
return !!this.$slots.label || !!this.$scopedSlots.label;
|
|
102
|
+
}
|
|
99
103
|
},
|
|
100
104
|
|
|
101
105
|
watch: {
|
|
@@ -149,12 +153,19 @@ export default Vue.extend({
|
|
|
149
153
|
/>
|
|
150
154
|
<div class="labeling">
|
|
151
155
|
<label
|
|
152
|
-
v-if="label"
|
|
153
156
|
:class="[ muteLabel ? 'text-muted' : '', 'radio-label', 'm-0']"
|
|
154
157
|
:for="name"
|
|
155
|
-
v-html="label"
|
|
156
158
|
>
|
|
157
|
-
<slot
|
|
159
|
+
<slot
|
|
160
|
+
v-if="hasLabelSlot"
|
|
161
|
+
name="label"
|
|
162
|
+
>
|
|
163
|
+
<!-- slot content -->
|
|
164
|
+
</slot>
|
|
165
|
+
<span
|
|
166
|
+
v-else-if="label"
|
|
167
|
+
v-clean-html="label"
|
|
168
|
+
/>
|
|
158
169
|
</label>
|
|
159
170
|
<div
|
|
160
171
|
v-if="descriptionKey || description"
|
package/scripts/build-pkg.sh
CHANGED
package/scripts/clean
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
BASE_DIR="$(pwd)"
|
|
4
|
+
|
|
5
|
+
CYAN="\033[96m"
|
|
6
|
+
RED="\033[91m"
|
|
7
|
+
RESET="\033[0m"
|
|
8
|
+
BOLD="\033[1m"
|
|
9
|
+
|
|
10
|
+
TMP=${BASE_DIR}/tmp
|
|
11
|
+
CHART_TEMPLATE=${TMP}/ui-plugin-server
|
|
12
|
+
|
|
13
|
+
PKG="${1}"
|
|
14
|
+
PKG_VERSION="${2}"
|
|
15
|
+
REGISTRY="${3}"
|
|
16
|
+
REGISTRY_ORG="${4}"
|
|
17
|
+
IMAGE_PREFIX="${5}"
|
|
18
|
+
PUSH="${6}"
|
|
19
|
+
|
|
20
|
+
PKG_NAME="${PKG}-${PKG_VERSION}"
|
|
21
|
+
|
|
22
|
+
# --------------------------------------------------------------------------------
|
|
23
|
+
# Create the container image
|
|
24
|
+
# --------------------------------------------------------------------------------
|
|
25
|
+
mkdir -p ${TMP}/container
|
|
26
|
+
cp -R ${CHART_TEMPLATE}/* ${TMP}/container/
|
|
27
|
+
|
|
28
|
+
# Copy the plugin assets
|
|
29
|
+
rm -rf ${TMP}/container/plugin
|
|
30
|
+
mkdir ${TMP}/container/plugin
|
|
31
|
+
|
|
32
|
+
cp -R ${BASE_DIR}/dist-pkg/${PKG_NAME}/* ${TMP}/container/plugin
|
|
33
|
+
rm -f ${TMP}/container/plugin/report.html
|
|
34
|
+
|
|
35
|
+
# Build the docker image
|
|
36
|
+
pushd ${TMP}/container > /dev/null
|
|
37
|
+
echo -e "${CYAN}Building container image ...${RESET}"
|
|
38
|
+
|
|
39
|
+
if [ ! -z "${REGISTRY}" ]; then
|
|
40
|
+
REGISTRY=${REGISTRY} ORG=${REGISTRY_ORG} REPO=${IMAGE_PREFIX}${PKG} TAG=${PKG_VERSION} ./scripts/package
|
|
41
|
+
|
|
42
|
+
if [ "${PUSH}" == "--push" ]; then
|
|
43
|
+
echo -e "${CYAN}Pushing container image ...${RESET}"
|
|
44
|
+
|
|
45
|
+
# Ensure that you do not overwrite production images
|
|
46
|
+
if [[ "${REGISTRY_ORG}" == "rancher" ]]; then
|
|
47
|
+
IMAGE=${REGISTRY}${REGISTRY_ORG}/${IMAGE_PREFIX}${PKG}:${PKG_VERSION}
|
|
48
|
+
if docker manifest inspect 2>&1 1>/dev/null; then
|
|
49
|
+
echo -e "${RED}${BOLD}Cannot overwrite production image ${IMAGE_PREFIX}${PKG} since it already exists${RESET}"
|
|
50
|
+
exit 1
|
|
51
|
+
fi
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
docker push ${REGISTRY}${REGISTRY_ORG}/${IMAGE_PREFIX}${PKG}:${PKG_VERSION}
|
|
55
|
+
fi
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
popd > /dev/null
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// Update Chart.yaml and values.yaml files
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const yaml = require('js-yaml');
|
|
8
|
+
|
|
9
|
+
// TODO: Should already be in the template
|
|
10
|
+
// const UI_PLUGIN_ANNOTATION_NAME = 'catalog.cattle.io/ui-component';
|
|
11
|
+
// const UI_PLUGIN_ANNOTATION_VALUE = 'plugins';
|
|
12
|
+
|
|
13
|
+
console.log('Helm file update');
|
|
14
|
+
|
|
15
|
+
if (process.argv.length !== 5) {
|
|
16
|
+
console.log('Incorrect number of arguments');
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const destFolder = process.argv[2];
|
|
21
|
+
const pkgFilePath = process.argv[3];
|
|
22
|
+
const image = process.argv[4];
|
|
23
|
+
|
|
24
|
+
const pkgFile = JSON.parse(fs.readFileSync(pkgFilePath, 'utf8'));
|
|
25
|
+
|
|
26
|
+
const chartFolder = destFolder;
|
|
27
|
+
const chartFile = path.join(chartFolder, 'Chart.yaml');
|
|
28
|
+
const valuesFile = path.join(chartFolder, 'values.yaml');
|
|
29
|
+
|
|
30
|
+
if (!fs.existsSync(chartFolder)) {
|
|
31
|
+
console.log('Charts folder does not exist');
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// --------------------------------------------------------------------------------
|
|
36
|
+
// Chart.yaml
|
|
37
|
+
// --------------------------------------------------------------------------------
|
|
38
|
+
const chart = yaml.load(fs.readFileSync(chartFile, 'utf8'));
|
|
39
|
+
let updated = false;
|
|
40
|
+
|
|
41
|
+
// Add in annotations to match any rules in the package file
|
|
42
|
+
if (pkgFile.rancher && typeof pkgFile.rancher === 'object') {
|
|
43
|
+
// See if there are any annotations and merge them in, if there are
|
|
44
|
+
if (pkgFile.rancher.annotations) {
|
|
45
|
+
chart.annotations = chart.annotations || {};
|
|
46
|
+
Object.keys(pkgFile.rancher.annotations).forEach((key) => {
|
|
47
|
+
chart.annotations[key] = pkgFile.rancher.annotations[key];
|
|
48
|
+
updated = true;
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (updated ){
|
|
54
|
+
// Write out updated file
|
|
55
|
+
const str = yaml.dump(chart);
|
|
56
|
+
|
|
57
|
+
fs.writeFileSync(chartFile, str, 'utf8');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// chart.name = pkgFile.name;
|
|
61
|
+
// chart.appVersion = pkgFile.version;
|
|
62
|
+
// chart.version = pkgFile.version;
|
|
63
|
+
// chart.description = pkgFile.description || `${pkgFile.name} ui plugin `;
|
|
64
|
+
// chart.annotations[UI_PLUGIN_ANNOTATION_NAME] = UI_PLUGIN_ANNOTATION_VALUE;
|
|
65
|
+
|
|
66
|
+
// if (pkgFile.icon) {
|
|
67
|
+
// chart.icon = pkgFile.icon;
|
|
68
|
+
// }
|
|
69
|
+
|
|
70
|
+
// if (pkgFile.keywords) {
|
|
71
|
+
// chart.keywords = pkgFile.keywords;
|
|
72
|
+
// } else {
|
|
73
|
+
// chart.keywords = [];
|
|
74
|
+
// }
|
|
75
|
+
|
|
76
|
+
// if (pkgFile.homepage) {
|
|
77
|
+
// chart.home = pkgFile.homepage;
|
|
78
|
+
// }
|
|
79
|
+
|
|
80
|
+
// --------------------------------------------------------------------------------
|
|
81
|
+
// values.yaml
|
|
82
|
+
// --------------------------------------------------------------------------------
|
|
83
|
+
|
|
84
|
+
const values = yaml.load(fs.readFileSync(valuesFile, 'utf8'));
|
|
85
|
+
|
|
86
|
+
values.pluginServer.image.repository = `${image}`;
|
|
87
|
+
|
|
88
|
+
const valuesYaml = yaml.dump(values, {quotingType: '"' });
|
|
89
|
+
fs.writeFileSync(valuesFile, valuesYaml, 'utf8');
|