diff --git a/package.json b/package.json
index 1b7d30b..4379632 100644
--- a/package.json
+++ b/package.json
@@ -14,7 +14,9 @@
"css-loader": "4.3.0"
},
"devDependencies": {
- "@types/node": "18.11.9"
+ "@types/node": "18.11.9",
+ "@types/semver": "^7.5.8",
+ "semver": "^7.6.0"
},
"scripts": {
"dev": "NODE_ENV=dev ./node_modules/.bin/vue-cli-service serve",
diff --git a/pkg/elemental/components/BuildMedia.vue b/pkg/elemental/components/BuildMedia.vue
index f196979..ec31103 100644
--- a/pkg/elemental/components/BuildMedia.vue
+++ b/pkg/elemental/components/BuildMedia.vue
@@ -4,6 +4,7 @@ import { Banner } from '@components/Banner';
import AsyncButton from '@shell/components/AsyncButton';
import { randomStr, CHARSET } from '@shell/utils/string';
import { ELEMENTAL_SCHEMA_IDS } from '../config/elemental-types';
+import { getOperatorVersion, checkGatedFeatureCompatibility, BUILD_MEDIA_RAW_SUPPORT } from '../utils/feature-versioning';
const MEDIA_TYPES = {
RAW: {
@@ -38,17 +39,28 @@ export default {
registrationEndpoint: {
type: String,
default: ''
+ },
+ resource: {
+ type: String,
+ default: ''
+ },
+ mode: {
+ type: String,
+ default: ''
}
},
async fetch() {
this.seedImagesList = await this.$store.dispatch('management/findAll', { type: ELEMENTAL_SCHEMA_IDS.SEED_IMAGE });
this.managedOsVersions = await this.$store.dispatch('management/findAll', { type: ELEMENTAL_SCHEMA_IDS.MANAGED_OS_VERSIONS });
+
+ this.operatorVersion = await getOperatorVersion(this.$store);
},
data() {
return {
seedImagesList: [],
managedOsVersions: [],
filteredManagedOsVersions: [],
+ operatorVersion: '',
buildMediaOsVersions: [],
buildMediaTypes: [
{ label: MEDIA_TYPES.ISO.label, value: MEDIA_TYPES.ISO.type },
@@ -79,6 +91,15 @@ export default {
}
},
computed: {
+ isRawDiskImageBuildSupported() {
+ const check = checkGatedFeatureCompatibility(this.resource, this.mode, BUILD_MEDIA_RAW_SUPPORT, this.operatorVersion);
+
+ if (!check) {
+ this.buildMediaTypeSelected = MEDIA_TYPES.ISO.type; // eslint-disable-line vue/no-side-effects-in-computed-properties
+ }
+
+ return check;
+ },
registrationEndpointsOptions() {
const activeRegEndpoints = this.registrationEndpointList.filter(item => item.state === 'active');
@@ -91,10 +112,10 @@ export default {
},
isBuildMediaBtnEnabled() {
if (this.displayRegEndpoints) {
- return this.registrationEndpointSelected && this.buildMediaOsVersionSelected && this.buildMediaTypeSelected;
+ return this.isRawDiskImageBuildSupported ? this.registrationEndpointSelected && this.buildMediaOsVersionSelected && this.buildMediaTypeSelected : this.registrationEndpointSelected && this.buildMediaOsVersionSelected;
}
- return this.buildMediaOsVersionSelected && this.buildMediaTypeSelected;
+ return this.isRawDiskImageBuildSupported ? this.buildMediaOsVersionSelected && this.buildMediaTypeSelected : this.buildMediaOsVersionSelected;
},
seedImageFound() {
if (this.seedImage) {
@@ -146,13 +167,12 @@ export default {
const machineRegName = this.displayRegEndpoints ? this.registrationEndpointSelected.split('/')[1] : this.registrationEndpoint.split('/')[1];
const machineRegNamespace = this.displayRegEndpoints ? this.registrationEndpointSelected.split('/')[0] : this.registrationEndpoint.split('/')[0];
- const seedImageModel = await this.$store.dispatch('management/create', {
+ const seedImageObject = {
metadata: {
name: `media-image-reg-${ machineRegName }-${ randomStr(8, CHARSET.ALPHA_LOWER ) }`,
namespace: 'fleet-default'
},
spec: {
- type: this.buildMediaTypeSelected,
baseImage: this.buildMediaOsVersionSelected,
registrationRef: {
name: machineRegName,
@@ -160,7 +180,13 @@ export default {
}
},
type: ELEMENTAL_SCHEMA_IDS.SEED_IMAGE,
- });
+ };
+
+ if (this.isRawDiskImageBuildSupported) {
+ seedImageObject.spec.type = this.buildMediaTypeSelected;
+ }
+
+ const seedImageModel = await this.$store.dispatch('management/create', seedImageObject);
try {
this.seedImage = await seedImageModel.save({ url: `/v1/${ ELEMENTAL_SCHEMA_IDS.SEED_IMAGE }`, method: 'POST' });
@@ -207,7 +233,10 @@ export default {
:options="registrationEndpointsOptions"
/>
-
+
import { allHash } from '@shell/utils/promise';
import { CAPI, CATALOG } from '@shell/config/types';
+import { _VIEW } from '@shell/config/query-params';
import { NAME } from '@shell/config/table-headers';
import ResourceTable from '@shell/components/ResourceTable';
import PercentageBar from '@shell/components/PercentageBar';
@@ -10,6 +11,7 @@ import {
ELEMENTAL_CLUSTER_PROVIDER,
KIND
} from '../config/elemental-types';
+import { ELEMENTAL_TYPES } from '../types';
import { createElementalRoute } from '../utils/custom-routing';
import { filterForElementalClusters } from '../utils/elemental-utils';
import BuildMedia from './BuildMedia';
@@ -54,7 +56,7 @@ export default {
// we need to check for the length of the response
// due to some issue with a standard-user, which can list apps
// but the list comes up empty []
- const isElementalOperatorNotInstalledOnApps = allDispatches.installedApps && allDispatches.installedApps.length && !allDispatches.installedApps.find(item => item.id.includes('elemental-operator'));
+ const isElementalOperatorNotInstalledOnApps = allDispatches.installedApps && allDispatches.installedApps.length && !allDispatches.installedApps.find(item => item.id.includes('elemental-operator') && !item.id.includes('elemental-operator-crds'));
// check if CRD is there but operator isn't
if (allDispatches.elementalSchema && isElementalOperatorNotInstalledOnApps) {
@@ -65,6 +67,8 @@ export default {
return {
ELEMENTAL_CLUSTERS: 'elementalClusters',
isElementalOpNotInstalledAndHasSchema: false,
+ resource: ELEMENTAL_TYPES.DASHBOARD,
+ mode: _VIEW,
resourcesData: {
[`${ ELEMENTAL_SCHEMA_IDS.MACHINE_REGISTRATIONS }`]: [],
[`${ ELEMENTAL_SCHEMA_IDS.MACHINE_INVENTORIES }`]: [],
@@ -283,6 +287,8 @@ export default {
diff --git a/pkg/elemental/detail/elemental.cattle.io.machineregistration.vue b/pkg/elemental/detail/elemental.cattle.io.machineregistration.vue
index 2f8b940..15a39b5 100644
--- a/pkg/elemental/detail/elemental.cattle.io.machineregistration.vue
+++ b/pkg/elemental/detail/elemental.cattle.io.machineregistration.vue
@@ -32,6 +32,10 @@ export default {
type: String,
required: true
},
+ resource: {
+ type: String,
+ required: true
+ },
},
data() {
return {
@@ -115,6 +119,8 @@ export default {
{{ t('elemental.machineRegistration.create.cloudConfiguration') }}
item.id.includes('elemental-operator'));
+ const isElementalOperatorNotInstalledOnApps = installedApps?.length && !installedApps?.find(item => item.id.includes('elemental-operator') && !item.id.includes('elemental-operator-crds'));
// check if operator is installed
if (!elementalSchema || isElementalOperatorNotInstalledOnApps) {
diff --git a/pkg/elemental/utils/feature-versioning.ts b/pkg/elemental/utils/feature-versioning.ts
new file mode 100644
index 0000000..475650b
--- /dev/null
+++ b/pkg/elemental/utils/feature-versioning.ts
@@ -0,0 +1,76 @@
+import semver from 'semver';
+
+import { _CREATE, _VIEW } from '@shell/config/query-params';
+import { WORKLOAD_TYPES } from '@shell/config/types';
+import { ELEMENTAL_SCHEMA_IDS } from '../config/elemental-types';
+import { ELEMENTAL_TYPES } from '../types';
+
+interface FeaturesGatingConfig {
+ area: string,
+ mode: string[],
+ minOperatorVersion: string,
+ features: string[],
+}
+
+// features to be gated to specific operator versions
+export const MACH_REG_CONFIG_DEFAULTS:string = 'machine-reg-config-defaults';
+export const BUILD_MEDIA_RAW_SUPPORT:string = 'build-media-raw-support';
+
+const FEATURES_GATING:FeaturesGatingConfig[] = [
+ {
+ area: ELEMENTAL_SCHEMA_IDS.MACHINE_REGISTRATIONS,
+ mode: [_CREATE],
+ minOperatorVersion: '1.6.0',
+ features: [MACH_REG_CONFIG_DEFAULTS]
+ },
+ {
+ area: ELEMENTAL_TYPES.DASHBOARD,
+ mode: [_VIEW],
+ minOperatorVersion: '1.6.0',
+ features: [BUILD_MEDIA_RAW_SUPPORT]
+ },
+ {
+ area: ELEMENTAL_SCHEMA_IDS.MACHINE_REGISTRATIONS,
+ mode: [_VIEW],
+ minOperatorVersion: '1.6.0',
+ features: [BUILD_MEDIA_RAW_SUPPORT]
+ }
+];
+
+/**
+ * Get the current elemental-operator version
+ * @param any Vue store
+ * @returns Promise
+ */
+export async function getOperatorVersion(store: any): Promise {
+ // needed to check operator version installed (on the deployment)
+ if (store.getters['management/canList'](WORKLOAD_TYPES.DEPLOYMENT)) {
+ const elementalOperatorDeployment = await store.dispatch('management/find', { type: WORKLOAD_TYPES.DEPLOYMENT, id: 'cattle-elemental-system/elemental-operator' });
+
+ return elementalOperatorDeployment?.metadata?.labels?.['app.kubernetes.io/version'] || '0.1.0';
+ }
+
+ return '0.1.0';
+}
+
+/**
+ * Check the gated feature compatibility with the current Elemental Operator version installed
+ * @param string resource type (ex: Deployment)
+ * @param string UI mode (ex: edit, create, view)
+ * @param string Elemental feature (ex: Build media, cloud config)
+ * @param string Elemental Operator version
+ * @returns Boolean
+ */
+export function checkGatedFeatureCompatibility(resource: string, mode: string, feature: string, operatorVersion: string): Boolean {
+ if (resource && mode && feature) {
+ const gatedFeature = FEATURES_GATING.find(feat => feat.area === resource && feat.mode.includes(mode) && feat.features.includes(feature));
+
+ if (!gatedFeature?.minOperatorVersion || !operatorVersion) {
+ return false;
+ }
+
+ return semver.gte(operatorVersion, gatedFeature?.minOperatorVersion);
+ }
+
+ return false;
+}
diff --git a/yarn.lock b/yarn.lock
index 47662b7..dcc4c25 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2969,10 +2969,10 @@
dependencies:
lodash.debounce "4.0.8"
-"@rancher/shell@0.5.1":
- version "0.5.1"
- resolved "https://registry.yarnpkg.com/@rancher/shell/-/shell-0.5.1.tgz#4ee59f4f60b6bba54995f00e289dcd64d97f162a"
- integrity sha512-oCmXZ9MDw9jh7plGmYT9ZnB2gHqs5FLgB22HEgUl46R7t94Tms3Z5VivN1JMS77R++C3jdC/EVpjgisKslK4Pg==
+"@rancher/shell@0.5.2":
+ version "0.5.2"
+ resolved "https://registry.yarnpkg.com/@rancher/shell/-/shell-0.5.2.tgz#ab11c8aa03001ba36734ec94f333d48f56846cdd"
+ integrity sha512-u48zCEhimZKh7WIJDGo7dLbkIV/PbRUE88/hkiQ1+xi5gZRgqO/rFCfiALzJKl35d7cCTXqWNodJnTpvwRn8JA==
dependencies:
"@aws-sdk/client-ec2" "3.1.0"
"@aws-sdk/client-eks" "3.1.0"
@@ -3487,6 +3487,11 @@
resolved "https://registry.yarnpkg.com/@types/relateurl/-/relateurl-0.2.33.tgz#fa174c30100d91e88d7b0ba60cefd7e8c532516f"
integrity sha512-bTQCKsVbIdzLqZhLkF5fcJQreE4y1ro4DIyVrlDNSCJRRwHhB8Z+4zXXa8jN6eDvc2HbRsEYgbvrnGvi54EpSw==
+"@types/semver@^7.5.8":
+ version "7.5.8"
+ resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e"
+ integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==
+
"@types/send@*":
version "0.17.4"
resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a"
@@ -14658,6 +14663,13 @@ semver@^7.0.0, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semve
dependencies:
lru-cache "^6.0.0"
+semver@^7.6.0:
+ version "7.6.0"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d"
+ integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==
+ dependencies:
+ lru-cache "^6.0.0"
+
semver@~7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e"
@@ -15329,7 +15341,7 @@ string-length@^4.0.1:
char-regex "^1.0.2"
strip-ansi "^6.0.0"
-"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
+"string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -15347,6 +15359,15 @@ string-width@^3.0.0, string-width@^3.1.0:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^5.1.0"
+string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
+ version "4.2.3"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+ integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+ dependencies:
+ emoji-regex "^8.0.0"
+ is-fullwidth-code-point "^3.0.0"
+ strip-ansi "^6.0.1"
+
string-width@^5.0.1, string-width@^5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
@@ -15397,7 +15418,7 @@ string_decoder@~1.1.1:
dependencies:
safe-buffer "~5.1.0"
-"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -15418,6 +15439,13 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
dependencies:
ansi-regex "^4.1.0"
+strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+ integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+ dependencies:
+ ansi-regex "^5.0.1"
+
strip-ansi@^7.0.1:
version "7.1.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
@@ -17064,7 +17092,7 @@ worker-rpc@^0.1.0:
dependencies:
microevent.ts "~0.1.1"
-"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
+"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
@@ -17091,6 +17119,15 @@ wrap-ansi@^6.0.0, wrap-ansi@^6.2.0:
string-width "^4.1.0"
strip-ansi "^6.0.0"
+wrap-ansi@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
+ integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
+ dependencies:
+ ansi-styles "^4.0.0"
+ string-width "^4.1.0"
+ strip-ansi "^6.0.0"
+
wrap-ansi@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"