Skip to content

Commit

Permalink
Merge pull request #12374 from richard-cox/2.8-helm-app-values
Browse files Browse the repository at this point in the history
[2.8] Update Helm App Values Approach
  • Loading branch information
richard-cox authored Oct 29, 2024
2 parents 1681024 + 2d21307 commit 3d49895
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 28 deletions.
21 changes: 17 additions & 4 deletions shell/detail/catalog.cattle.io.app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import RelatedResources from '@shell/components/RelatedResources';
import jsyaml from 'js-yaml';
import merge from 'lodash/merge';
import { CATALOG } from '@shell/config/types';
import { sortBy } from '~shell/utils/sort';
import { sortBy } from '@shell/utils/sort';
import { allHash } from '@shell/utils/promise';
export default {
name: 'DetailRelease',
Expand All @@ -30,9 +31,15 @@ export default {
},
async fetch() {
await this.$store.dispatch('catalog/load', { force: true, reset: true });
const promises = {
catalog: this.$store.dispatch('catalog/load', { force: true, reset: true }),
allOperations: this.$store.dispatch('cluster/findAll', { type: CATALOG.OPERATION }),
secrets: this.value.fetchValues(true),
};
this.allOperations = await this.$store.dispatch('cluster/findAll', { type: CATALOG.OPERATION });
const res = await allHash(promises);
this.allOperations = res.allOperations;
},
computed: {
Expand All @@ -45,7 +52,7 @@ export default {
},
valuesYaml() {
const combined = merge(merge({}, this.value?.spec?.chart?.values || {}), this.value?.spec?.values || {});
const combined = merge(merge({}, this.value?.chartValues || {}), this.value?.values || {});
return jsyaml.dump(combined);
},
Expand Down Expand Up @@ -95,6 +102,12 @@ export default {
}
},
},
watch: {
'value.secretId'(neu, old) {
this.value.fetchValues(true);
}
},
};
</script>
Expand Down
8 changes: 6 additions & 2 deletions shell/mixins/chart.js
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,8 @@ export default {
id: `${ this.query.appNamespace }/${ this.query.appName }`,
});

await this.existing?.fetchValues(true);

this.mode = _EDIT;
} catch (e) {
this.mode = _CREATE;
Expand Down Expand Up @@ -420,10 +422,12 @@ export default {
}
}
if (existingCRDApp) {
await existingCRDApp.fetchValues(true);

// spec.values are any non-default values the user configured
// the installation form should show these, as well as any default values from the chart
const existingValues = clone(existingCRDApp.spec?.values || {});
const defaultValues = clone(existingCRDApp.spec?.chart?.values || {});
const existingValues = clone(existingCRDApp.values || {});
const defaultValues = clone(existingCRDApp.chartValues || {});

crdVersionInfo.existingValues = existingValues;
crdVersionInfo.allValues = merge(defaultValues, existingValues);
Expand Down
133 changes: 112 additions & 21 deletions shell/models/catalog.cattle.io.app.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
import { CATALOG as CATALOG_ANNOTATIONS, FLEET } from '@shell/config/labels-annotations';
import { compare, isPrerelease, sortable } from '@shell/utils/version';
import { filterBy } from '@shell/utils/array';
import { CATALOG, MANAGEMENT, NORMAN } from '@shell/config/types';
import { CATALOG, MANAGEMENT, NORMAN, SECRET } from '@shell/config/types';
import { SHOW_PRE_RELEASE } from '@shell/store/prefs';
import { set } from '@shell/utils/object';

Expand Down Expand Up @@ -271,28 +271,119 @@ export default class CatalogApp extends SteveModel {
};
}

get deployedAsLegacy() {
return async() => {
if (this.spec?.values?.global) {
const { clusterName, projectName } = this.spec.values.global;

if (clusterName && projectName) {
try {
const legacyApp = await this.$dispatch('rancher/find', {
type: NORMAN.APP,
id: `${ projectName }:${ this.metadata?.name }`,
opt: { url: `/v3/project/${ clusterName }:${ projectName }/apps/${ projectName }:${ this.metadata?.name }` }
}, { root: true });

if (legacyApp) {
return legacyApp;
}
} catch (e) {}
}
async deployedAsLegacy() {
await this.fetchValues();

if (this.values?.global) {
const { clusterName, projectName } = this.values.global;

if (clusterName && projectName) {
try {
const legacyApp = await this.$dispatch('rancher/find', {
type: NORMAN.APP,
id: `${ projectName }:${ this.metadata?.name }`,
opt: { url: `/v3/project/${ clusterName }:${ projectName }/apps/${ projectName }:${ this.metadata?.name }` }
}, { root: true });

if (legacyApp) {
return legacyApp;
}
} catch (e) {}
}
}

return false;
};
return false;
}

/**
* User and Chart values live in a helm secret, so fetch it (with special param)
*/
async fetchValues(force = false) {
if (!this.secretId) {
// If there's no secret id this isn't ever going to work, no need to carry on
return;
}

const haveValues = !!this._values && !!this._chartValues;

if (haveValues && !force) {
// If we already have the required values and we're not forced to re-fetch, no need to carry on
return;
}

try {
await this.$dispatch('find', {
type: SECRET,
id: this.secretId,
opt: {
force: force || (!!this._secret && !haveValues), // force if explicitly requested or there's ean existing secret without the required values we have a secret without the values in (Secret has been fetched another way)
watch: false, // Cannot watch with custom params (they are dropped on calls made when resyncing over socket)
params: { includeHelmData: true }
}
});
} catch (e) {
console.error(`Cannot find values for ${ this.id } (unable to fetch)`, e); // eslint-disable-line no-console
}
}

get secretId() {
const metadata = this.metadata;

if (!metadata) {
return null;
}
const secretReference = metadata?.ownerReferences?.find((ow) => ow.kind.toLowerCase() === SECRET);

const secretId = secretReference?.name;
const secretNamespace = metadata.namespace;

if (!secretNamespace || !secretId) {
console.warn(`Cannot find values for ${ this.id } (cannot find related secret namespace or id)`); // eslint-disable-line no-console

return null;
}

return `${ secretNamespace }/${ secretId }`;
}

get _secret() {
return this.secretId ? this.$getters['byId'](SECRET, this.secretId) : null;
}

_validateSecret(noun) {
if (this._secret === undefined) {
throw new Error(`Cannot find ${ noun } for ${ this.id } (chart secret has not been fetched via app \`fetchValues\`)`);
}

if (this._secret === null) {
throw new Error(`Cannot find ${ noun } for ${ this.id } (chart secret cannot or has failed to fetch) `);
}
}

/**
* The user's helm values
*/
get values() {
this._validateSecret('values');

return this._values;
}

get _values() {
return this._secret?.data?.release?.config;
}

/**
* The Charts default helm values
*/
get chartValues() {
this._validateSecret('chartValues');

return this._chartValues;
}

get _chartValues() {
return this._secret?.data?.release?.chart?.values;
}
}

Expand Down
3 changes: 2 additions & 1 deletion shell/pages/c/_cluster/apps/charts/install.vue
Original file line number Diff line number Diff line change
Expand Up @@ -301,8 +301,9 @@ export default {
*/
userValues = diff(this.loadedVersionValues, this.chartValues);
} else if ( this.existing ) {
await this.existing.fetchValues(); // In theory this has already been called, but do again to be safe
/* For an already installed app, use the values from the previous install. */
userValues = clone(this.existing.spec?.values || {});
userValues = clone(this.existing.values || {});
} else {
/* For an new app, start empty. */
userValues = {};
Expand Down

0 comments on commit 3d49895

Please sign in to comment.