diff --git a/assets/translations/en-us.yaml b/assets/translations/en-us.yaml index cef6331a06..643518b93b 100644 --- a/assets/translations/en-us.yaml +++ b/assets/translations/en-us.yaml @@ -3,7 +3,7 @@ locale: en-us: English product: - apps: App Marketplace + apps: Apps & Marketplace explorer: Cluster Explorer gatekeeper: OPA Gatekeeper istio: Istio @@ -889,7 +889,7 @@ typeLabel: } catalog.cattle.io.release: | {count, plural, - one { App } + one { Apps } other { Apps } } chartInstallAction: | diff --git a/components/form/NameNsDescription.vue b/components/form/NameNsDescription.vue index 6f1a4b1e7f..4e5e47962d 100644 --- a/components/form/NameNsDescription.vue +++ b/components/form/NameNsDescription.vue @@ -98,6 +98,9 @@ export default { if ( !namespace ) { namespace = this.$store.getters['defaultNamespace']; + if ( metadata ) { + metadata.namespace = namespace; + } } } diff --git a/components/nav/WindowManager/ContainerLogs.vue b/components/nav/WindowManager/ContainerLogs.vue index 094f373ba9..b0f141f90f 100644 --- a/components/nav/WindowManager/ContainerLogs.vue +++ b/components/nav/WindowManager/ContainerLogs.vue @@ -208,13 +208,16 @@ export default { } const params = { - container: this.container, previous: this.previous, follow: true, timestamps: true, pretty: true, }; + if ( this.container ) { + params.container = this.container; + } + const range = `${ this.range }`.trim().toLowerCase(); const match = range.match(/^(\d+)?\s*(.*?)s?$/); diff --git a/config/product/apps.js b/config/product/apps.js index c0e5470a0f..968b25ff7b 100644 --- a/config/product/apps.js +++ b/config/product/apps.js @@ -11,7 +11,8 @@ export function init(store) { product, basicType, headers, - formOnlyType + virtualType, + uncreatableType, } = DSL(store, NAME); product({ @@ -20,14 +21,26 @@ export function init(store) { weight: 1000, }); + virtualType({ + label: 'Launch', + group: 'Root', + namespaced: false, + name: 'launch', + weight: 100, + route: { name: 'c-cluster-apps' }, + exact: true, + }); + basicType([ + 'launch', CATALOG.REPO, CATALOG.CLUSTER_REPO, CATALOG.RELEASE, CATALOG.OPERATION, ]); - formOnlyType(CATALOG.RELEASE); + uncreatableType(CATALOG.RELEASE); + uncreatableType(CATALOG.OPERATION); headers(CATALOG.RELEASE, [STATE, NAMESPACE_NAME, CHART, RESOURCES, AGE]); headers(CATALOG.REPO, [NAMESPACE_NAME, URL, AGE]); diff --git a/config/query-params.js b/config/query-params.js index ee4690d7a6..f65a532718 100644 --- a/config/query-params.js +++ b/config/query-params.js @@ -45,7 +45,7 @@ export const ADD_SIDECAR = 'add-sidecar'; export const EDIT_CONTAINER = 'container'; // App launch -export const CLUSTER_REPO = 'cluster-repo'; +export const REPO_TYPE = 'repo-type'; export const REPO = 'repo'; export const CHART = 'chart'; export const VERSION = 'version'; diff --git a/edit/chartInstallAction.vue b/edit/chartInstallAction.vue index ece9ec8a33..f0aea9b62e 100644 --- a/edit/chartInstallAction.vue +++ b/edit/chartInstallAction.vue @@ -2,42 +2,22 @@ import jsyaml from 'js-yaml'; import merge from 'lodash/merge'; import Loading from '@/components/Loading'; -import ButtonGroup from '@/components/ButtonGroup'; -import Checkbox from '@/components/form/Checkbox'; import NameNsDescription from '@/components/form/NameNsDescription'; import LabeledSelect from '@/components/form/LabeledSelect'; -import LazyImage from '@/components/LazyImage'; import Markdown from '@/components/Markdown'; import { CATALOG } from '@/config/types'; -import { allHash } from '@/utils/promise'; -import { sortBy } from '@/utils/sort'; import { defaultAsyncData } from '@/components/ResourceDetail'; -import { - CLUSTER_REPO, REPO, CHART, VERSION, STEP -} from '@/config/query-params'; -import { findBy } from '@/utils/array'; -import { addParams } from '@/utils/url'; -import YamlEditor from '@/components/YamlEditor'; +import { REPO_TYPE, REPO, CHART, VERSION } from '@/config/query-params'; import Wizard from '@/components/Wizard'; -import { CATALOG as CATALOG_ANNOTATIONS, DESCRIPTION } from '@/config/labels-annotations'; -import { ensureRegex } from '@/utils/string'; +import YamlEditor from '@/components/YamlEditor'; +import { DESCRIPTION } from '@/config/labels-annotations'; import { exceptionToErrorsArray } from '@/utils/error'; -const CERTIFIED_SORTS = { - [CATALOG_ANNOTATIONS._RANCHER]: 1, - [CATALOG_ANNOTATIONS._EXPERIMENTAL]: 1, - [CATALOG_ANNOTATIONS._PARTNER]: 2, - other: 3, -}; - export default { name: 'ChartInstall', components: { - ButtonGroup, - Checkbox, LabeledSelect, - LazyImage, Loading, Markdown, NameNsDescription, @@ -65,54 +45,47 @@ export default { async fetch() { const query = this.$route.query; - if ( !this.clusterRepos ) { - await this.loadReposAndCharts(); - } + await this.$store.dispatch('catalog/load'); - let repoKey; - let repoName; - let repoType; + const repoType = query[REPO_TYPE]; + const repoName = query[REPO]; - if ( query[CLUSTER_REPO] ) { - repoKey = CLUSTER_REPO; - repoName = query[CLUSTER_REPO]; - repoType = CATALOG.CLUSTER_REPO; - } else if ( query[REPO] ) { - repoKey = REPO; - repoName = query[REPO]; - repoType = CATALOG.REPO; - } - - this.repo = findBy(this.repos, { type: repoType, 'metadata.name': repoName }); + this.repo = this.$store.getters['catalog/repo']({ repoType, repoName }); const chartName = query[CHART]; - const versionName = query[VERSION]; + const version = query[VERSION]; - if ( this.repo && chartName ) { - this.chart = findBy(this.allCharts, { [repoKey]: repoName, name: chartName }); - - if ( this.chart.targetNamespace ) { - this.forceNamespace = this.chart.targetNamespace; - } else { - this.forceNamespace = null; - } - - if ( this.chart.targetName ) { - this.value.metadata.name = this.chart.targetName; - this.nameDisabled = true; - } else { - this.nameDisabled = false; - } + if ( this.repo && !this.chart && chartName ) { + this.chart = this.$store.getters['catalog/chart']({ + repoType, repoName, chartName + }); } - let version; - - if ( this.chart && versionName ) { - version = findBy(this.chart.versions, 'version', versionName); + if ( !this.chart ) { + throw new Error('Chart not found'); } - if ( version && !this.versionInfo ) { - this.versionInfo = await this.repo.followLink('info', { url: addParams(this.repo.links.info, { chartName, version: versionName }) }); + if ( this.chart.targetNamespace ) { + this.forceNamespace = this.chart.targetNamespace; + } else { + this.forceNamespace = null; + } + + if ( this.chart.targetName ) { + this.value.metadata.name = this.chart.targetName; + this.nameDisabled = true; + } else { + this.nameDisabled = false; + } + + if ( version ) { + this.version = this.$store.getters['catalog/version']({ + repoType, repoName, chartName, version + }); + + this.versionInfo = await this.$store.dispatch('catalog/getVersionInfo', { + repoType, repoName, chartName, version + }); this.mergeValues(this.versionInfo.values); this.valuesYaml = jsyaml.safeDump(this.value.values); } @@ -124,27 +97,15 @@ export default { data() { return { - clusterRepos: null, - namespacedRepos: null, - - allCharts: null, + repo: null, chart: null, - + version: null, versionInfo: null, valuesYaml: null, - namespace: null, - description: null, forceNamespace: null, nameDisabled: false, - searchQuery: '', - sortField: 'certifiedSort', - showDeprecated: false, - showRancher: true, - showPartner: true, - showOther: true, - errors: null, }; }, @@ -169,158 +130,11 @@ export default { }, ]; }, - - repos() { - const clustered = this.clusterRepos || []; - const namespaced = this.namespacedRepos || []; - - return [...clustered, ...namespaced]; - }, - - filteredCharts() { - return (this.allCharts || []).filter((c) => { - if ( c.deprecated && !this.showDeprecated ) { - return false; - } - - if ( ( c.certified === CATALOG_ANNOTATIONS._RANCHER && !this.showRancher ) || - ( c.certified === CATALOG_ANNOTATIONS._PARTNER && !this.showPartner ) || - ( c.certified !== CATALOG_ANNOTATIONS._RANCHER && c.certified !== CATALOG_ANNOTATIONS._PARTNER && !this.showOther ) - ) { - return false; - } - - if ( this.searchQuery ) { - const searchTokens = this.searchQuery.split(/\s*[, ]\s*/).map(x => ensureRegex(x, false)); - - for ( const token of searchTokens ) { - if ( !c.name.match(token) && !c.description.match(token) ) { - return false; - } - } - } - - return true; - }); - }, - - arrangedCharts() { - return sortBy(this.filteredCharts, [this.sortField, 'name']); - }, }, watch: { '$route.query': '$fetch' }, methods: { - async loadReposAndCharts() { - let promises = { - clusterRepos: this.$store.dispatch('cluster/findAll', { type: CATALOG.CLUSTER_REPO }), - namespacedRepos: this.$store.dispatch('cluster/findAll', { type: CATALOG.REPO }), - }; - - const hash = await allHash(promises); - - this.clusterRepos = hash.clusterRepos; - this.namespacedRepos = hash.namespacedRepos; - - promises = []; - for ( const repo of this.repos ) { - promises.push(repo.followLink('index')); - } - - const indexes = await Promise.all(promises); - - const charts = {}; - - for ( let i = 0 ; i < indexes.length ; i++ ) { - const obj = indexes[i]; - const repo = this.repos[i]; - - for ( const k in obj.entries ) { - for ( const entry of obj.entries[k] ) { - addChart(entry, repo); - } - } - } - - this.allCharts = sortBy(Object.values(charts), ['key']); - - function addChart(chart, repo) { - const key = `${ repo.type }/${ repo.metadata.name }/${ chart.name }`; - const certifiedAnnotation = chart.annotations?.[CATALOG_ANNOTATIONS.CERTIFIED]; - - let certified = CATALOG_ANNOTATIONS._OTHER; - let sideLabel = null; - - // @TODO remove fake hackery - if ( repo.name === 'dev-charts' ) { - certified = CATALOG_ANNOTATIONS._RANCHER; - } else if ( chart.name.startsWith('f') ) { - certified = CATALOG_ANNOTATIONS._PARTNER; - } else if ( repo.isRancher ) { - // Only charts from a rancher repo can actually set the certified flag - certified = certifiedAnnotation || certified; - } - - // @TODO remove fake hackery - if ( chart.name.includes('b') ) { - sideLabel = 'Experimental'; - } else if ( chart.annotations?.[CATALOG_ANNOTATIONS.EXPERIMENTAL] ) { - sideLabel = 'Experimental'; - } else if ( !repo.isRancher && certifiedAnnotation && certified === CATALOG_ANNOTATIONS._OTHER) { - // But anybody can set the side label - sideLabel = certifiedAnnotation; - } - - let icon = chart.icon; - - if ( icon ) { - icon = icon.replace(/^(https?:\/\/github.com\/[^/]+\/[^/]+)\/blob/, '$1/raw'); - } - - let obj = charts[key]; - - if ( !obj ) { - obj = { - key, - icon, - certified, - sideLabel, - certifiedSort: CERTIFIED_SORTS[certified] || 99, - name: chart.name, - description: chart.description, - repoName: repo.name, - versions: [], - deprecated: !!chart.deprecated, - targetNamespace: chart.annotations?.[CATALOG_ANNOTATIONS.NAMESPACE], - targetName: chart.annotations?.[CATALOG_ANNOTATIONS.RELEASE_NAME], - }; - - if ( repo.type === CATALOG.CLUSTER_REPO ) { - obj.clusterRepo = repo.metadata.name; - } else { - obj.repo = repo.metadata.name; - } - - charts[key] = obj; - } - - obj.versions.push(chart); - } - }, - - selectChart(chart) { - this.chart = chart; - - this.$router.applyQuery({ - [CLUSTER_REPO]: chart.clusterRepo, - [CHART]: chart.name, - [REPO]: chart.repo, - [VERSION]: chart.versions[0].version, - [STEP]: 2 - }); - }, - selectVersion(version) { this.$router.applyQuery({ [VERSION]: version }); }, @@ -375,7 +189,7 @@ export default { installInput() { const out = JSON.parse(JSON.stringify(this.value)); - out.chartName = this.chart.name; + out.chartName = this.chart.chartName; out.version = this.$route.query.version; out.releaseName = out.metadata.name; out.namespace = out.metadata.namespace; @@ -387,11 +201,6 @@ export default { return out; }, - - focusSearch() { - this.$refs.searchQuery.focus(); - this.$refs.searchQuery.select(); - } } }; @@ -405,40 +214,7 @@ export default { :errors="errors" @finish="finish($event)" > - - -