diff --git a/charts/opskubedbcom-druidopsrequest-editor/ui/create-ui.yaml b/charts/opskubedbcom-druidopsrequest-editor/ui/create-ui.yaml
index f4a0e448a7..87ee766452 100644
--- a/charts/opskubedbcom-druidopsrequest-editor/ui/create-ui.yaml
+++ b/charts/opskubedbcom-druidopsrequest-editor/ui/create-ui.yaml
@@ -487,109 +487,176 @@ step:
if:
name: ifRequestTypeEqualsTo|Reconfigure
type: function
+ loader:
+ name: fetchConfigSecrets
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
elements:
- type: block-layout
label: Configuration
elements:
- - type: radio
- label: Reconfigure Type
- subtitle: Choose the action you'd like to perform. Select a new configuration secret, apply a custom configuration, or remove an existing setup.
+ - type: label-element
+ label: ''
+ subtitle: Select a new configuration secret, apply a custom configuration, or remove an existing setup to update your database settings
+ - type: tab-layout
+ label: New Config Secret
+ schema: temp/properties/reconfigurationType
options:
- - text: Select New Config Secret
+ - text: NEW CONFIG SECRET
value: selectNewConfigSecret
- - text: Apply Config
+ - text: APPLY CONFIG
value: applyConfig
- - text: Remove
+ - text: REMOVE
value: remove
- watcher:
- func: onReconfigurationTypeChange
- paths:
- - temp/properties/reconfigurationType
- validation:
- type: required
- schema: temp/properties/reconfigurationType
- - type: block-layout
- label: Config Secret
- showLabels: true
- if:
- name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret
- type: function
elements:
- - type: label-element
- label: Select a configuration secret from the available list to update your database settings
- customClass: mb-10
- - type: select
- addNewButton:
- label: Create Secret
- target: _blank
- url:
- function: createSecretUrl
- label: Config Secret
- loader:
- name: getConfigSecrets
- watchPaths:
- - schema/properties/metadata/properties/namespace
- init:
- type: func
- value: setValueFromDbDetails|/spec/configSecret/name
- refresh: true
- validation:
- type: required
- schema: schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- - type: label-element
- label: Selected Config Secret
- loader: getSelectedConfigSecret
- watcher:
- func: getSelectedConfigSecret
- paths:
- - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- - type: editor
- label: Value
- readonly: true
- hasCopy: false
- watcher:
- func: getSelectedConfigSecretValue
- paths:
- - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- validation:
- type: required
- schema: temp/properties/configArray/items/properties/value
- - type: array-object-form
- label: Apply Config
- buttonClass: is-light is-outlined
- if:
- name: ifReconfigurationTypeEqualsTo|applyConfig
- type: function
- validation:
- type: required
- watcher:
- func: onApplyconfigChange
- paths:
- - temp/properties/applyConfig
- schema: temp/properties/applyConfig
- elements:
- - type: label-element
- label: New Apply Config
- subtitle: Define custom configurations for your database using key-value pairs. These parameters will overwrite existing settings. Enter the parameter you want to configure (e.g., druid.emitter).
- customClass: mb-10
- - type: input
- label: key
- validation:
- type: required
- schema: key
- - type: editor
- hasCopy: false
- label: value
- validation:
- type: required
- schema: value
- - type: switch
- fullwidth: true
- schema: schema/properties/spec/properties/configuration/properties/removeCustomConfig
- label: Remove CustomConfig
- if:
- name: returnFalse
- type: function
+ - type: block-layout
+ label: Config Secret
+ if:
+ name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret
+ type: function
+ elements:
+ - type: label-element
+ label: Config Secret
+ subtitle: Select a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ refresh: true
+ label: Config Secret
+ loader:
+ name: getConfigSecrets
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - temp/properties/createSecret/properties/status
+ init:
+ type: func
+ value: setValueFromDbDetails|/spec/configSecret/name
+ watcher:
+ func: onCreateSecretChange
+ paths:
+ - temp/properties/createSecret/properties/status
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|create
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ - type: block-layout
+ label: Create a New Config Secret
+ showLabels: true
+ if:
+ type: function
+ name: isCreateSecret
+ hasButton:
+ text: Save
+ hasCancel: cancelCreateSecret
+ action: createNewConfigSecret
+ elements:
+ - type: input
+ label: Secret Name
+ schema: temp/properties/createSecret/properties/name
+ validation:
+ type: required
+ - type: array-object-form
+ label: String Data
+ schema: temp/properties/createSecret/properties/data
+ buttonClass: is-light is-outlined
+ validation:
+ type: required
+ elements:
+ - type: select
+ label: Key
+ schema: key
+ loader:
+ name: onSelectedSecretChange
+ watchPaths:
+ - temp/properties/createSecret/properties/data
+ validation:
+ type: required
+ - type: textarea
+ label: Value
+ schema: value
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ if:
+ type: function
+ name: isNotCreateSecret
+ loader:
+ name: onNewConfigSecretChange
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ schema: temp/properties/newConfigSecret
+ - type: block-layout
+ label: ApplyConfig
+ if:
+ name: ifReconfigurationTypeEqualsTo|applyConfig
+ type: function
+ elements:
+ - type: label-element
+ label: New Apply Config
+ subtitle: Define custom configurations for your database using key-value pairs. These parameters will overwrite existing settings.
Enter the parameter you want to configure (e.g., max_connections).
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/selectedConfiguration
+ refresh: true
+ label: Configuration
+ loader: getConfigSecretsforAppyConfig
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|apply
+ watchPaths:
+ - temp/properties/selectedConfiguration
+ - type: multi-file-editor
+ editorHeight: 500px
+ schema: temp/properties/applyConfig
+ loader:
+ name: setApplyConfig
+ watchPaths:
+ - temp/properties/selectedConfiguration
+ watcher:
+ func: onApplyconfigChange
+ paths:
+ - temp/properties/applyConfig
+ - type: block-layout
+ label: Remove
+ if:
+ name: ifReconfigurationTypeEqualsTo|remove
+ type: function
+ elements:
+ - type: label-element
+ label: Remove
+ subtitle: Selected a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/selectedConfigurationRemove
+ refresh: true
+ label: Configuration
+ loader:
+ name: getConfigSecretsforAppyConfig
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|remove
+ watchPaths:
+ - temp/properties/selectedConfigurationRemove
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ init:
+ type: func
+ value: onRemoveConfigChange
+ watcher:
+ func: onRemoveConfigChange
+ paths:
+ - temp/properties/selectedConfigurationRemove
+ schema: temp/properties/removeConfig
# Restart
- type: block-layout
label: TLS
diff --git a/charts/opskubedbcom-druidopsrequest-editor/ui/functions.js b/charts/opskubedbcom-druidopsrequest-editor/ui/functions.js
index 5cf6f25562..d1ef674046 100644
--- a/charts/opskubedbcom-druidopsrequest-editor/ui/functions.js
+++ b/charts/opskubedbcom-druidopsrequest-editor/ui/functions.js
@@ -1,4 +1,4 @@
-const { axios, useOperator, store } = window.vueHelpers || {}
+const { axios, useOperator, store, useToast } = window.vueHelpers || {}
// ============================================================
// MACHINE PROFILES - Predefined Resource Configurations
@@ -323,9 +323,11 @@ const druidNodeTypes = [
let machinesFromPreset = []
let secretArray = []
+const configSecretKeys = ['.properties']
export const useFunc = (model) => {
const route = store.state?.route
+ const toast = useToast()
const { getValue, storeGet, discriminator, setDiscriminatorValue, commit } = useOperator(
model,
@@ -1058,33 +1060,415 @@ export const useFunc = (model) => {
// CONFIGURATION FUNCTIONS
// ============================================================
- /**
- * Fetch all secrets from the current namespace for configuration
- * Populates the global secretArray for later use in config secret value display
- * @returns {Array} List of secrets with text/value properties
- */
- async function getConfigSecrets() {
+ // Fetch and store database Infos
+ // for secret configurations in reconfigure
+ let configSecrets = []
+ let secretConfigData = []
+
+ async function fetchConfigSecrets() {
const owner = storeGet('/route/params/user')
const cluster = storeGet('/route/params/cluster')
const namespace = getValue(model, '/metadata/namespace')
// watchDependency('model#/metadata/namespace')
- const resp = await axios.get(
- `/clusters/${owner}/${cluster}/proxy/core/v1/namespaces/${namespace}/secrets`,
+ const name = getValue(model, '/spec/databaseRef/name')
+ const dbGroup = getValue(model, '/route/params/group')
+ const dbKind = getValue(store.state, '/resource/definition/result/kind')
+ const dbResource = getValue(model, '/route/params/resource')
+ const dbVersion = getValue(model, '/route/params/version')
+
+ try {
+ const resp = await axios.post(
+ `/clusters/${owner}/${cluster}/proxy/ui.kubedb.com/v1alpha1/databaseinfos`,
+ {
+ apiVersion: 'ui.kubedb.com/v1alpha1',
+ kind: 'DatabaseInfo',
+ request: {
+ source: {
+ ref: {
+ name: name,
+ namespace: namespace,
+ },
+ resource: {
+ group: dbGroup,
+ kind: dbKind,
+ name: dbResource,
+ version: dbVersion,
+ },
+ },
+ keys: ['.properties'],
+ },
+ },
+ )
+ configSecrets = resp?.data?.response?.availableSecrets || []
+ secretConfigData = resp?.data?.response?.configurations || []
+ } catch (e) {
+ console.log(e)
+ }
+ }
+
+ async function getConfigSecrets(type) {
+ type = type ? type + '/' : ''
+ const secretStatus = getValue(discriminator, `${type}createSecret/status`)
+ if (secretStatus === 'success') {
+ await fetchConfigSecrets()
+ }
+ const mappedSecrets = configSecrets.map((item) => {
+ return { text: item, value: item }
+ })
+ mappedSecrets.push({ text: '+ Create a new Secret', value: 'Create' })
+ return mappedSecrets
+ }
+
+ async function getConfigSecretsforAppyConfig() {
+ const secrets = secretConfigData.map((item) => {
+ return { text: item.componentName, value: item.componentName }
+ })
+ return secrets
+ }
+
+ function getSelectedConfigurationData(type) {
+ type = type ? type + '/' : ''
+ const path = `/${type}selectedConfiguration`
+ const selectedConfiguration = getValue(discriminator, path)
+
+ if (!selectedConfiguration) {
+ return []
+ }
+
+ const configuration = secretConfigData.find(
+ (item) => item.componentName === selectedConfiguration,
)
- const secrets = (resp && resp.data && resp.data.items) || []
- secretArray = secrets
+ if (!configuration) {
+ return []
+ }
- const filteredSecrets = secrets
+ const result = []
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(configuration.data).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedContent = atob(configuration.data[fileName])
+ result.push({
+ name: fileName,
+ content: decodedContent,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ result.push({
+ name: fileName,
+ content: configuration.data[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
- filteredSecrets.map((item) => {
- const name = (item.metadata && item.metadata.name) || ''
- item.text = name
- item.value = name
- return true
+ // Set the value to the model
+ commit('wizard/model$update', {
+ path: `/temp/${type}applyConfig`,
+ value: result,
+ force: true,
+ })
+
+ return result
+ }
+
+ function getSelectedConfigurationName(configType, type) {
+ type = type ? type + '/' : ''
+ let path = ''
+ if (configType === 'create') path = `/spec/configuration/${type}/configSecret/name`
+ else if (configType === 'apply') path = `/${type}selectedConfiguration`
+ else if (configType === 'remove') path = `/${type}selectedConfigurationRemove`
+
+ const selectedConfiguration =
+ configType === 'create' ? getValue(model, path) : getValue(discriminator, path)
+
+ if (selectedConfiguration)
+ return { subtitle: ` You have selected ${selectedConfiguration} secret` }
+ else return { subtitle: 'No secret selected' }
+ }
+
+ function getSelectedConfigurationValueForRemove(type) {
+ type = type ? type + '/' : ''
+ const path = `/${type}selectedConfigurationRemove`
+ const selectedConfiguration = getValue(discriminator, path)
+
+ if (!selectedConfiguration) {
+ return ''
+ }
+
+ const configuration = secretConfigData.find(
+ (item) => item.componentName === selectedConfiguration,
+ )
+
+ if (!configuration) {
+ return ''
+ }
+
+ let data = {}
+ // Decode base64 and parse YAML for each key in the secret data
+ Object.keys(configuration.data).forEach((item) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(configuration.data[item])
+ // Parse YAML string to object
+ const parsedYaml = yaml.load(decodedString)
+ // Store the parsed object with the filename as key
+ data[item] = parsedYaml
+ } catch (e) {
+ console.error(`Error parsing ${item}:`, e)
+ data[item] = atob(configuration.data[item]) // Fallback to decoded string
+ }
+ })
+
+ // Convert data object back to YAML string
+ return yaml.dump(data)
+ }
+
+ async function createNewConfigSecret(type) {
+ type = type ? type + '/' : ''
+ const { user, cluster } = route.params
+ const url = `/clusters/${user}/${cluster}/resources`
+ const namespace = storeGet('/route/query/namespace') || getValue(model, '/metadata/namespace')
+ const secretName = getValue(discriminator, `${type}createSecret/name`)
+ const secretData = getValue(discriminator, `${type}createSecret/data`)
+ const secretDataObj = Object.fromEntries(secretData.map((item) => [item.key, item.value]))
+
+ try {
+ const res = await axios.post(url, {
+ apiVersion: 'v1',
+ stringData: secretDataObj,
+ kind: 'Secret',
+ metadata: {
+ name: secretName,
+ namespace: namespace,
+ },
+ type: 'Opaque',
+ })
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'success',
+ })
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/lastCreatedSecret`,
+ value: secretName,
+ })
+ toast.success('Secret created successfully')
+ } catch (error) {
+ const errMsg = decodeError(error, 'Failed to create secret')
+ toast.error(errMsg, { timeout: 5000 })
+ cancelCreateSecret()
+ }
+ }
+
+ function decodeError(msg, defaultMsg) {
+ if (typeof msg === 'string') {
+ return msg || defaultMsg
+ }
+ return (
+ (msg.response && msg.response.data && msg.response.data.message) ||
+ (msg.response && msg.response.data) ||
+ (msg.status && msg.status.status) ||
+ defaultMsg
+ )
+ }
+
+ function isCreateSecret(type) {
+ type = type ? type + '/' : ''
+ const selectedSecret = getValue(model, `spec/configuration/${type}configSecret/name`)
+ const res = selectedSecret === 'Create'
+
+ if (res === true) {
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'pending',
+ })
+ }
+ return res
+ }
+
+ function isNotCreateSecret(type) {
+ return !isCreateSecret(type)
+ }
+
+ function onCreateSecretChange(type) {
+ type = type ? type + '/' : ''
+ const secretStatus = getValue(discriminator, `${type}createSecret/status`)
+ if (secretStatus === 'cancelled') return ''
+ else if (secretStatus === 'success') {
+ const name = getValue(discriminator, `${type}createSecret/lastCreatedSecret`)
+
+ const configFound = configSecrets.find((item) => item === name)
+ return configFound ? { text: name, value: name } : ''
+ }
+ }
+
+ function cancelCreateSecret(type) {
+ type = type ? type + '/' : ''
+ commit('wizard/temp$delete', `${type}createSecret/name`)
+ commit('wizard/temp$delete', `${type}createSecret/data`)
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'cancelled',
+ })
+ }
+
+ async function onApplyconfigChange(type) {
+ type = type ? type + '/' : ''
+ const configValue = getValue(discriminator, `${type}applyConfig`)
+
+ if (!configValue) {
+ commit('wizard/model$delete', `/spec/configuration/${type}applyConfig`)
+ return
+ }
+ const tempConfigObj = {}
+ configValue.forEach((item) => {
+ if (item.name) {
+ tempConfigObj[item.name] = item.content
+ }
+ })
+ if (Object.keys(tempConfigObj).length === 0) {
+ commit('wizard/model$delete', `/spec/configuration/${type}applyConfig`)
+ return
+ }
+ commit('wizard/model$update', {
+ path: `/spec/configuration/${type}applyConfig`,
+ value: tempConfigObj,
+ })
+ }
+
+ function setApplyConfig(type) {
+ type = type ? type + '/' : ''
+ const configPath = `/${type}selectedConfiguration`
+ const selectedConfig = getValue(discriminator, configPath)
+ if (!selectedConfig) {
+ return [{ name: '', content: '' }]
+ }
+ const applyconfigData = secretConfigData.find((item) => {
+ if (item.componentName === selectedConfig) {
+ return item
+ }
+ })
+ const { applyConfig } = applyconfigData
+ const configObj = []
+
+ if (applyConfig) {
+ Object.keys(applyConfig).forEach((fileName) => {
+ configObj.push({
+ name: fileName,
+ content: applyConfig[fileName],
+ })
+ })
+ } else {
+ configObj.push({ name: '', content: '' })
+ }
+ return configObj
+ }
+
+ function onRemoveConfigChange(type) {
+ type = type ? type + '/' : ''
+ const configPath = `/${type}selectedConfigurationRemove`
+ const selectedConfig = getValue(discriminator, configPath)
+
+ if (!selectedConfig) {
+ commit('wizard/model$delete', `/spec/configuration/${type}removeCustomConfig`)
+ return [{ name: '', content: '' }]
+ }
+ commit('wizard/model$update', {
+ path: `/spec/configuration/${type}removeCustomConfig`,
+ value: true,
+ })
+
+ const configuration = secretConfigData.find((item) => item.componentName === selectedConfig)
+
+ if (!configuration.data) {
+ return [{ name: '', content: '' }]
+ }
+
+ const configObj = []
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(configuration.data).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(configuration.data[fileName])
+ configObj.push({
+ name: fileName,
+ content: decodedString,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ configObj.push({
+ name: fileName,
+ content: configuration.data[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
+ return configObj
+ }
+
+ async function onNewConfigSecretChange(type) {
+ type = type ? type + '/' : ''
+ const path = `/spec/configuration/${type}configSecret/name`
+ const selectedSecret = getValue(model, path)
+
+ if (!selectedSecret) {
+ commit('wizard/model$delete', `/spec/configuration/${type}configSecret`)
+ return [{ name: '', content: '' }]
+ }
+ if (selectedSecret === 'Create') return [{ name: '', content: '' }]
+
+ const owner = storeGet('/route/params/user')
+ const cluster = storeGet('/route/params/cluster')
+ const namespace = storeGet('/route/query/namespace') || getValue(model, '/metadata/namespace')
+
+ try {
+ // Fetch the secret data from API
+ const secretResp = await axios.get(
+ `/clusters/${owner}/${cluster}/proxy/core/v1/namespaces/${namespace}/secrets/${selectedSecret}`,
+ )
+
+ const secretData = secretResp.data?.data || {}
+ const configObj = []
+
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(secretData).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(secretData[fileName])
+ configObj.push({
+ name: fileName,
+ content: decodedString,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ configObj.push({
+ name: fileName,
+ content: secretData[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
+
+ return configObj
+ } catch (e) {
+ console.error('Error fetching secret:', e)
+ return [{ name: '', content: '' }]
+ }
+ }
+
+ function onSelectedSecretChange(index) {
+ const secretData = getValue(discriminator, 'createSecret/data') || []
+ const selfSecrets = secretData.map((item) => item.key)
+
+ const remainingSecrets = configSecretKeys.filter((item) => !selfSecrets.includes(item))
+
+ const selfKey = getValue(discriminator, `createSecret/data/${index}/key`)
+ if (selfKey) {
+ remainingSecrets.push(selfKey)
+ }
+ const resSecret = remainingSecrets.map((item) => {
+ return { text: item, value: item }
})
- return filteredSecrets
+ return resSecret
}
/**
@@ -1218,26 +1602,6 @@ export const useFunc = (model) => {
return data || 'No Data Found'
}
- function onApplyconfigChange(type) {
- const configPath = `/${type}/applyConfig`
- const applyconfig = getValue(discriminator, configPath)
-
- const configObj = {}
-
- if (applyconfig) {
- applyconfig.forEach((item) => {
- const { key, value } = item
- configObj[key] = value
- })
- }
-
- commit('wizard/model$update', {
- path: `/spec/configuration/${type}/applyConfig`,
- value: configObj,
- force: true,
- })
- }
-
// ============================================================
// RECONFIGURATION FUNCTIONS
// ============================================================
@@ -1744,6 +2108,21 @@ export const useFunc = (model) => {
getSelectedConfigSecretData,
setSelectedConfigSecret,
onApplyconfigChange,
+ fetchConfigSecrets,
+ getConfigSecretsforAppyConfig,
+ getSelectedConfigurationData,
+ getSelectedConfigurationName,
+ getSelectedConfigurationValueForRemove,
+ createNewConfigSecret,
+ decodeError,
+ isCreateSecret,
+ isNotCreateSecret,
+ onCreateSecretChange,
+ cancelCreateSecret,
+ setApplyConfig,
+ onRemoveConfigChange,
+ onNewConfigSecretChange,
+ onSelectedSecretChange,
// Reconfiguration functions
ifReconfigurationTypeEqualsTo,
diff --git a/charts/opskubedbcom-elasticsearchopsrequest-editor/ui/create-ui.yaml b/charts/opskubedbcom-elasticsearchopsrequest-editor/ui/create-ui.yaml
index 77cbe987a8..b3b94b6ed6 100644
--- a/charts/opskubedbcom-elasticsearchopsrequest-editor/ui/create-ui.yaml
+++ b/charts/opskubedbcom-elasticsearchopsrequest-editor/ui/create-ui.yaml
@@ -1137,113 +1137,176 @@ step:
if:
name: ifRequestTypeEqualsTo|Reconfigure
type: function
+ loader:
+ name: fetchConfigSecrets
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
elements:
- type: block-layout
label: Configuration
elements:
- - type: radio
- label: Reconfigure Type
- subtitle: Choose the action you'd like to perform. Select a new configuration secret, apply a custom configuration, or remove an existing setup.
+ - type: label-element
+ label: ''
+ subtitle: Select a new configuration secret, apply a custom configuration, or remove an existing setup to update your database settings
+ - type: tab-layout
+ label: New Config Secret
+ schema: temp/properties/reconfigurationType
options:
- - text: Select New Config Secret
+ - text: NEW CONFIG SECRET
value: selectNewConfigSecret
- - text: Apply Config
+ - text: APPLY CONFIG
value: applyConfig
- - text: Remove
+ - text: REMOVE
value: remove
- watcher:
- func: onReconfigurationTypeChange
- paths:
- - temp/properties/reconfigurationType
- validation:
- type: required
- schema: temp/properties/reconfigurationType
- - type: block-layout
- label: Configuration config secret
- showLabels: true
- if:
- name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret
- type: function
elements:
- - type: label-element
- label: Select a configuration secret from the available list to update your database settings
- customClass: mb-10
- - type: select
- addNewButton:
- label: Create a new Secret
- target: _blank
- url:
- function: createSecretUrl
- label: Config Secret
- loader:
- name: getConfigSecrets
- watchPaths:
- - schema/properties/metadata/properties/namespace
- init:
- type: func
- value: setValueFromDbDetails|/spec/configSecret/name
- refresh: true
- validation:
- type: required
- schema: schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- - type: label-element
- label: Selected Config Secret
- loader: getSelectedConfigSecret
- watcher:
- func: getSelectedConfigSecret
- paths:
- - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- - type: editor
- label: Value
- readonly: true
- hasCopy: false
- if:
- name: isConfigSelected
- type: function
- watcher:
- func: getSelectedConfigSecretValue
- paths:
- - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- validation:
- type: required
- schema: temp/properties/configArray/items/properties/value
- - type: array-object-form
- label: ApplyConfig
- buttonClass: is-light is-outlined
- if:
- name: ifReconfigurationTypeEqualsTo|applyConfig
- type: function
- validation:
- type: required
- watcher:
- func: onApplyconfigChange
- paths:
- - temp/properties/applyConfig
- schema: temp/properties/applyConfig
- elements:
- - type: label-element
- label: New Apply Config
- subtitle: Define custom configurations for your database using key-value pairs. These parameters will overwrite existing settings. Enter the parameter you want to configure (e.g., max_connections).
- customClass: mb-10
- - type: input
- label: key
- validation:
- type: required
- schema: key
- - type: editor
- hasCopy: false
- label: value
- validation:
- type: required
- schema: value
- - type: switch
- schema: schema/properties/spec/properties/configuration/properties/removeCustomConfig
- label: Remove CustomConfig
- fullwidth: true
- if:
- name: returnFalse
- type: function
-
+ - type: block-layout
+ label: Config Secret
+ if:
+ name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret
+ type: function
+ elements:
+ - type: label-element
+ label: Config Secret
+ subtitle: Select a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ refresh: true
+ label: Config Secret
+ loader:
+ name: getConfigSecrets
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - temp/properties/createSecret/properties/status
+ init:
+ type: func
+ value: setValueFromDbDetails|/spec/configSecret/name
+ watcher:
+ func: onCreateSecretChange
+ paths:
+ - temp/properties/createSecret/properties/status
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|create
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ - type: block-layout
+ label: Create a New Config Secret
+ showLabels: true
+ if:
+ type: function
+ name: isCreateSecret
+ hasButton:
+ text: Save
+ hasCancel: cancelCreateSecret
+ action: createNewConfigSecret
+ elements:
+ - type: input
+ label: Secret Name
+ schema: temp/properties/createSecret/properties/name
+ validation:
+ type: required
+ - type: array-object-form
+ label: String Data
+ schema: temp/properties/createSecret/properties/data
+ buttonClass: is-light is-outlined
+ validation:
+ type: required
+ elements:
+ - type: select
+ label: Key
+ schema: key
+ loader:
+ name: onSelectedSecretChange
+ watchPaths:
+ - temp/properties/createSecret/properties/data
+ validation:
+ type: required
+ - type: textarea
+ label: Value
+ schema: value
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ if:
+ type: function
+ name: isNotCreateSecret
+ loader:
+ name: onNewConfigSecretChange
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ schema: temp/properties/newConfigSecret
+ - type: block-layout
+ label: ApplyConfig
+ if:
+ name: ifReconfigurationTypeEqualsTo|applyConfig
+ type: function
+ elements:
+ - type: label-element
+ label: New Apply Config
+ subtitle: Define custom configurations for your database using key-value pairs. These parameters will overwrite existing settings.
Enter the parameter you want to configure (e.g., max_connections).
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/selectedConfiguration
+ refresh: true
+ label: Configuration
+ loader: getConfigSecretsforAppyConfig
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|apply
+ watchPaths:
+ - temp/properties/selectedConfiguration
+ - type: multi-file-editor
+ editorHeight: 500px
+ schema: temp/properties/applyConfig
+ loader:
+ name: setApplyConfig
+ watchPaths:
+ - temp/properties/selectedConfiguration
+ watcher:
+ func: onApplyconfigChange
+ paths:
+ - temp/properties/applyConfig
+ - type: block-layout
+ label: Remove
+ if:
+ name: ifReconfigurationTypeEqualsTo|remove
+ type: function
+ elements:
+ - type: label-element
+ label: Remove
+ subtitle: Selected a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/selectedConfigurationRemove
+ refresh: true
+ label: Configuration
+ loader:
+ name: getConfigSecretsforAppyConfig
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|remove
+ watchPaths:
+ - temp/properties/selectedConfigurationRemove
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ init:
+ type: func
+ value: onRemoveConfigChange
+ watcher:
+ func: onRemoveConfigChange
+ paths:
+ - temp/properties/selectedConfigurationRemove
+ schema: temp/properties/removeConfig
# Reconfigure TLS
- type: block-layout
label: TLS
diff --git a/charts/opskubedbcom-elasticsearchopsrequest-editor/ui/functions.js b/charts/opskubedbcom-elasticsearchopsrequest-editor/ui/functions.js
index 7dc32974af..422b157eeb 100644
--- a/charts/opskubedbcom-elasticsearchopsrequest-editor/ui/functions.js
+++ b/charts/opskubedbcom-elasticsearchopsrequest-editor/ui/functions.js
@@ -1,4 +1,4 @@
-const { axios, useOperator, store } = window.vueHelpers || {}
+const { axios, useOperator, store, useToast } = window.vueHelpers || {}
// =====================================================
// Machine Profiles Configuration
@@ -314,9 +314,11 @@ const machineList = [
// =====================================================
let machinesFromPreset = []
let secretArray = []
+const configSecretKeys = ['elasticsearch.yml', 'data-elasticsearch.yml', 'ingest-elasticsearch.yml']
export const useFunc = (model) => {
const route = store.state?.route
+ const toast = useToast()
const { getValue, storeGet, discriminator, setDiscriminatorValue, commit } = useOperator(
model,
@@ -1089,28 +1091,415 @@ export const useFunc = (model) => {
// Configuration/Reconfiguration Functions
// =====================================================
- async function getConfigSecrets() {
+ // Fetch and store database Infos
+ // for secret configurations in reconfigure
+ let configSecrets = []
+ let secretConfigData = []
+
+ async function fetchConfigSecrets() {
const owner = storeGet('/route/params/user')
const cluster = storeGet('/route/params/cluster')
const namespace = getValue(model, '/metadata/namespace')
// watchDependency('model#/metadata/namespace')
- const resp = await axios.get(
- `/clusters/${owner}/${cluster}/proxy/core/v1/namespaces/${namespace}/secrets`,
+ const name = getValue(model, '/spec/databaseRef/name')
+ const dbGroup = getValue(model, '/route/params/group')
+ const dbKind = getValue(store.state, '/resource/definition/result/kind')
+ const dbResource = getValue(model, '/route/params/resource')
+ const dbVersion = getValue(model, '/route/params/version')
+
+ try {
+ const resp = await axios.post(
+ `/clusters/${owner}/${cluster}/proxy/ui.kubedb.com/v1alpha1/databaseinfos`,
+ {
+ apiVersion: 'ui.kubedb.com/v1alpha1',
+ kind: 'DatabaseInfo',
+ request: {
+ source: {
+ ref: {
+ name: name,
+ namespace: namespace,
+ },
+ resource: {
+ group: dbGroup,
+ kind: dbKind,
+ name: dbResource,
+ version: dbVersion,
+ },
+ },
+ keys: ['elasticsearch.yml', 'data-elasticsearch.yml', 'ingest-elasticsearch.yml'],
+ },
+ },
+ )
+ configSecrets = resp?.data?.response?.availableSecrets || []
+ secretConfigData = resp?.data?.response?.configurations || []
+ } catch (e) {
+ console.log(e)
+ }
+ }
+
+ async function getConfigSecrets(type) {
+ type = type ? type + '/' : ''
+ const secretStatus = getValue(discriminator, `${type}createSecret/status`)
+ if (secretStatus === 'success') {
+ await fetchConfigSecrets()
+ }
+ const mappedSecrets = configSecrets.map((item) => {
+ return { text: item, value: item }
+ })
+ mappedSecrets.push({ text: '+ Create a new Secret', value: 'Create' })
+ return mappedSecrets
+ }
+
+ async function getConfigSecretsforAppyConfig() {
+ const secrets = secretConfigData.map((item) => {
+ return { text: item.componentName, value: item.componentName }
+ })
+ return secrets
+ }
+
+ function getSelectedConfigurationData(type) {
+ type = type ? type + '/' : ''
+ const path = `/${type}selectedConfiguration`
+ const selectedConfiguration = getValue(discriminator, path)
+
+ if (!selectedConfiguration) {
+ return []
+ }
+
+ const configuration = secretConfigData.find(
+ (item) => item.componentName === selectedConfiguration,
)
- const secrets = (resp && resp.data && resp.data.items) || []
- secretArray = secrets
+ if (!configuration) {
+ return []
+ }
+
+ const result = []
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(configuration.data).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedContent = atob(configuration.data[fileName])
+ result.push({
+ name: fileName,
+ content: decodedContent,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ result.push({
+ name: fileName,
+ content: configuration.data[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
- const filteredSecrets = secrets
+ // Set the value to the model
+ commit('wizard/model$update', {
+ path: `/temp/${type}applyConfig`,
+ value: result,
+ force: true,
+ })
- filteredSecrets.map((item) => {
- const name = (item.metadata && item.metadata.name) || ''
- item.text = name
- item.value = name
- return true
+ return result
+ }
+
+ function getSelectedConfigurationName(configType, type) {
+ type = type ? type + '/' : ''
+ let path = ''
+ if (configType === 'create') path = `/spec/configuration/${type}/configSecret/name`
+ else if (configType === 'apply') path = `/${type}selectedConfiguration`
+ else if (configType === 'remove') path = `/${type}selectedConfigurationRemove`
+
+ const selectedConfiguration =
+ configType === 'create' ? getValue(model, path) : getValue(discriminator, path)
+
+ if (selectedConfiguration)
+ return { subtitle: ` You have selected ${selectedConfiguration} secret` }
+ else return { subtitle: 'No secret selected' }
+ }
+
+ function getSelectedConfigurationValueForRemove(type) {
+ type = type ? type + '/' : ''
+ const path = `/${type}selectedConfigurationRemove`
+ const selectedConfiguration = getValue(discriminator, path)
+
+ if (!selectedConfiguration) {
+ return ''
+ }
+
+ const configuration = secretConfigData.find(
+ (item) => item.componentName === selectedConfiguration,
+ )
+
+ if (!configuration) {
+ return ''
+ }
+
+ let data = {}
+ // Decode base64 and parse YAML for each key in the secret data
+ Object.keys(configuration.data).forEach((item) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(configuration.data[item])
+ // Parse YAML string to object
+ const parsedYaml = yaml.load(decodedString)
+ // Store the parsed object with the filename as key
+ data[item] = parsedYaml
+ } catch (e) {
+ console.error(`Error parsing ${item}:`, e)
+ data[item] = atob(configuration.data[item]) // Fallback to decoded string
+ }
+ })
+
+ // Convert data object back to YAML string
+ return yaml.dump(data)
+ }
+
+ async function createNewConfigSecret(type) {
+ type = type ? type + '/' : ''
+ const { user, cluster } = route.params
+ const url = `/clusters/${user}/${cluster}/resources`
+ const namespace = storeGet('/route/query/namespace') || getValue(model, '/metadata/namespace')
+ const secretName = getValue(discriminator, `${type}createSecret/name`)
+ const secretData = getValue(discriminator, `${type}createSecret/data`)
+ const secretDataObj = Object.fromEntries(secretData.map((item) => [item.key, item.value]))
+
+ try {
+ const res = await axios.post(url, {
+ apiVersion: 'v1',
+ stringData: secretDataObj,
+ kind: 'Secret',
+ metadata: {
+ name: secretName,
+ namespace: namespace,
+ },
+ type: 'Opaque',
+ })
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'success',
+ })
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/lastCreatedSecret`,
+ value: secretName,
+ })
+ toast.success('Secret created successfully')
+ } catch (error) {
+ const errMsg = decodeError(error, 'Failed to create secret')
+ toast.error(errMsg, { timeout: 5000 })
+ cancelCreateSecret()
+ }
+ }
+
+ function decodeError(msg, defaultMsg) {
+ if (typeof msg === 'string') {
+ return msg || defaultMsg
+ }
+ return (
+ (msg.response && msg.response.data && msg.response.data.message) ||
+ (msg.response && msg.response.data) ||
+ (msg.status && msg.status.status) ||
+ defaultMsg
+ )
+ }
+
+ function isCreateSecret(type) {
+ type = type ? type + '/' : ''
+ const selectedSecret = getValue(model, `spec/configuration/${type}configSecret/name`)
+ const res = selectedSecret === 'Create'
+
+ if (res === true) {
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'pending',
+ })
+ }
+ return res
+ }
+
+ function isNotCreateSecret(type) {
+ return !isCreateSecret(type)
+ }
+
+ function onCreateSecretChange(type) {
+ type = type ? type + '/' : ''
+ const secretStatus = getValue(discriminator, `${type}createSecret/status`)
+ if (secretStatus === 'cancelled') return ''
+ else if (secretStatus === 'success') {
+ const name = getValue(discriminator, `${type}createSecret/lastCreatedSecret`)
+
+ const configFound = configSecrets.find((item) => item === name)
+ return configFound ? { text: name, value: name } : ''
+ }
+ }
+
+ function cancelCreateSecret(type) {
+ type = type ? type + '/' : ''
+ commit('wizard/temp$delete', `${type}createSecret/name`)
+ commit('wizard/temp$delete', `${type}createSecret/data`)
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'cancelled',
+ })
+ }
+
+ async function onApplyconfigChange(type) {
+ type = type ? type + '/' : ''
+ const configValue = getValue(discriminator, `${type}applyConfig`)
+
+ if (!configValue) {
+ commit('wizard/model$delete', `/spec/configuration/${type}applyConfig`)
+ return
+ }
+ const tempConfigObj = {}
+ configValue.forEach((item) => {
+ if (item.name) {
+ tempConfigObj[item.name] = item.content
+ }
+ })
+ if (Object.keys(tempConfigObj).length === 0) {
+ commit('wizard/model$delete', `/spec/configuration/${type}applyConfig`)
+ return
+ }
+ commit('wizard/model$update', {
+ path: `/spec/configuration/${type}applyConfig`,
+ value: tempConfigObj,
+ })
+ }
+
+ function setApplyConfig(type) {
+ type = type ? type + '/' : ''
+ const configPath = `/${type}selectedConfiguration`
+ const selectedConfig = getValue(discriminator, configPath)
+ if (!selectedConfig) {
+ return [{ name: '', content: '' }]
+ }
+ const applyconfigData = secretConfigData.find((item) => {
+ if (item.componentName === selectedConfig) {
+ return item
+ }
})
- return filteredSecrets
+ const { applyConfig } = applyconfigData
+ const configObj = []
+
+ if (applyConfig) {
+ Object.keys(applyConfig).forEach((fileName) => {
+ configObj.push({
+ name: fileName,
+ content: applyConfig[fileName],
+ })
+ })
+ } else {
+ configObj.push({ name: '', content: '' })
+ }
+ return configObj
+ }
+
+ function onRemoveConfigChange(type) {
+ type = type ? type + '/' : ''
+ const configPath = `/${type}selectedConfigurationRemove`
+ const selectedConfig = getValue(discriminator, configPath)
+
+ if (!selectedConfig) {
+ commit('wizard/model$delete', `/spec/configuration/${type}removeCustomConfig`)
+ return [{ name: '', content: '' }]
+ }
+ commit('wizard/model$update', {
+ path: `/spec/configuration/${type}removeCustomConfig`,
+ value: true,
+ })
+
+ const configuration = secretConfigData.find((item) => item.componentName === selectedConfig)
+
+ if (!configuration.data) {
+ return [{ name: '', content: '' }]
+ }
+
+ const configObj = []
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(configuration.data).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(configuration.data[fileName])
+ configObj.push({
+ name: fileName,
+ content: decodedString,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ configObj.push({
+ name: fileName,
+ content: configuration.data[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
+ return configObj
+ }
+
+ async function onNewConfigSecretChange(type) {
+ type = type ? type + '/' : ''
+ const path = `/spec/configuration/${type}configSecret/name`
+ const selectedSecret = getValue(model, path)
+
+ if (!selectedSecret) {
+ commit('wizard/model$delete', `/spec/configuration/${type}configSecret`)
+ return [{ name: '', content: '' }]
+ }
+ if (selectedSecret === 'Create') return [{ name: '', content: '' }]
+
+ const owner = storeGet('/route/params/user')
+ const cluster = storeGet('/route/params/cluster')
+ const namespace = storeGet('/route/query/namespace') || getValue(model, '/metadata/namespace')
+
+ try {
+ // Fetch the secret data from API
+ const secretResp = await axios.get(
+ `/clusters/${owner}/${cluster}/proxy/core/v1/namespaces/${namespace}/secrets/${selectedSecret}`,
+ )
+
+ const secretData = secretResp.data?.data || {}
+ const configObj = []
+
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(secretData).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(secretData[fileName])
+ configObj.push({
+ name: fileName,
+ content: decodedString,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ configObj.push({
+ name: fileName,
+ content: secretData[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
+
+ return configObj
+ } catch (e) {
+ console.error('Error fetching secret:', e)
+ return [{ name: '', content: '' }]
+ }
+ }
+
+ function onSelectedSecretChange(index) {
+ const secretData = getValue(discriminator, 'createSecret/data') || []
+ const selfSecrets = secretData.map((item) => item.key)
+
+ const remainingSecrets = configSecretKeys.filter((item) => !selfSecrets.includes(item))
+
+ const selfKey = getValue(discriminator, `createSecret/data/${index}/key`)
+ if (selfKey) {
+ remainingSecrets.push(selfKey)
+ }
+ const resSecret = remainingSecrets.map((item) => {
+ return { text: item, value: item }
+ })
+ return resSecret
}
function getSelectedConfigSecret(type) {
@@ -1218,26 +1607,6 @@ export const useFunc = (model) => {
}
}
- function onApplyconfigChange(type) {
- const configPath = `/${type}/applyConfig`
- const applyconfig = getValue(discriminator, configPath)
-
- const configObj = {}
-
- if (applyconfig) {
- applyconfig.forEach((item) => {
- const { key, value } = item
- configObj[key] = value
- })
- }
-
- commit('wizard/model$update', {
- path: `/spec/configuration/${type}/applyConfig`,
- value: configObj,
- force: true,
- })
- }
-
// =====================================================
// TLS Functions
// =====================================================
@@ -1660,6 +2029,21 @@ export const useFunc = (model) => {
ifReconfigurationTypeEqualsTo,
onReconfigurationTypeChange,
onApplyconfigChange,
+ fetchConfigSecrets,
+ getConfigSecretsforAppyConfig,
+ getSelectedConfigurationData,
+ getSelectedConfigurationName,
+ getSelectedConfigurationValueForRemove,
+ createNewConfigSecret,
+ decodeError,
+ isCreateSecret,
+ isNotCreateSecret,
+ onCreateSecretChange,
+ cancelCreateSecret,
+ setApplyConfig,
+ onRemoveConfigChange,
+ onNewConfigSecretChange,
+ onSelectedSecretChange,
// TLS functions
getDbTls,
diff --git a/charts/opskubedbcom-kafkaopsrequest-editor/ui/create-ui.yaml b/charts/opskubedbcom-kafkaopsrequest-editor/ui/create-ui.yaml
index 176e78fcda..6754078f21 100644
--- a/charts/opskubedbcom-kafkaopsrequest-editor/ui/create-ui.yaml
+++ b/charts/opskubedbcom-kafkaopsrequest-editor/ui/create-ui.yaml
@@ -442,108 +442,176 @@ step:
if:
name: ifRequestTypeEqualsTo|Reconfigure
type: function
+ loader:
+ name: fetchConfigSecrets
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
elements:
- type: block-layout
label: Configuration
elements:
- - type: radio
- label: Reconfigure Type
- subtitle: Choose the action you'd like to perform. Select a new configuration secret, apply a custom configuration, or remove an existing setup.
+ - type: label-element
+ label: ''
+ subtitle: Select a new configuration secret, apply a custom configuration, or remove an existing setup to update your database settings
+ - type: tab-layout
+ label: New Config Secret
+ schema: temp/properties/reconfigurationType
options:
- - text: Select New Config Secret
+ - text: NEW CONFIG SECRET
value: selectNewConfigSecret
- - text: Apply Config
+ - text: APPLY CONFIG
value: applyConfig
- - text: Remove
+ - text: REMOVE
value: remove
- watcher:
- func: onReconfigurationTypeChange
- paths:
- - temp/properties/reconfigurationType
- validation:
- type: required
- schema: temp/properties/reconfigurationType
- - type: block-layout
- label: Configuration config secret
- showLabels: true
- if:
- name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret
- type: function
- elements:
- - type: label-element
- label: Select a configuration secret from the available list to update your database settings
- customClass: mb-10
- - type: select
- addNewButton:
- label: Create a new Secret
- target: _blank
- url:
- function: createSecretUrl
- label: Config Secret
- loader:
- name: getConfigSecrets
- watchPaths:
- - schema/properties/metadata/properties/namespace
- init:
- type: func
- value: setValueFromDbDetails|/spec/configSecret/name
- refresh: true
- validation:
- type: required
- schema: schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- - type: label-element
- label: Selected Config Secret
- loader: getSelectedConfigSecret|node
- watcher:
- func: getSelectedConfigSecret|node
- paths:
- - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- - type: editor
- label: ''
- readonly: true
- hasCopy: false
- loader: getSelectedConfigSecretValue|node
- watcher:
- func: getSelectedConfigSecretValue|node
- paths:
- - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- schema: temp/properties/selectedConfigSecretData
- - type: array-object-form
- label: Apply Config
- if:
- name: ifReconfigurationTypeEqualsTo|applyConfig
- type: function
- validation:
- type: required
- watcher:
- func: onApplyconfigChange
- paths:
- - temp/properties/applyConfig
- schema: temp/properties/applyConfig
- buttonClass: is-light is-outlined
elements:
- - type: label-element
- label: New Apply Config
- subtitle: Define custom configurations for your database using key-value pairs. These parameters will overwrite existing settings. Enter the parameter you want to configure (e.g., max_connections).
- customClass: mb-10
- - type: input
- label: key
- validation:
- type: required
- schema: key
- - type: editor
- hasCopy: false
- label: value
- validation:
- type: required
- schema: value
- - type: switch
- schema: schema/properties/spec/properties/configuration/properties/removeCustomConfig
- label: Remove CustomConfig
- fullwidth: true
- if:
- name: returnFalse
- type: function
+ - type: block-layout
+ label: Config Secret
+ if:
+ name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret
+ type: function
+ elements:
+ - type: label-element
+ label: Config Secret
+ subtitle: Select a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ refresh: true
+ label: Config Secret
+ loader:
+ name: getConfigSecrets
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - temp/properties/createSecret/properties/status
+ init:
+ type: func
+ value: setValueFromDbDetails|/spec/configSecret/name
+ watcher:
+ func: onCreateSecretChange
+ paths:
+ - temp/properties/createSecret/properties/status
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|create
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ - type: block-layout
+ label: Create a New Config Secret
+ showLabels: true
+ if:
+ type: function
+ name: isCreateSecret
+ hasButton:
+ text: Save
+ hasCancel: cancelCreateSecret
+ action: createNewConfigSecret
+ elements:
+ - type: input
+ label: Secret Name
+ schema: temp/properties/createSecret/properties/name
+ validation:
+ type: required
+ - type: array-object-form
+ label: String Data
+ schema: temp/properties/createSecret/properties/data
+ buttonClass: is-light is-outlined
+ validation:
+ type: required
+ elements:
+ - type: select
+ label: Key
+ schema: key
+ loader:
+ name: onSelectedSecretChange
+ watchPaths:
+ - temp/properties/createSecret/properties/data
+ validation:
+ type: required
+ - type: textarea
+ label: Value
+ schema: value
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ if:
+ type: function
+ name: isNotCreateSecret
+ loader:
+ name: onNewConfigSecretChange
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ schema: temp/properties/newConfigSecret
+ - type: block-layout
+ label: ApplyConfig
+ if:
+ name: ifReconfigurationTypeEqualsTo|applyConfig
+ type: function
+ elements:
+ - type: label-element
+ label: New Apply Config
+ subtitle: Define custom configurations for your database using key-value pairs. These parameters will overwrite existing settings.
Enter the parameter you want to configure (e.g., max_connections).
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/selectedConfiguration
+ refresh: true
+ label: Configuration
+ loader: getConfigSecretsforAppyConfig
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|apply
+ watchPaths:
+ - temp/properties/selectedConfiguration
+ - type: multi-file-editor
+ editorHeight: 500px
+ schema: temp/properties/applyConfig
+ loader:
+ name: setApplyConfig
+ watchPaths:
+ - temp/properties/selectedConfiguration
+ watcher:
+ func: onApplyconfigChange
+ paths:
+ - temp/properties/applyConfig
+ - type: block-layout
+ label: Remove
+ if:
+ name: ifReconfigurationTypeEqualsTo|remove
+ type: function
+ elements:
+ - type: label-element
+ label: Remove
+ subtitle: Selected a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/selectedConfigurationRemove
+ refresh: true
+ label: Configuration
+ loader:
+ name: getConfigSecretsforAppyConfig
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|remove
+ watchPaths:
+ - temp/properties/selectedConfigurationRemove
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ init:
+ type: func
+ value: onRemoveConfigChange
+ watcher:
+ func: onRemoveConfigChange
+ paths:
+ - temp/properties/selectedConfigurationRemove
+ schema: temp/properties/removeConfig
# Reconfigure TLS
- type: block-layout
label: TLS
diff --git a/charts/opskubedbcom-kafkaopsrequest-editor/ui/functions.js b/charts/opskubedbcom-kafkaopsrequest-editor/ui/functions.js
index 5221da8421..c9880fb815 100644
--- a/charts/opskubedbcom-kafkaopsrequest-editor/ui/functions.js
+++ b/charts/opskubedbcom-kafkaopsrequest-editor/ui/functions.js
@@ -1,4 +1,4 @@
-const { axios, useOperator, store } = window.vueHelpers || {}
+const { axios, useOperator, store, useToast } = window.vueHelpers || {}
const machines = {
'db.t.micro': {
resources: {
@@ -307,9 +307,11 @@ const machineList = [
let machinesFromPreset = []
let secretArray = []
+const configSecretKeys = ['server.properties', 'broker.properties', 'controller.properties']
export const useFunc = (model) => {
const route = store.state?.route
+ const toast = useToast()
const { getValue, storeGet, discriminator, setDiscriminatorValue, commit } = useOperator(
model,
@@ -878,30 +880,415 @@ export const useFunc = (model) => {
return machine === 'custom'
}
- // for config secret
- async function getConfigSecrets() {
+ // Fetch and store database Infos
+ // for secret configurations in reconfigure
+ let configSecrets = []
+ let secretConfigData = []
+
+ async function fetchConfigSecrets() {
const owner = storeGet('/route/params/user')
const cluster = storeGet('/route/params/cluster')
const namespace = getValue(model, '/metadata/namespace')
// watchDependency('model#/metadata/namespace')
- const resp = await axios.get(
- `/clusters/${owner}/${cluster}/proxy/core/v1/namespaces/${namespace}/secrets`,
+ const name = getValue(model, '/spec/databaseRef/name')
+ const dbGroup = getValue(model, '/route/params/group')
+ const dbKind = getValue(store.state, '/resource/definition/result/kind')
+ const dbResource = getValue(model, '/route/params/resource')
+ const dbVersion = getValue(model, '/route/params/version')
+
+ try {
+ const resp = await axios.post(
+ `/clusters/${owner}/${cluster}/proxy/ui.kubedb.com/v1alpha1/databaseinfos`,
+ {
+ apiVersion: 'ui.kubedb.com/v1alpha1',
+ kind: 'DatabaseInfo',
+ request: {
+ source: {
+ ref: {
+ name: name,
+ namespace: namespace,
+ },
+ resource: {
+ group: dbGroup,
+ kind: dbKind,
+ name: dbResource,
+ version: dbVersion,
+ },
+ },
+ keys: ['server.properties', 'broker.properties', 'controller.properties'],
+ },
+ },
+ )
+ configSecrets = resp?.data?.response?.availableSecrets || []
+ secretConfigData = resp?.data?.response?.configurations || []
+ } catch (e) {
+ console.log(e)
+ }
+ }
+
+ async function getConfigSecrets(type) {
+ type = type ? type + '/' : ''
+ const secretStatus = getValue(discriminator, `${type}createSecret/status`)
+ if (secretStatus === 'success') {
+ await fetchConfigSecrets()
+ }
+ const mappedSecrets = configSecrets.map((item) => {
+ return { text: item, value: item }
+ })
+ mappedSecrets.push({ text: '+ Create a new Secret', value: 'Create' })
+ return mappedSecrets
+ }
+
+ async function getConfigSecretsforAppyConfig() {
+ const secrets = secretConfigData.map((item) => {
+ return { text: item.componentName, value: item.componentName }
+ })
+ return secrets
+ }
+
+ function getSelectedConfigurationData(type) {
+ type = type ? type + '/' : ''
+ const path = `/${type}selectedConfiguration`
+ const selectedConfiguration = getValue(discriminator, path)
+
+ if (!selectedConfiguration) {
+ return []
+ }
+
+ const configuration = secretConfigData.find(
+ (item) => item.componentName === selectedConfiguration,
)
- const secrets = (resp && resp.data && resp.data.items) || []
+ if (!configuration) {
+ return []
+ }
- const filteredSecrets = secrets
+ const result = []
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(configuration.data).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedContent = atob(configuration.data[fileName])
+ result.push({
+ name: fileName,
+ content: decodedContent,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ result.push({
+ name: fileName,
+ content: configuration.data[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
- secretArray = secrets
+ // Set the value to the model
+ commit('wizard/model$update', {
+ path: `/temp/${type}applyConfig`,
+ value: result,
+ force: true,
+ })
- filteredSecrets.map((item) => {
- const name = (item.metadata && item.metadata.name) || ''
- item.text = name
- item.value = name
- return true
+ return result
+ }
+
+ function getSelectedConfigurationName(configType, type) {
+ type = type ? type + '/' : ''
+ let path = ''
+ if (configType === 'create') path = `/spec/configuration/${type}/configSecret/name`
+ else if (configType === 'apply') path = `/${type}selectedConfiguration`
+ else if (configType === 'remove') path = `/${type}selectedConfigurationRemove`
+
+ const selectedConfiguration =
+ configType === 'create' ? getValue(model, path) : getValue(discriminator, path)
+
+ if (selectedConfiguration)
+ return { subtitle: ` You have selected ${selectedConfiguration} secret` }
+ else return { subtitle: 'No secret selected' }
+ }
+
+ function getSelectedConfigurationValueForRemove(type) {
+ type = type ? type + '/' : ''
+ const path = `/${type}selectedConfigurationRemove`
+ const selectedConfiguration = getValue(discriminator, path)
+
+ if (!selectedConfiguration) {
+ return ''
+ }
+
+ const configuration = secretConfigData.find(
+ (item) => item.componentName === selectedConfiguration,
+ )
+
+ if (!configuration) {
+ return ''
+ }
+
+ let data = {}
+ // Decode base64 and parse YAML for each key in the secret data
+ Object.keys(configuration.data).forEach((item) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(configuration.data[item])
+ // Parse YAML string to object
+ const parsedYaml = yaml.load(decodedString)
+ // Store the parsed object with the filename as key
+ data[item] = parsedYaml
+ } catch (e) {
+ console.error(`Error parsing ${item}:`, e)
+ data[item] = atob(configuration.data[item]) // Fallback to decoded string
+ }
})
- return filteredSecrets
+
+ // Convert data object back to YAML string
+ return yaml.dump(data)
+ }
+
+ async function createNewConfigSecret(type) {
+ type = type ? type + '/' : ''
+ const { user, cluster } = route.params
+ const url = `/clusters/${user}/${cluster}/resources`
+ const namespace = storeGet('/route/query/namespace') || getValue(model, '/metadata/namespace')
+ const secretName = getValue(discriminator, `${type}createSecret/name`)
+ const secretData = getValue(discriminator, `${type}createSecret/data`)
+ const secretDataObj = Object.fromEntries(secretData.map((item) => [item.key, item.value]))
+
+ try {
+ const res = await axios.post(url, {
+ apiVersion: 'v1',
+ stringData: secretDataObj,
+ kind: 'Secret',
+ metadata: {
+ name: secretName,
+ namespace: namespace,
+ },
+ type: 'Opaque',
+ })
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'success',
+ })
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/lastCreatedSecret`,
+ value: secretName,
+ })
+ toast.success('Secret created successfully')
+ } catch (error) {
+ const errMsg = decodeError(error, 'Failed to create secret')
+ toast.error(errMsg, { timeout: 5000 })
+ cancelCreateSecret()
+ }
+ }
+
+ function decodeError(msg, defaultMsg) {
+ if (typeof msg === 'string') {
+ return msg || defaultMsg
+ }
+ return (
+ (msg.response && msg.response.data && msg.response.data.message) ||
+ (msg.response && msg.response.data) ||
+ (msg.status && msg.status.status) ||
+ defaultMsg
+ )
+ }
+
+ function isCreateSecret(type) {
+ type = type ? type + '/' : ''
+ const selectedSecret = getValue(model, `spec/configuration/${type}configSecret/name`)
+ const res = selectedSecret === 'Create'
+
+ if (res === true) {
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'pending',
+ })
+ }
+ return res
+ }
+
+ function isNotCreateSecret(type) {
+ return !isCreateSecret(type)
+ }
+
+ function onCreateSecretChange(type) {
+ type = type ? type + '/' : ''
+ const secretStatus = getValue(discriminator, `${type}createSecret/status`)
+ if (secretStatus === 'cancelled') return ''
+ else if (secretStatus === 'success') {
+ const name = getValue(discriminator, `${type}createSecret/lastCreatedSecret`)
+
+ const configFound = configSecrets.find((item) => item === name)
+ return configFound ? { text: name, value: name } : ''
+ }
+ }
+
+ function cancelCreateSecret(type) {
+ type = type ? type + '/' : ''
+ commit('wizard/temp$delete', `${type}createSecret/name`)
+ commit('wizard/temp$delete', `${type}createSecret/data`)
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'cancelled',
+ })
+ }
+
+ async function onApplyconfigChange(type) {
+ type = type ? type + '/' : ''
+ const configValue = getValue(discriminator, `${type}applyConfig`)
+
+ if (!configValue) {
+ commit('wizard/model$delete', `/spec/configuration/${type}applyConfig`)
+ return
+ }
+ const tempConfigObj = {}
+ configValue.forEach((item) => {
+ if (item.name) {
+ tempConfigObj[item.name] = item.content
+ }
+ })
+ if (Object.keys(tempConfigObj).length === 0) {
+ commit('wizard/model$delete', `/spec/configuration/${type}applyConfig`)
+ return
+ }
+ commit('wizard/model$update', {
+ path: `/spec/configuration/${type}applyConfig`,
+ value: tempConfigObj,
+ })
+ }
+
+ function setApplyConfig(type) {
+ type = type ? type + '/' : ''
+ const configPath = `/${type}selectedConfiguration`
+ const selectedConfig = getValue(discriminator, configPath)
+ if (!selectedConfig) {
+ return [{ name: '', content: '' }]
+ }
+ const applyconfigData = secretConfigData.find((item) => {
+ if (item.componentName === selectedConfig) {
+ return item
+ }
+ })
+ const { applyConfig } = applyconfigData
+ const configObj = []
+
+ if (applyConfig) {
+ Object.keys(applyConfig).forEach((fileName) => {
+ configObj.push({
+ name: fileName,
+ content: applyConfig[fileName],
+ })
+ })
+ } else {
+ configObj.push({ name: '', content: '' })
+ }
+ return configObj
+ }
+
+ function onRemoveConfigChange(type) {
+ type = type ? type + '/' : ''
+ const configPath = `/${type}selectedConfigurationRemove`
+ const selectedConfig = getValue(discriminator, configPath)
+
+ if (!selectedConfig) {
+ commit('wizard/model$delete', `/spec/configuration/${type}removeCustomConfig`)
+ return [{ name: '', content: '' }]
+ }
+ commit('wizard/model$update', {
+ path: `/spec/configuration/${type}removeCustomConfig`,
+ value: true,
+ })
+
+ const configuration = secretConfigData.find((item) => item.componentName === selectedConfig)
+
+ if (!configuration.data) {
+ return [{ name: '', content: '' }]
+ }
+
+ const configObj = []
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(configuration.data).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(configuration.data[fileName])
+ configObj.push({
+ name: fileName,
+ content: decodedString,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ configObj.push({
+ name: fileName,
+ content: configuration.data[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
+ return configObj
+ }
+
+ async function onNewConfigSecretChange(type) {
+ type = type ? type + '/' : ''
+ const path = `/spec/configuration/${type}configSecret/name`
+ const selectedSecret = getValue(model, path)
+
+ if (!selectedSecret) {
+ commit('wizard/model$delete', `/spec/configuration/${type}configSecret`)
+ return [{ name: '', content: '' }]
+ }
+ if (selectedSecret === 'Create') return [{ name: '', content: '' }]
+
+ const owner = storeGet('/route/params/user')
+ const cluster = storeGet('/route/params/cluster')
+ const namespace = storeGet('/route/query/namespace') || getValue(model, '/metadata/namespace')
+
+ try {
+ // Fetch the secret data from API
+ const secretResp = await axios.get(
+ `/clusters/${owner}/${cluster}/proxy/core/v1/namespaces/${namespace}/secrets/${selectedSecret}`,
+ )
+
+ const secretData = secretResp.data?.data || {}
+ const configObj = []
+
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(secretData).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(secretData[fileName])
+ configObj.push({
+ name: fileName,
+ content: decodedString,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ configObj.push({
+ name: fileName,
+ content: secretData[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
+
+ return configObj
+ } catch (e) {
+ console.error('Error fetching secret:', e)
+ return [{ name: '', content: '' }]
+ }
+ }
+
+ function onSelectedSecretChange(index) {
+ const secretData = getValue(discriminator, 'createSecret/data') || []
+ const selfSecrets = secretData.map((item) => item.key)
+
+ const remainingSecrets = configSecretKeys.filter((item) => !selfSecrets.includes(item))
+
+ const selfKey = getValue(discriminator, `createSecret/data/${index}/key`)
+ if (selfKey) {
+ remainingSecrets.push(selfKey)
+ }
+ const resSecret = remainingSecrets.map((item) => {
+ return { text: item, value: item }
+ })
+ return resSecret
}
function getSelectedConfigSecret(type) {
@@ -1098,26 +1485,6 @@ export const useFunc = (model) => {
return reconfigurationType === value
}
- function onApplyconfigChange() {
- const configPath = `/applyConfig`
- const applyconfig = getValue(discriminator, configPath)
-
- const configObj = {}
-
- if (applyconfig) {
- applyconfig.forEach((item) => {
- const { key, value } = item
- configObj[key] = value
- })
- }
-
- commit('wizard/model$update', {
- path: `/spec/configuration/applyConfig`,
- value: configObj,
- force: true,
- })
- }
-
function onReconfigurationTypeChange() {
setDiscriminatorValue(`/applyConfig`, [])
const reconfigurationType = getValue(discriminator, '/reconfigurationType')
@@ -1448,5 +1815,20 @@ export const useFunc = (model) => {
getSelectedConfigSecret,
getSelectedConfigSecretValue,
isVerticalScaleTopologyRequired,
+ fetchConfigSecrets,
+ getConfigSecretsforAppyConfig,
+ getSelectedConfigurationData,
+ getSelectedConfigurationName,
+ getSelectedConfigurationValueForRemove,
+ createNewConfigSecret,
+ decodeError,
+ isCreateSecret,
+ isNotCreateSecret,
+ onCreateSecretChange,
+ cancelCreateSecret,
+ setApplyConfig,
+ onRemoveConfigChange,
+ onNewConfigSecretChange,
+ onSelectedSecretChange,
}
}
diff --git a/charts/opskubedbcom-mariadbopsrequest-editor/ui/create-ui.yaml b/charts/opskubedbcom-mariadbopsrequest-editor/ui/create-ui.yaml
index b020540266..8b7188a519 100644
--- a/charts/opskubedbcom-mariadbopsrequest-editor/ui/create-ui.yaml
+++ b/charts/opskubedbcom-mariadbopsrequest-editor/ui/create-ui.yaml
@@ -263,106 +263,176 @@ step:
if:
name: ifRequestTypeEqualsTo|Reconfigure
type: function
+ loader:
+ name: fetchConfigSecrets
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
elements:
- - type: radio
- label: Reconfigure Type
- subtitle: Choose the action you'd like to perform. Select a new configuration secret, apply a custom configuration , or remove an existing setup.
- validation:
- type: required
- options:
- - text: New Config Secret
- value: selectNewConfigSecret
- - text: Apply Config
- value: applyConfig
- - text: Remove
- value: remove
- schema: temp/properties/reconfigurationType
- watcher:
- func: onReconfigurationTypeChange
- paths:
- - temp/properties/reconfigurationType
- type: block-layout
- label: Config Secret
- showLabels: true
- if:
- name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret
- type: function
- elements:
- - type: label-element
- label: Select a configuration secret from the available list to update your database settings
- customClass: mb-10
- - type: select
- addNewButton:
- label: Create a new Secret
- target: _blank
- url:
- function: createSecretUrl
- schema: schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- validation:
- type: required
- refresh: true
- label: Config Secret
- loader:
- name: getConfigSecrets
- watchPaths:
- - schema/properties/metadata/properties/namespace
- init:
- type: func
- value: setValueFromDbDetails|/spec/configSecret/name
- - type: label-element
- label: Selected Config Secret
- loader: getSelectedConfigSecret|mariadb
- watcher:
- func: getSelectedConfigSecret|mariadb
- paths:
- - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- - type: editor
- label: Value
- readonly: true
- hasCopy: false
- watcher:
- func: getSelectedConfigSecretValue|mariadb
- paths:
- - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- validation:
- type: required
- schema: temp/properties/configArray/items/properties/value
- - type: array-object-form
- label: ApplyConfig
- buttonClass: is-light is-outlined
- schema: temp/properties/applyConfig
- validation:
- type: required
- if:
- name: ifReconfigurationTypeEqualsTo|applyConfig
- type: function
- watcher:
- func: onApplyconfigChange
- paths:
- - temp/properties/applyConfig
+ label: Configuration
elements:
- type: label-element
- label: New Apply Config
- subtitle: Define custom configurations for your database using key-value pairs. These parameters will overwrite existing settings. Enter the parameter you want to configure (e.g., max_connections).
- customClass: mb-10
- - type: input
- label: key
- validation:
- type: required
- schema: key
- - type: editor
- label: value
- hasCopy: false
- validation:
- type: required
- schema: value
- - type: switch
- schema: schema/properties/spec/properties/configuration/properties/removeCustomConfig
- label: Remove CustomConfig
- fullwidth: true
- if:
- name: returnFalse
- type: function
+ label: ''
+ subtitle: Select a new configuration secret, apply a custom configuration, or remove an existing setup to update your database settings
+ - type: tab-layout
+ label: New Config Secret
+ schema: temp/properties/reconfigurationType
+ options:
+ - text: NEW CONFIG SECRET
+ value: selectNewConfigSecret
+ - text: APPLY CONFIG
+ value: applyConfig
+ - text: REMOVE
+ value: remove
+ elements:
+ - type: block-layout
+ label: Config Secret
+ if:
+ name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret
+ type: function
+ elements:
+ - type: label-element
+ label: Config Secret
+ subtitle: Select a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ refresh: true
+ label: Config Secret
+ loader:
+ name: getConfigSecrets
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - temp/properties/createSecret/properties/status
+ init:
+ type: func
+ value: setValueFromDbDetails|/spec/configSecret/name
+ watcher:
+ func: onCreateSecretChange
+ paths:
+ - temp/properties/createSecret/properties/status
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|create
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ - type: block-layout
+ label: Create a New Config Secret
+ showLabels: true
+ if:
+ type: function
+ name: isCreateSecret
+ hasButton:
+ text: Save
+ hasCancel: cancelCreateSecret
+ action: createNewConfigSecret
+ elements:
+ - type: input
+ label: Secret Name
+ schema: temp/properties/createSecret/properties/name
+ validation:
+ type: required
+ - type: array-object-form
+ label: String Data
+ schema: temp/properties/createSecret/properties/data
+ buttonClass: is-light is-outlined
+ validation:
+ type: required
+ elements:
+ - type: select
+ label: Key
+ schema: key
+ loader:
+ name: onSelectedSecretChange
+ watchPaths:
+ - temp/properties/createSecret/properties/data
+ validation:
+ type: required
+ - type: textarea
+ label: Value
+ schema: value
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ if:
+ type: function
+ name: isNotCreateSecret
+ loader:
+ name: onNewConfigSecretChange
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ schema: temp/properties/newConfigSecret
+ - type: block-layout
+ label: ApplyConfig
+ if:
+ name: ifReconfigurationTypeEqualsTo|applyConfig
+ type: function
+ elements:
+ - type: label-element
+ label: New Apply Config
+ subtitle: Define custom configurations for your database using key-value pairs. These parameters will overwrite existing settings.
Enter the parameter you want to configure (e.g., max_connections).
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/selectedConfiguration
+ refresh: true
+ label: Configuration
+ loader: getConfigSecretsforAppyConfig
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|apply
+ watchPaths:
+ - temp/properties/selectedConfiguration
+ - type: multi-file-editor
+ editorHeight: 500px
+ schema: temp/properties/applyConfig
+ loader:
+ name: setApplyConfig
+ watchPaths:
+ - temp/properties/selectedConfiguration
+ watcher:
+ func: onApplyconfigChange
+ paths:
+ - temp/properties/applyConfig
+ - type: block-layout
+ label: Remove
+ if:
+ name: ifReconfigurationTypeEqualsTo|remove
+ type: function
+ elements:
+ - type: label-element
+ label: Remove
+ subtitle: Selected a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/selectedConfigurationRemove
+ refresh: true
+ label: Configuration
+ loader:
+ name: getConfigSecretsforAppyConfig
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|remove
+ watchPaths:
+ - temp/properties/selectedConfigurationRemove
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ init:
+ type: func
+ value: onRemoveConfigChange
+ watcher:
+ func: onRemoveConfigChange
+ paths:
+ - temp/properties/selectedConfigurationRemove
+ schema: temp/properties/removeConfig
# Reconfigure TLS
- type: block-layout
label: TLS
diff --git a/charts/opskubedbcom-mariadbopsrequest-editor/ui/functions.js b/charts/opskubedbcom-mariadbopsrequest-editor/ui/functions.js
index 5fe6145c1c..2a4b975299 100644
--- a/charts/opskubedbcom-mariadbopsrequest-editor/ui/functions.js
+++ b/charts/opskubedbcom-mariadbopsrequest-editor/ui/functions.js
@@ -1,4 +1,4 @@
-const { axios, useOperator, store } = window.vueHelpers || {}
+const { axios, useOperator, store, useToast } = window.vueHelpers || {}
const machines = {
'db.t.micro': {
resources: {
@@ -306,9 +306,11 @@ const machineList = [
]
let machinesFromPreset = []
+const configSecretKeys = ['kubedb-user.cnf']
export const useFunc = (model) => {
const route = store.state?.route
+ const toast = useToast()
const { getValue, storeGet, discriminator, setDiscriminatorValue, commit } = useOperator(
model,
@@ -830,32 +832,419 @@ export const useFunc = (model) => {
return machine === 'custom'
}
- // for config secret
- let secretArray = []
- async function getConfigSecrets() {
+ // Fetch and store database Infos
+ // for secret configurations in reconfigure
+ let configSecrets = []
+ let secretConfigData = []
+
+ async function fetchConfigSecrets() {
const owner = storeGet('/route/params/user')
const cluster = storeGet('/route/params/cluster')
const namespace = getValue(model, '/metadata/namespace')
// watchDependency('model#/metadata/namespace')
- const resp = await axios.get(
- `/clusters/${owner}/${cluster}/proxy/core/v1/namespaces/${namespace}/secrets`,
+ const name = getValue(model, '/spec/databaseRef/name')
+ const dbGroup = getValue(model, '/route/params/group')
+ const dbKind = getValue(store.state, '/resource/definition/result/kind')
+ const dbResource = getValue(model, '/route/params/resource')
+ const dbVersion = getValue(model, '/route/params/version')
+
+ try {
+ const resp = await axios.post(
+ `/clusters/${owner}/${cluster}/proxy/ui.kubedb.com/v1alpha1/databaseinfos`,
+ {
+ apiVersion: 'ui.kubedb.com/v1alpha1',
+ kind: 'DatabaseInfo',
+ request: {
+ source: {
+ ref: {
+ name: name,
+ namespace: namespace,
+ },
+ resource: {
+ group: dbGroup,
+ kind: dbKind,
+ name: dbResource,
+ version: dbVersion,
+ },
+ },
+ keys: ['kubedb-user.cnf'],
+ },
+ },
+ )
+ configSecrets = resp?.data?.response?.availableSecrets || []
+ secretConfigData = resp?.data?.response?.configurations || []
+ } catch (e) {
+ console.log(e)
+ }
+ }
+
+ async function getConfigSecrets(type) {
+ type = type ? type + '/' : ''
+ const secretStatus = getValue(discriminator, `${type}createSecret/status`)
+ if (secretStatus === 'success') {
+ await fetchConfigSecrets()
+ }
+ const mappedSecrets = configSecrets.map((item) => {
+ return { text: item, value: item }
+ })
+ mappedSecrets.push({ text: '+ Create a new Secret', value: 'Create' })
+ return mappedSecrets
+ }
+
+ async function getConfigSecretsforAppyConfig() {
+ const secrets = secretConfigData.map((item) => {
+ return { text: item.componentName, value: item.componentName }
+ })
+ return secrets
+ }
+
+ function getSelectedConfigurationData(type) {
+ type = type ? type + '/' : ''
+ const path = `/${type}selectedConfiguration`
+ const selectedConfiguration = getValue(discriminator, path)
+
+ if (!selectedConfiguration) {
+ return []
+ }
+
+ const configuration = secretConfigData.find(
+ (item) => item.componentName === selectedConfiguration,
)
- const secrets = (resp && resp.data && resp.data.items) || []
- secretArray = secrets
+ if (!configuration) {
+ return []
+ }
- const filteredSecrets = secrets
+ const result = []
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(configuration.data).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedContent = atob(configuration.data[fileName])
+ result.push({
+ name: fileName,
+ content: decodedContent,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ result.push({
+ name: fileName,
+ content: configuration.data[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
- filteredSecrets.map((item) => {
- const name = (item.metadata && item.metadata.name) || ''
- item.text = name
- item.value = name
- return true
+ // Set the value to the model
+ commit('wizard/model$update', {
+ path: `/temp/${type}applyConfig`,
+ value: result,
+ force: true,
})
- return filteredSecrets
+
+ return result
+ }
+
+ function getSelectedConfigurationName(configType, type) {
+ type = type ? type + '/' : ''
+ let path = ''
+ if (configType === 'create') path = `/spec/configuration/${type}/configSecret/name`
+ else if (configType === 'apply') path = `/${type}selectedConfiguration`
+ else if (configType === 'remove') path = `/${type}selectedConfigurationRemove`
+
+ const selectedConfiguration =
+ configType === 'create' ? getValue(model, path) : getValue(discriminator, path)
+
+ if (selectedConfiguration)
+ return { subtitle: ` You have selected ${selectedConfiguration} secret` }
+ else return { subtitle: 'No secret selected' }
+ }
+
+ function getSelectedConfigurationValueForRemove(type) {
+ type = type ? type + '/' : ''
+ const path = `/${type}selectedConfigurationRemove`
+ const selectedConfiguration = getValue(discriminator, path)
+
+ if (!selectedConfiguration) {
+ return ''
+ }
+
+ const configuration = secretConfigData.find(
+ (item) => item.componentName === selectedConfiguration,
+ )
+
+ if (!configuration) {
+ return ''
+ }
+
+ let data = {}
+ // Decode base64 and parse YAML for each key in the secret data
+ Object.keys(configuration.data).forEach((item) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(configuration.data[item])
+ // Parse YAML string to object
+ const parsedYaml = yaml.load(decodedString)
+ // Store the parsed object with the filename as key
+ data[item] = parsedYaml
+ } catch (e) {
+ console.error(`Error parsing ${item}:`, e)
+ data[item] = atob(configuration.data[item]) // Fallback to decoded string
+ }
+ })
+
+ // Convert data object back to YAML string
+ return yaml.dump(data)
+ }
+
+ async function createNewConfigSecret(type) {
+ type = type ? type + '/' : ''
+ const { user, cluster } = route.params
+ const url = `/clusters/${user}/${cluster}/resources`
+ const namespace = storeGet('/route/query/namespace') || getValue(model, '/metadata/namespace')
+ const secretName = getValue(discriminator, `${type}createSecret/name`)
+ const secretData = getValue(discriminator, `${type}createSecret/data`)
+ const secretDataObj = Object.fromEntries(secretData.map((item) => [item.key, item.value]))
+
+ try {
+ const res = await axios.post(url, {
+ apiVersion: 'v1',
+ stringData: secretDataObj,
+ kind: 'Secret',
+ metadata: {
+ name: secretName,
+ namespace: namespace,
+ },
+ type: 'Opaque',
+ })
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'success',
+ })
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/lastCreatedSecret`,
+ value: secretName,
+ })
+ toast.success('Secret created successfully')
+ } catch (error) {
+ const errMsg = decodeError(error, 'Failed to create secret')
+ toast.error(errMsg, { timeout: 5000 })
+ cancelCreateSecret()
+ }
+ }
+
+ function decodeError(msg, defaultMsg) {
+ if (typeof msg === 'string') {
+ return msg || defaultMsg
+ }
+ return (
+ (msg.response && msg.response.data && msg.response.data.message) ||
+ (msg.response && msg.response.data) ||
+ (msg.status && msg.status.status) ||
+ defaultMsg
+ )
+ }
+
+ function isCreateSecret(type) {
+ type = type ? type + '/' : ''
+ const selectedSecret = getValue(model, `spec/configuration/${type}configSecret/name`)
+ const res = selectedSecret === 'Create'
+
+ if (res === true) {
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'pending',
+ })
+ }
+ return res
+ }
+
+ function isNotCreateSecret(type) {
+ return !isCreateSecret(type)
}
+ function onCreateSecretChange(type) {
+ type = type ? type + '/' : ''
+ const secretStatus = getValue(discriminator, `${type}createSecret/status`)
+ if (secretStatus === 'cancelled') return ''
+ else if (secretStatus === 'success') {
+ const name = getValue(discriminator, `${type}createSecret/lastCreatedSecret`)
+
+ const configFound = configSecrets.find((item) => item === name)
+ return configFound ? { text: name, value: name } : ''
+ }
+ }
+
+ function cancelCreateSecret(type) {
+ type = type ? type + '/' : ''
+ commit('wizard/temp$delete', `${type}createSecret/name`)
+ commit('wizard/temp$delete', `${type}createSecret/data`)
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'cancelled',
+ })
+ }
+
+ async function onApplyconfigChange(type) {
+ type = type ? type + '/' : ''
+ const configValue = getValue(discriminator, `${type}applyConfig`)
+
+ if (!configValue) {
+ commit('wizard/model$delete', `/spec/configuration/${type}applyConfig`)
+ return
+ }
+ const tempConfigObj = {}
+ configValue.forEach((item) => {
+ if (item.name) {
+ tempConfigObj[item.name] = item.content
+ }
+ })
+ if (Object.keys(tempConfigObj).length === 0) {
+ commit('wizard/model$delete', `/spec/configuration/${type}applyConfig`)
+ return
+ }
+ commit('wizard/model$update', {
+ path: `/spec/configuration/${type}applyConfig`,
+ value: tempConfigObj,
+ })
+ }
+
+ function setApplyConfig(type) {
+ type = type ? type + '/' : ''
+ const configPath = `/${type}selectedConfiguration`
+ const selectedConfig = getValue(discriminator, configPath)
+ if (!selectedConfig) {
+ return [{ name: '', content: '' }]
+ }
+ const applyconfigData = secretConfigData.find((item) => {
+ if (item.componentName === selectedConfig) {
+ return item
+ }
+ })
+ const { applyConfig } = applyconfigData
+ const configObj = []
+
+ if (applyConfig) {
+ Object.keys(applyConfig).forEach((fileName) => {
+ configObj.push({
+ name: fileName,
+ content: applyConfig[fileName],
+ })
+ })
+ } else {
+ configObj.push({ name: '', content: '' })
+ }
+ return configObj
+ }
+
+ function onRemoveConfigChange(type) {
+ type = type ? type + '/' : ''
+ const configPath = `/${type}selectedConfigurationRemove`
+ const selectedConfig = getValue(discriminator, configPath)
+
+ if (!selectedConfig) {
+ commit('wizard/model$delete', `/spec/configuration/${type}removeCustomConfig`)
+ return [{ name: '', content: '' }]
+ }
+ commit('wizard/model$update', {
+ path: `/spec/configuration/${type}removeCustomConfig`,
+ value: true,
+ })
+
+ const configuration = secretConfigData.find((item) => item.componentName === selectedConfig)
+
+ if (!configuration.data) {
+ return [{ name: '', content: '' }]
+ }
+
+ const configObj = []
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(configuration.data).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(configuration.data[fileName])
+ configObj.push({
+ name: fileName,
+ content: decodedString,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ configObj.push({
+ name: fileName,
+ content: configuration.data[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
+ return configObj
+ }
+
+ async function onNewConfigSecretChange(type) {
+ type = type ? type + '/' : ''
+ const path = `/spec/configuration/${type}configSecret/name`
+ const selectedSecret = getValue(model, path)
+
+ if (!selectedSecret) {
+ commit('wizard/model$delete', `/spec/configuration/${type}configSecret`)
+ return [{ name: '', content: '' }]
+ }
+ if (selectedSecret === 'Create') return [{ name: '', content: '' }]
+
+ const owner = storeGet('/route/params/user')
+ const cluster = storeGet('/route/params/cluster')
+ const namespace = storeGet('/route/query/namespace') || getValue(model, '/metadata/namespace')
+
+ try {
+ // Fetch the secret data from API
+ const secretResp = await axios.get(
+ `/clusters/${owner}/${cluster}/proxy/core/v1/namespaces/${namespace}/secrets/${selectedSecret}`,
+ )
+
+ const secretData = secretResp.data?.data || {}
+ const configObj = []
+
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(secretData).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(secretData[fileName])
+ configObj.push({
+ name: fileName,
+ content: decodedString,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ configObj.push({
+ name: fileName,
+ content: secretData[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
+
+ return configObj
+ } catch (e) {
+ console.error('Error fetching secret:', e)
+ return [{ name: '', content: '' }]
+ }
+ }
+
+ function onSelectedSecretChange(index) {
+ const secretData = getValue(discriminator, 'createSecret/data') || []
+ const selfSecrets = secretData.map((item) => item.key)
+
+ const remainingSecrets = configSecretKeys.filter((item) => !selfSecrets.includes(item))
+
+ const selfKey = getValue(discriminator, `createSecret/data/${index}/key`)
+ if (selfKey) {
+ remainingSecrets.push(selfKey)
+ }
+ const resSecret = remainingSecrets.map((item) => {
+ return { text: item, value: item }
+ })
+ return resSecret
+ }
+
+ let secretArray = []
+
function createSecretUrl() {
const user = storeGet('/route/params/user')
const cluster = storeGet('/route/params/cluster')
@@ -977,25 +1366,6 @@ export const useFunc = (model) => {
return reconfigurationType === value
}
- function onApplyconfigChange() {
- const applyconfig = getValue(discriminator, '/applyConfig')
-
- const configObj = {}
-
- if (applyconfig) {
- applyconfig.forEach((item) => {
- const { key, value } = item
- configObj[key] = value
- })
- }
-
- commit('wizard/model$update', {
- path: `/spec/configuration/applyConfig`,
- value: configObj,
- force: true,
- })
- }
-
function onReconfigurationTypeChange() {
setDiscriminatorValue(`/applyConfig`, [])
const reconfigurationType = getValue(discriminator, '/reconfigurationType')
@@ -1407,5 +1777,20 @@ export const useFunc = (model) => {
onMachineChange,
isMachineCustom,
checkVolume,
+ fetchConfigSecrets,
+ getConfigSecretsforAppyConfig,
+ getSelectedConfigurationData,
+ getSelectedConfigurationName,
+ getSelectedConfigurationValueForRemove,
+ createNewConfigSecret,
+ decodeError,
+ isCreateSecret,
+ isNotCreateSecret,
+ onCreateSecretChange,
+ cancelCreateSecret,
+ setApplyConfig,
+ onRemoveConfigChange,
+ onNewConfigSecretChange,
+ onSelectedSecretChange,
}
}
diff --git a/charts/opskubedbcom-memcachedopsrequest-editor/ui/create-ui.yaml b/charts/opskubedbcom-memcachedopsrequest-editor/ui/create-ui.yaml
index cce12ccad9..d0d82a02de 100644
--- a/charts/opskubedbcom-memcachedopsrequest-editor/ui/create-ui.yaml
+++ b/charts/opskubedbcom-memcachedopsrequest-editor/ui/create-ui.yaml
@@ -194,85 +194,176 @@ step:
if:
name: ifRequestTypeEqualsTo|Reconfigure
type: function
+ loader:
+ name: fetchConfigSecrets
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
elements:
- type: block-layout
label: Configuration
elements:
- - type: radio
- label: Reconfigure Type
+ - type: label-element
+ label: ''
+ subtitle: Select a new configuration secret, apply a custom configuration, or remove an existing setup to update your database settings
+ - type: tab-layout
+ label: New Config Secret
+ schema: temp/properties/reconfigurationType
options:
- - text: Select New Config Secret
+ - text: NEW CONFIG SECRET
value: selectNewConfigSecret
- - text: Apply Config
+ - text: APPLY CONFIG
value: applyConfig
- - text: Remove
+ - text: REMOVE
value: remove
- watcher:
- func: onReconfigurationTypeChange
- paths:
- - temp/properties/reconfigurationType
- validation:
- type: required
- schema: temp/properties/reconfigurationType
- - type: block-layout
- label: Configuration config secret
- showLabels: false
- if:
- name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret
- type: function
elements:
- - type: select
- addNewButton:
- label: Create a new Secret
- target: _blank
- url:
- function: createSecretUrl
+ - type: block-layout
label: Config Secret
if:
name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret
type: function
- loader:
- name: getConfigSecrets
- watchPaths:
- - schema/properties/metadata/properties/namespace
- init:
- type: func
- value: setValueFromDbDetails|/spec/configSecret/name
- refresh: true
- validation:
- type: required
- schema: schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- - type: array-object-form
- label: Apply Config
- if:
- name: ifReconfigurationTypeEqualsTo|applyConfig
- type: function
- validation:
- type: required
- watcher:
- func: onApplyconfigChange
- paths:
- - temp/properties/applyConfig
- schema: temp/properties/applyConfig
- elements:
- - type: input
- label: key
- validation:
- type: required
- schema: key
- - type: editor
- hasCopy: false
- label: value
- validation:
- type: required
- schema: value
- - type: switch
- fullwidth: true
- schema: schema/properties/spec/properties/configuration/properties/removeCustomConfig
- label: Remove CustomConfig
- if:
- name: returnFalse
- type: function
+ elements:
+ - type: label-element
+ label: Config Secret
+ subtitle: Select a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ refresh: true
+ label: Config Secret
+ loader:
+ name: getConfigSecrets
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - temp/properties/createSecret/properties/status
+ init:
+ type: func
+ value: setValueFromDbDetails|/spec/configSecret/name
+ watcher:
+ func: onCreateSecretChange
+ paths:
+ - temp/properties/createSecret/properties/status
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|create
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ - type: block-layout
+ label: Create a New Config Secret
+ showLabels: true
+ if:
+ type: function
+ name: isCreateSecret
+ hasButton:
+ text: Save
+ hasCancel: cancelCreateSecret
+ action: createNewConfigSecret
+ elements:
+ - type: input
+ label: Secret Name
+ schema: temp/properties/createSecret/properties/name
+ validation:
+ type: required
+ - type: array-object-form
+ label: String Data
+ schema: temp/properties/createSecret/properties/data
+ buttonClass: is-light is-outlined
+ validation:
+ type: required
+ elements:
+ - type: select
+ label: Key
+ schema: key
+ loader:
+ name: onSelectedSecretChange
+ watchPaths:
+ - temp/properties/createSecret/properties/data
+ validation:
+ type: required
+ - type: textarea
+ label: Value
+ schema: value
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ if:
+ type: function
+ name: isNotCreateSecret
+ loader:
+ name: onNewConfigSecretChange
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ schema: temp/properties/newConfigSecret
+ - type: block-layout
+ label: ApplyConfig
+ if:
+ name: ifReconfigurationTypeEqualsTo|applyConfig
+ type: function
+ elements:
+ - type: label-element
+ label: New Apply Config
+ subtitle: Define custom configurations for your database using key-value pairs. These parameters will overwrite existing settings.
Enter the parameter you want to configure (e.g., max_connections).
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/selectedConfiguration
+ refresh: true
+ label: Configuration
+ loader: getConfigSecretsforAppyConfig
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|apply
+ watchPaths:
+ - temp/properties/selectedConfiguration
+ - type: multi-file-editor
+ editorHeight: 500px
+ schema: temp/properties/applyConfig
+ loader:
+ name: setApplyConfig
+ watchPaths:
+ - temp/properties/selectedConfiguration
+ watcher:
+ func: onApplyconfigChange
+ paths:
+ - temp/properties/applyConfig
+ - type: block-layout
+ label: Remove
+ if:
+ name: ifReconfigurationTypeEqualsTo|remove
+ type: function
+ elements:
+ - type: label-element
+ label: Remove
+ subtitle: Selected a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/selectedConfigurationRemove
+ refresh: true
+ label: Configuration
+ loader:
+ name: getConfigSecretsforAppyConfig
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|remove
+ watchPaths:
+ - temp/properties/selectedConfigurationRemove
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ init:
+ type: func
+ value: onRemoveConfigChange
+ watcher:
+ func: onRemoveConfigChange
+ paths:
+ - temp/properties/selectedConfigurationRemove
+ schema: temp/properties/removeConfig
# common
- type: time-picker
label: Timeout
diff --git a/charts/opskubedbcom-memcachedopsrequest-editor/ui/functions.js b/charts/opskubedbcom-memcachedopsrequest-editor/ui/functions.js
index 5742f601f0..227897e072 100644
--- a/charts/opskubedbcom-memcachedopsrequest-editor/ui/functions.js
+++ b/charts/opskubedbcom-memcachedopsrequest-editor/ui/functions.js
@@ -1,4 +1,4 @@
-const { axios, useOperator, store } = window.vueHelpers || {}
+const { axios, useOperator, store, useToast } = window.vueHelpers || {}
const machines = {
'db.t.micro': {
resources: {
@@ -306,9 +306,11 @@ const machineList = [
]
let machinesFromPreset = []
+const configSecretKeys = ['kubedb-user.cnf']
export const useFunc = (model) => {
const route = store.state?.route
+ const toast = useToast()
const { getValue, storeGet, discriminator, setDiscriminatorValue, commit } = useOperator(
model,
@@ -845,28 +847,415 @@ export const useFunc = (model) => {
return machine === 'custom'
}
- // for config secret
- async function getConfigSecrets() {
+ // Fetch and store database Infos
+ // for secret configurations in reconfigure
+ let configSecrets = []
+ let secretConfigData = []
+
+ async function fetchConfigSecrets() {
const owner = storeGet('/route/params/user')
const cluster = storeGet('/route/params/cluster')
const namespace = getValue(model, '/metadata/namespace')
// watchDependency('model#/metadata/namespace')
- const resp = await axios.get(
- `/clusters/${owner}/${cluster}/proxy/core/v1/namespaces/${namespace}/secrets`,
+ const name = getValue(model, '/spec/databaseRef/name')
+ const dbGroup = getValue(model, '/route/params/group')
+ const dbKind = getValue(store.state, '/resource/definition/result/kind')
+ const dbResource = getValue(model, '/route/params/resource')
+ const dbVersion = getValue(model, '/route/params/version')
+
+ try {
+ const resp = await axios.post(
+ `/clusters/${owner}/${cluster}/proxy/ui.kubedb.com/v1alpha1/databaseinfos`,
+ {
+ apiVersion: 'ui.kubedb.com/v1alpha1',
+ kind: 'DatabaseInfo',
+ request: {
+ source: {
+ ref: {
+ name: name,
+ namespace: namespace,
+ },
+ resource: {
+ group: dbGroup,
+ kind: dbKind,
+ name: dbResource,
+ version: dbVersion,
+ },
+ },
+ keys: ['kubedb-user.cnf'],
+ },
+ },
+ )
+ configSecrets = resp?.data?.response?.availableSecrets || []
+ secretConfigData = resp?.data?.response?.configurations || []
+ } catch (e) {
+ console.log(e)
+ }
+ }
+
+ async function getConfigSecrets(type) {
+ type = type ? type + '/' : ''
+ const secretStatus = getValue(discriminator, `${type}createSecret/status`)
+ if (secretStatus === 'success') {
+ await fetchConfigSecrets()
+ }
+ const mappedSecrets = configSecrets.map((item) => {
+ return { text: item, value: item }
+ })
+ mappedSecrets.push({ text: '+ Create a new Secret', value: 'Create' })
+ return mappedSecrets
+ }
+
+ async function getConfigSecretsforAppyConfig() {
+ const secrets = secretConfigData.map((item) => {
+ return { text: item.componentName, value: item.componentName }
+ })
+ return secrets
+ }
+
+ function getSelectedConfigurationData(type) {
+ type = type ? type + '/' : ''
+ const path = `/${type}selectedConfiguration`
+ const selectedConfiguration = getValue(discriminator, path)
+
+ if (!selectedConfiguration) {
+ return []
+ }
+
+ const configuration = secretConfigData.find(
+ (item) => item.componentName === selectedConfiguration,
)
- const secrets = (resp && resp.data && resp.data.items) || []
+ if (!configuration) {
+ return []
+ }
- const filteredSecrets = secrets
+ const result = []
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(configuration.data).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedContent = atob(configuration.data[fileName])
+ result.push({
+ name: fileName,
+ content: decodedContent,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ result.push({
+ name: fileName,
+ content: configuration.data[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
- filteredSecrets.map((item) => {
- const name = (item.metadata && item.metadata.name) || ''
- item.text = name
- item.value = name
- return true
+ // Set the value to the model
+ commit('wizard/model$update', {
+ path: `/temp/${type}applyConfig`,
+ value: result,
+ force: true,
+ })
+
+ return result
+ }
+
+ function getSelectedConfigurationName(configType, type) {
+ type = type ? type + '/' : ''
+ let path = ''
+ if (configType === 'create') path = `/spec/configuration/${type}/configSecret/name`
+ else if (configType === 'apply') path = `/${type}selectedConfiguration`
+ else if (configType === 'remove') path = `/${type}selectedConfigurationRemove`
+
+ const selectedConfiguration =
+ configType === 'create' ? getValue(model, path) : getValue(discriminator, path)
+
+ if (selectedConfiguration)
+ return { subtitle: ` You have selected ${selectedConfiguration} secret` }
+ else return { subtitle: 'No secret selected' }
+ }
+
+ function getSelectedConfigurationValueForRemove(type) {
+ type = type ? type + '/' : ''
+ const path = `/${type}selectedConfigurationRemove`
+ const selectedConfiguration = getValue(discriminator, path)
+
+ if (!selectedConfiguration) {
+ return ''
+ }
+
+ const configuration = secretConfigData.find(
+ (item) => item.componentName === selectedConfiguration,
+ )
+
+ if (!configuration) {
+ return ''
+ }
+
+ let data = {}
+ // Decode base64 and parse YAML for each key in the secret data
+ Object.keys(configuration.data).forEach((item) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(configuration.data[item])
+ // Parse YAML string to object
+ const parsedYaml = yaml.load(decodedString)
+ // Store the parsed object with the filename as key
+ data[item] = parsedYaml
+ } catch (e) {
+ console.error(`Error parsing ${item}:`, e)
+ data[item] = atob(configuration.data[item]) // Fallback to decoded string
+ }
+ })
+
+ // Convert data object back to YAML string
+ return yaml.dump(data)
+ }
+
+ async function createNewConfigSecret(type) {
+ type = type ? type + '/' : ''
+ const { user, cluster } = route.params
+ const url = `/clusters/${user}/${cluster}/resources`
+ const namespace = storeGet('/route/query/namespace') || getValue(model, '/metadata/namespace')
+ const secretName = getValue(discriminator, `${type}createSecret/name`)
+ const secretData = getValue(discriminator, `${type}createSecret/data`)
+ const secretDataObj = Object.fromEntries(secretData.map((item) => [item.key, item.value]))
+
+ try {
+ const res = await axios.post(url, {
+ apiVersion: 'v1',
+ stringData: secretDataObj,
+ kind: 'Secret',
+ metadata: {
+ name: secretName,
+ namespace: namespace,
+ },
+ type: 'Opaque',
+ })
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'success',
+ })
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/lastCreatedSecret`,
+ value: secretName,
+ })
+ toast.success('Secret created successfully')
+ } catch (error) {
+ const errMsg = decodeError(error, 'Failed to create secret')
+ toast.error(errMsg, { timeout: 5000 })
+ cancelCreateSecret()
+ }
+ }
+
+ function decodeError(msg, defaultMsg) {
+ if (typeof msg === 'string') {
+ return msg || defaultMsg
+ }
+ return (
+ (msg.response && msg.response.data && msg.response.data.message) ||
+ (msg.response && msg.response.data) ||
+ (msg.status && msg.status.status) ||
+ defaultMsg
+ )
+ }
+
+ function isCreateSecret(type) {
+ type = type ? type + '/' : ''
+ const selectedSecret = getValue(model, `spec/configuration/${type}configSecret/name`)
+ const res = selectedSecret === 'Create'
+
+ if (res === true) {
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'pending',
+ })
+ }
+ return res
+ }
+
+ function isNotCreateSecret(type) {
+ return !isCreateSecret(type)
+ }
+
+ function onCreateSecretChange(type) {
+ type = type ? type + '/' : ''
+ const secretStatus = getValue(discriminator, `${type}createSecret/status`)
+ if (secretStatus === 'cancelled') return ''
+ else if (secretStatus === 'success') {
+ const name = getValue(discriminator, `${type}createSecret/lastCreatedSecret`)
+
+ const configFound = configSecrets.find((item) => item === name)
+ return configFound ? { text: name, value: name } : ''
+ }
+ }
+
+ function cancelCreateSecret(type) {
+ type = type ? type + '/' : ''
+ commit('wizard/temp$delete', `${type}createSecret/name`)
+ commit('wizard/temp$delete', `${type}createSecret/data`)
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'cancelled',
})
- return filteredSecrets
+ }
+
+ async function onApplyconfigChange(type) {
+ type = type ? type + '/' : ''
+ const configValue = getValue(discriminator, `${type}applyConfig`)
+
+ if (!configValue) {
+ commit('wizard/model$delete', `/spec/configuration/${type}applyConfig`)
+ return
+ }
+ const tempConfigObj = {}
+ configValue.forEach((item) => {
+ if (item.name) {
+ tempConfigObj[item.name] = item.content
+ }
+ })
+ if (Object.keys(tempConfigObj).length === 0) {
+ commit('wizard/model$delete', `/spec/configuration/${type}applyConfig`)
+ return
+ }
+ commit('wizard/model$update', {
+ path: `/spec/configuration/${type}applyConfig`,
+ value: tempConfigObj,
+ })
+ }
+
+ function setApplyConfig(type) {
+ type = type ? type + '/' : ''
+ const configPath = `/${type}selectedConfiguration`
+ const selectedConfig = getValue(discriminator, configPath)
+ if (!selectedConfig) {
+ return [{ name: '', content: '' }]
+ }
+ const applyconfigData = secretConfigData.find((item) => {
+ if (item.componentName === selectedConfig) {
+ return item
+ }
+ })
+ const { applyConfig } = applyconfigData
+ const configObj = []
+
+ if (applyConfig) {
+ Object.keys(applyConfig).forEach((fileName) => {
+ configObj.push({
+ name: fileName,
+ content: applyConfig[fileName],
+ })
+ })
+ } else {
+ configObj.push({ name: '', content: '' })
+ }
+ return configObj
+ }
+
+ function onRemoveConfigChange(type) {
+ type = type ? type + '/' : ''
+ const configPath = `/${type}selectedConfigurationRemove`
+ const selectedConfig = getValue(discriminator, configPath)
+
+ if (!selectedConfig) {
+ commit('wizard/model$delete', `/spec/configuration/${type}removeCustomConfig`)
+ return [{ name: '', content: '' }]
+ }
+ commit('wizard/model$update', {
+ path: `/spec/configuration/${type}removeCustomConfig`,
+ value: true,
+ })
+
+ const configuration = secretConfigData.find((item) => item.componentName === selectedConfig)
+
+ if (!configuration.data) {
+ return [{ name: '', content: '' }]
+ }
+
+ const configObj = []
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(configuration.data).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(configuration.data[fileName])
+ configObj.push({
+ name: fileName,
+ content: decodedString,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ configObj.push({
+ name: fileName,
+ content: configuration.data[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
+ return configObj
+ }
+
+ async function onNewConfigSecretChange(type) {
+ type = type ? type + '/' : ''
+ const path = `/spec/configuration/${type}configSecret/name`
+ const selectedSecret = getValue(model, path)
+
+ if (!selectedSecret) {
+ commit('wizard/model$delete', `/spec/configuration/${type}configSecret`)
+ return [{ name: '', content: '' }]
+ }
+ if (selectedSecret === 'Create') return [{ name: '', content: '' }]
+
+ const owner = storeGet('/route/params/user')
+ const cluster = storeGet('/route/params/cluster')
+ const namespace = storeGet('/route/query/namespace') || getValue(model, '/metadata/namespace')
+
+ try {
+ // Fetch the secret data from API
+ const secretResp = await axios.get(
+ `/clusters/${owner}/${cluster}/proxy/core/v1/namespaces/${namespace}/secrets/${selectedSecret}`,
+ )
+
+ const secretData = secretResp.data?.data || {}
+ const configObj = []
+
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(secretData).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(secretData[fileName])
+ configObj.push({
+ name: fileName,
+ content: decodedString,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ configObj.push({
+ name: fileName,
+ content: secretData[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
+
+ return configObj
+ } catch (e) {
+ console.error('Error fetching secret:', e)
+ return [{ name: '', content: '' }]
+ }
+ }
+
+ function onSelectedSecretChange(index) {
+ const secretData = getValue(discriminator, 'createSecret/data') || []
+ const selfSecrets = secretData.map((item) => item.key)
+
+ const remainingSecrets = configSecretKeys.filter((item) => !selfSecrets.includes(item))
+
+ const selfKey = getValue(discriminator, `createSecret/data/${index}/key`)
+ if (selfKey) {
+ remainingSecrets.push(selfKey)
+ }
+ const resSecret = remainingSecrets.map((item) => {
+ return { text: item, value: item }
+ })
+ return resSecret
}
function createSecretUrl() {
@@ -990,25 +1379,6 @@ export const useFunc = (model) => {
return reconfigurationType === value
}
- function onApplyconfigChange() {
- const applyconfig = getValue(discriminator, '/applyConfig')
-
- const configObj = {}
-
- if (applyconfig) {
- applyconfig.forEach((item) => {
- const { key, value } = item
- configObj[key] = value
- })
- }
-
- commit('wizard/model$update', {
- path: `/spec/configuration/applyConfig`,
- value: configObj,
- force: true,
- })
- }
-
function onReconfigurationTypeChange() {
setDiscriminatorValue('/applyConfig', [])
const reconfigurationType = getValue(discriminator, '/reconfigurationType')
@@ -1177,5 +1547,20 @@ export const useFunc = (model) => {
setMachine,
onMachineChange,
isMachineCustom,
+ fetchConfigSecrets,
+ getConfigSecretsforAppyConfig,
+ getSelectedConfigurationData,
+ getSelectedConfigurationName,
+ getSelectedConfigurationValueForRemove,
+ createNewConfigSecret,
+ decodeError,
+ isCreateSecret,
+ isNotCreateSecret,
+ onCreateSecretChange,
+ cancelCreateSecret,
+ setApplyConfig,
+ onRemoveConfigChange,
+ onNewConfigSecretChange,
+ onSelectedSecretChange,
}
}
diff --git a/charts/opskubedbcom-mongodbopsrequest-editor/ui/create-ui.yaml b/charts/opskubedbcom-mongodbopsrequest-editor/ui/create-ui.yaml
index 6b62097a6f..217d9d5599 100644
--- a/charts/opskubedbcom-mongodbopsrequest-editor/ui/create-ui.yaml
+++ b/charts/opskubedbcom-mongodbopsrequest-editor/ui/create-ui.yaml
@@ -651,15 +651,20 @@ step:
if:
name: ifRequestTypeEqualsTo|Reconfigure
type: function
+ loader:
+ name: fetchConfigSecrets
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
elements:
+ - type: label-element
+ label: ''
+ subtitle: Select a new configuration secret, apply a custom configuration, or remove an existing setup to update your database settings
- type: block-layout
label: Standalone
if:
name: ifDbTypeEqualsTo|standalone|configuration
type: function
elements:
- - type: label-element
- label: ''
- type: tab-layout
label: New Config Secret
schema: temp/properties/reconfigurationType
@@ -687,7 +692,7 @@ step:
refresh: true
label: Config Secret
loader:
- name: getConfigSecrets
+ name: getConfigSecrets|standalone
watchPaths:
- schema/properties/metadata/properties/namespace
- temp/properties/standalone/properties/createSecret/properties/status
@@ -701,7 +706,7 @@ step:
- type: label-element
label: ''
loader:
- name: getSelectedConfigSecret|standalone
+ name: getSelectedConfigurationName|create|standalone
watchPaths:
- schema/properties/spec/properties/configuration/properties/standalone/properties/configSecret/properties/name
- type: block-layout
@@ -724,6 +729,8 @@ step:
label: String Data
schema: temp/properties/standalone/properties/createSecret/properties/data
buttonClass: is-light is-outlined
+ validation:
+ type: required
elements:
- type: select
label: Key
@@ -767,7 +774,7 @@ step:
- type: label-element
label: ''
loader:
- name: getSelectedApplyConfigName|standalone
+ name: getSelectedConfigurationName|apply|standalone
watchPaths:
- temp/properties/standalone/selectedConfiguration
- type: multi-file-editor
@@ -803,7 +810,7 @@ step:
- type: label-element
label: ''
loader:
- name: getSelectedConfigurationName|standalone
+ name: getSelectedConfigurationName|remove|standalone
watchPaths:
- temp/properties/standalone/selectedConfigurationRemove
- type: multi-file-editor
@@ -823,8 +830,6 @@ step:
name: ifDbTypeEqualsTo|replicaSet|configuration
type: function
elements:
- - type: label-element
- label: ''
- type: tab-layout
label: New Config Secret
schema: temp/properties/reconfigurationType
@@ -852,7 +857,7 @@ step:
refresh: true
label: Config Secret
loader:
- name: getConfigSecrets
+ name: getConfigSecrets|replicaSet
watchPaths:
- schema/properties/metadata/properties/namespace
- temp/properties/replicaSet/properties/createSecret/properties/status
@@ -866,7 +871,7 @@ step:
- type: label-element
label: ''
loader:
- name: getSelectedConfigSecret|replicaSet
+ name: getSelectedConfigurationName|create|replicaSet
watchPaths:
- schema/properties/spec/properties/configuration/properties/replicaSet/properties/configSecret/properties/name
- type: block-layout
@@ -889,6 +894,8 @@ step:
label: String Data
schema: temp/properties/replicaSet/properties/createSecret/properties/data
buttonClass: is-light is-outlined
+ validation:
+ type: required
elements:
- type: select
label: Key
@@ -932,7 +939,7 @@ step:
- type: label-element
label: ''
loader:
- name: getSelectedApplyConfigName|replicaSet
+ name: getSelectedConfigurationName|apply|replicaSet
watchPaths:
- temp/properties/replicaSet/selectedConfiguration
- type: multi-file-editor
@@ -968,7 +975,7 @@ step:
- type: label-element
label: ''
loader:
- name: getSelectedConfigurationName|replicaSet
+ name: getSelectedConfigurationName|remove|replicaSet
watchPaths:
- temp/properties/replicaSet/selectedConfigurationRemove
- type: multi-file-editor
@@ -989,8 +996,6 @@ step:
showLabels: true
customClass: mt-10
elements:
- - type: label-element
- label: ''
- type: tab-layout
label: New Config Secret
schema: temp/properties/reconfigurationType-configServer
@@ -1018,7 +1023,7 @@ step:
refresh: true
label: Config Secret
loader:
- name: getConfigSecrets
+ name: getConfigSecrets|configServer
watchPaths:
- schema/properties/metadata/properties/namespace
- temp/properties/configServer/properties/createSecret/properties/status
@@ -1032,7 +1037,7 @@ step:
- type: label-element
label: ''
loader:
- name: getSelectedConfigSecret|configServer
+ name: getSelectedConfigurationName|create|configServer
watchPaths:
- schema/properties/spec/properties/configuration/properties/configServer/properties/configSecret/properties/name
- type: block-layout
@@ -1055,6 +1060,8 @@ step:
label: String Data
schema: temp/properties/configServer/properties/createSecret/properties/data
buttonClass: is-light is-outlined
+ validation:
+ type: required
elements:
- type: select
label: Key
@@ -1098,7 +1105,7 @@ step:
- type: label-element
label: ''
loader:
- name: getSelectedApplyConfigName|configServer
+ name: getSelectedConfigurationName|apply|configServer
watchPaths:
- temp/properties/configServer/selectedConfiguration
- type: multi-file-editor
@@ -1134,7 +1141,7 @@ step:
- type: label-element
label: ''
loader:
- name: getSelectedConfigurationName|configServer
+ name: getSelectedConfigurationName|remove|configServer
watchPaths:
- temp/properties/configServer/selectedConfigurationRemove
- type: multi-file-editor
@@ -1152,8 +1159,6 @@ step:
label: Mongos
showLabels: true
elements:
- - type: label-element
- label: ''
- type: tab-layout
label: New Config Secret
schema: temp/properties/reconfigurationType-mongos
@@ -1181,7 +1186,7 @@ step:
refresh: true
label: Config Secret
loader:
- name: getConfigSecrets
+ name: getConfigSecrets|mongos
watchPaths:
- schema/properties/metadata/properties/namespace
- temp/properties/mongos/properties/createSecret/properties/status
@@ -1195,7 +1200,7 @@ step:
- type: label-element
label: ''
loader:
- name: getSelectedConfigSecret|mongos
+ name: getSelectedConfigurationName|create|mongos
watchPaths:
- schema/properties/spec/properties/configuration/properties/mongos/properties/configSecret/properties/name
- type: block-layout
@@ -1218,6 +1223,8 @@ step:
label: String Data
schema: temp/properties/mongos/properties/createSecret/properties/data
buttonClass: is-light is-outlined
+ validation:
+ type: required
elements:
- type: select
label: Key
@@ -1261,7 +1268,7 @@ step:
- type: label-element
label: ''
loader:
- name: getSelectedApplyConfigName|mongos
+ name: getSelectedConfigurationName|apply|mongos
watchPaths:
- temp/properties/mongos/selectedConfiguration
- type: multi-file-editor
@@ -1297,7 +1304,7 @@ step:
- type: label-element
label: ''
loader:
- name: getSelectedConfigurationName|mongos
+ name: getSelectedConfigurationName|remove|mongos
watchPaths:
- temp/properties/mongos/selectedConfigurationRemove
- type: multi-file-editor
@@ -1315,8 +1322,6 @@ step:
label: Shard
showLabels: true
elements:
- - type: label-element
- label: ''
- type: tab-layout
label: New Config Secret
schema: temp/properties/reconfigurationType-shard
@@ -1344,7 +1349,7 @@ step:
refresh: true
label: Config Secret
loader:
- name: getConfigSecrets
+ name: getConfigSecrets|shard
watchPaths:
- schema/properties/metadata/properties/namespace
- temp/properties/shard/properties/createSecret/properties/status
@@ -1358,7 +1363,7 @@ step:
- type: label-element
label: ''
loader:
- name: getSelectedConfigSecret|shard
+ name: getSelectedConfigurationName|create|shard
watchPaths:
- schema/properties/spec/properties/configuration/properties/shard/properties/configSecret/properties/name
- type: block-layout
@@ -1381,6 +1386,8 @@ step:
label: String Data
schema: temp/properties/shard/properties/createSecret/properties/data
buttonClass: is-light is-outlined
+ validation:
+ type: required
elements:
- type: select
label: Key
@@ -1424,7 +1431,7 @@ step:
- type: label-element
label: ''
loader:
- name: getSelectedApplyConfigName|shard
+ name: getSelectedConfigurationName|apply|shard
watchPaths:
- temp/properties/shard/selectedConfiguration
- type: multi-file-editor
@@ -1460,7 +1467,7 @@ step:
- type: label-element
label: ''
loader:
- name: getSelectedConfigurationName|shard
+ name: getSelectedConfigurationName|remove|shard
watchPaths:
- temp/properties/shard/selectedConfigurationRemove
- type: multi-file-editor
diff --git a/charts/opskubedbcom-mongodbopsrequest-editor/ui/functions.js b/charts/opskubedbcom-mongodbopsrequest-editor/ui/functions.js
index 6698bc4abf..9a507605c2 100644
--- a/charts/opskubedbcom-mongodbopsrequest-editor/ui/functions.js
+++ b/charts/opskubedbcom-mongodbopsrequest-editor/ui/functions.js
@@ -315,7 +315,7 @@ const machineList = [
'db.r.24xlarge',
]
-const configSecretKeys = ['mongod.conf']
+const configSecretKeys = ['mongod.conf', 'replicaset.json', 'configuration.js']
let machinesFromPreset = []
@@ -900,9 +900,12 @@ export const useFunc = (model) => {
return machine === 'custom'
}
- // for config secret
- let newConfigSecrets = []
- async function getConfigSecrets() {
+ // Fetch and store database Infos
+ // for secret configurations in reconfigure
+ let configSecrets = []
+ let secretConfigData = []
+
+ async function fetchConfigSecrets() {
const owner = storeGet('/route/params/user')
const cluster = storeGet('/route/params/cluster')
const namespace = getValue(model, '/metadata/namespace')
@@ -913,7 +916,6 @@ export const useFunc = (model) => {
const dbKind = getValue(store.state, '/resource/definition/result/kind')
const dbResource = getValue(model, '/route/params/resource')
const dbVersion = getValue(model, '/route/params/version')
- let secrets = []
try {
const resp = await axios.post(
@@ -934,79 +936,47 @@ export const useFunc = (model) => {
version: dbVersion,
},
},
- keys: ['mongod.conf'],
+ keys: ['mongod.conf', 'replicaset.json', 'configuration.js'],
},
},
)
- secrets = resp?.data?.response?.availableSecrets || []
- newConfigSecrets = secrets
+ configSecrets = resp?.data?.response?.availableSecrets || []
+ secretConfigData = resp?.data?.response?.configurations || []
} catch (e) {
console.log(e)
}
- const mappedSecrets = secrets.map((item) => {
+ }
+
+ async function getConfigSecrets(type) {
+ type = type ? type + '/' : ''
+ const secretStatus = getValue(discriminator, `${type}createSecret/status`)
+ if (secretStatus === 'success') {
+ await fetchConfigSecrets()
+ }
+ const mappedSecrets = configSecrets.map((item) => {
return { text: item, value: item }
})
mappedSecrets.push({ text: '+ Create a new Secret', value: 'Create' })
return mappedSecrets
}
- let ConfigurationsData = []
-
async function getConfigSecretsforAppyConfig() {
- const owner = storeGet('/route/params/user')
- const cluster = storeGet('/route/params/cluster')
- const namespace = getValue(model, '/metadata/namespace')
- // watchDependency('model#/metadata/namespace')
-
- const name = getValue(model, '/spec/databaseRef/name')
- const dbGroup = getValue(model, '/route/params/group')
- const dbKind = getValue(store.state, '/resource/definition/result/kind')
- const dbResource = getValue(model, '/route/params/resource')
- const dbVersion = getValue(model, '/route/params/version')
-
- try {
- const resp = await axios.post(
- `/clusters/${owner}/${cluster}/proxy/ui.kubedb.com/v1alpha1/databaseinfos`,
- {
- apiVersion: 'ui.kubedb.com/v1alpha1',
- kind: 'DatabaseInfo',
- request: {
- source: {
- ref: {
- name: name,
- namespace: namespace,
- },
- resource: {
- group: dbGroup,
- kind: dbKind,
- name: dbResource,
- version: dbVersion,
- },
- },
- keys: ['mongod.conf'],
- },
- },
- )
- ConfigurationsData = resp?.data?.response?.configurations || []
- const secrets = ConfigurationsData.map((item) => {
- return { text: item.componentName, value: item.componentName }
- })
- return secrets
- } catch (e) {
- console.log(e)
- }
- return []
+ const secrets = secretConfigData.map((item) => {
+ return { text: item.componentName, value: item.componentName }
+ })
+ return secrets
}
function getSelectedConfigurationData(type) {
- const path = `/${type}/selectedConfiguration`
+ type = type ? type + '/' : ''
+ const path = `/${type}selectedConfiguration`
const selectedConfiguration = getValue(discriminator, path)
if (!selectedConfiguration) {
return []
}
- const configuration = ConfigurationsData.find(
+ const configuration = secretConfigData.find(
(item) => item.componentName === selectedConfiguration,
)
@@ -1035,7 +1005,7 @@ export const useFunc = (model) => {
// Set the value to the model
commit('wizard/model$update', {
- path: `/temp/${type}/applyConfig`,
+ path: `/temp/${type}applyConfig`,
value: result,
force: true,
})
@@ -1043,56 +1013,31 @@ export const useFunc = (model) => {
return result
}
- function getSelectedConfigurationName(type) {
- const path = `/${type}/selectedConfigurationRemove`
- const selectedConfiguration = getValue(discriminator, path)
-
- if (!selectedConfiguration) {
- return { subtitle: 'No secret selected' }
- }
+ function getSelectedConfigurationName(configType, type) {
+ type = type ? type + '/' : ''
+ let path = ''
+ if (configType === 'create') path = `/spec/configuration/${type}/configSecret/name`
+ else if (configType === 'apply') path = `/${type}selectedConfiguration`
+ else if (configType === 'remove') path = `/${type}selectedConfigurationRemove`
- const configuration = ConfigurationsData.find(
- (item) => item.componentName === selectedConfiguration,
- )
+ const selectedConfiguration =
+ configType === 'create' ? getValue(model, path) : getValue(discriminator, path)
- if (!configuration) {
- return { subtitle: 'No secret selected' }
- }
-
- if (configuration.componentName)
- return { subtitle: ` You have selected ${configuration.componentName} secret` }
+ if (selectedConfiguration)
+ return { subtitle: ` You have selected ${selectedConfiguration} secret` }
else return { subtitle: 'No secret selected' }
}
- function getSelectedApplyConfigName(type) {
- const path = `/${type}/selectedConfiguration`
- const selectedConfiguration = getValue(discriminator, path)
-
- if (!selectedConfiguration) {
- return { subtitle: 'No configuration selected' }
- }
-
- const configuration = ConfigurationsData.find(
- (item) => item.componentName === selectedConfiguration,
- )
-
- if (!configuration) {
- return { subtitle: 'No configuration selected' }
- }
- if (configuration.componentName)
- return { subtitle: ` You have selected ${configuration.componentName} configuration` }
- else return { subtitle: 'No configuration selected' }
- }
-
function getSelectedConfigurationValueForRemove(type) {
- const path = `/${type}/selectedConfigurationRemove`
+ type = type ? type + '/' : ''
+ const path = `/${type}selectedConfigurationRemove`
const selectedConfiguration = getValue(discriminator, path)
if (!selectedConfiguration) {
return ''
}
- const configuration = ConfigurationsData.find(
+ const configuration = secretConfigData.find(
(item) => item.componentName === selectedConfiguration,
)
@@ -1134,11 +1079,12 @@ export const useFunc = (model) => {
}
async function createNewConfigSecret(type) {
+ type = type ? type + '/' : ''
const { user, cluster } = route.params
const url = `/clusters/${user}/${cluster}/resources`
const namespace = storeGet('/route/query/namespace') || getValue(model, '/metadata/namespace')
- const secretName = getValue(discriminator, `${type}/createSecret/name`)
- const secretData = getValue(discriminator, `${type}/createSecret/data`)
+ const secretName = getValue(discriminator, `${type}createSecret/name`)
+ const secretData = getValue(discriminator, `${type}createSecret/data`)
const secretDataObj = Object.fromEntries(secretData.map((item) => [item.key, item.value]))
try {
@@ -1153,11 +1099,11 @@ export const useFunc = (model) => {
type: 'Opaque',
})
commit('wizard/temp$update', {
- path: `${type}/createSecret/status`,
+ path: `${type}createSecret/status`,
value: 'success',
})
commit('wizard/temp$update', {
- path: `${type}/createSecret/lastCreatedSecret`,
+ path: `${type}createSecret/lastCreatedSecret`,
value: secretName,
})
toast.success('Secret created successfully')
@@ -1181,12 +1127,13 @@ export const useFunc = (model) => {
}
function isCreateSecret(type) {
- const selectedSecret = getValue(model, `spec/configuration/${type}/configSecret/name`)
+ type = type ? type + '/' : ''
+ const selectedSecret = getValue(model, `spec/configuration/${type}configSecret/name`)
const res = selectedSecret === 'Create'
if (res === true) {
commit('wizard/temp$update', {
- path: `${type}/createSecret/status`,
+ path: `${type}createSecret/status`,
value: 'pending',
})
}
@@ -1198,140 +1145,33 @@ export const useFunc = (model) => {
}
function onCreateSecretChange(type) {
- const secretStatus = getValue(discriminator, `${type}/createSecret/status`)
+ type = type ? type + '/' : ''
+ const secretStatus = getValue(discriminator, `${type}createSecret/status`)
if (secretStatus === 'cancelled') return ''
else if (secretStatus === 'success') {
- const name = getValue(discriminator, `${type}/createSecret/lastCreatedSecret`)
+ const name = getValue(discriminator, `${type}createSecret/lastCreatedSecret`)
- const configFound = newConfigSecrets.find((item) => item === name)
+ const configFound = configSecrets.find((item) => item === name)
return configFound ? { text: name, value: name } : ''
}
}
function cancelCreateSecret(type) {
- commit('wizard/temp$delete', `${type}/createSecret/name`)
- commit('wizard/temp$delete', `${type}/createSecret/data`)
+ type = type ? type + '/' : ''
+ commit('wizard/temp$delete', `${type}createSecret/name`)
+ commit('wizard/temp$delete', `${type}createSecret/data`)
commit('wizard/temp$update', {
- path: `${type}/createSecret/status`,
+ path: `${type}createSecret/status`,
value: 'cancelled',
})
}
- function isEqualToValueFromType(value) {
- // watchDependency('discriminator#/valueFromType')
- const valueFrom = getValue(discriminator, '/valueFromType')
- return valueFrom === value
- }
-
- async function getNamespacedResourceList({ namespace, group, version, resource }) {
- const owner = storeGet('/route/params/user')
- const cluster = storeGet('/route/params/cluster')
-
- const url = `/clusters/${owner}/${cluster}/proxy/${group}/${version}/namespaces/${namespace}/${resource}`
-
- let ans = []
- try {
- const resp = await axios.get(url, {
- params: {
- filter: { items: { metadata: { name: null }, type: null } },
- },
- })
-
- const items = (resp && resp.data && resp.data.items) || []
- ans = items
- } catch (e) {
- console.log(e)
- }
-
- return ans
- }
- async function getResourceList({ group, version, resource }) {
- const owner = storeGet('/route/params/user')
- const cluster = storeGet('/route/params/cluster')
-
- const url = `/clusters/${owner}/${cluster}/proxy/${group}/${version}/${resource}`
-
- let ans = []
- try {
- const resp = await axios.get(url, {
- params: {
- filter: { items: { metadata: { name: null }, type: null } },
- },
- })
-
- const items = (resp && resp.data && resp.data.items) || []
- ans = items
- } catch (e) {
- console.log(e)
- }
-
- return ans
- }
- async function resourceNames(group, version, resource) {
- const namespace = getValue(model, '/metadata/namespace')
- // watchDependency('model#/metadata/namespace')
-
- let resources = await getNamespacedResourceList({
- namespace,
- group,
- version,
- resource,
- })
-
- if (resource === 'secrets') {
- resources = resources.filter((item) => {
- const validType = ['kubernetes.io/service-account-token', 'Opaque']
- return validType.includes(item.type)
- })
- }
-
- return resources.map((resource) => {
- const name = (resource.metadata && resource.metadata.name) || ''
- return {
- text: name,
- value: name,
- }
- })
- }
- async function unNamespacedResourceNames(group, version, resource) {
- let resources = await getResourceList({
- group,
- version,
- resource,
- })
-
- if (resource === 'secrets') {
- resources = resources.filter((item) => {
- const validType = ['kubernetes.io/service-account-token', 'Opaque']
- return validType.includes(item.type)
- })
- }
-
- return resources.map((resource) => {
- const name = (resource.metadata && resource.metadata.name) || ''
- return {
- text: name,
- value: name,
- }
- })
- }
-
- // reconfiguration type
- function ifReconfigurationTypeEqualsTo(value, property, isShard) {
- let path = '/reconfigurationType'
- if (isShard) path += `-${property}`
- const reconfigurationType = getValue(discriminator, path)
-
- const watchPath = `discriminator#${path}`
- // watchDependency(watchPath)
- return reconfigurationType === value
- }
-
async function onApplyconfigChange(type) {
- const configValue = getValue(discriminator, `/${type}/applyConfig`)
+ type = type ? type + '/' : ''
+ const configValue = getValue(discriminator, `${type}applyConfig`)
if (!configValue) {
- commit('wizard/model$delete', `/spec/configuration/${type}/applyConfig`)
+ commit('wizard/model$delete', `/spec/configuration/${type}applyConfig`)
return
}
const tempConfigObj = {}
@@ -1341,70 +1181,58 @@ export const useFunc = (model) => {
}
})
if (Object.keys(tempConfigObj).length === 0) {
- commit('wizard/model$delete', `/spec/configuration/${type}/applyConfig`)
+ commit('wizard/model$delete', `/spec/configuration/${type}applyConfig`)
return
}
commit('wizard/model$update', {
- path: `/spec/configuration/${type}/applyConfig`,
+ path: `/spec/configuration/${type}applyConfig`,
value: tempConfigObj,
})
}
function setApplyConfig(type) {
- const configPath = `/${type}/selectedConfiguration`
+ type = type ? type + '/' : ''
+ const configPath = `/${type}selectedConfiguration`
const selectedConfig = getValue(discriminator, configPath)
if (!selectedConfig) {
return [{ name: '', content: '' }]
}
- const applyconfig = ConfigurationsData.find((item) => {
+ const applyconfigData = secretConfigData.find((item) => {
if (item.componentName === selectedConfig) {
return item
}
})
-
- const { secretName, data } = applyconfig
+ const { applyConfig } = applyconfigData
const configObj = []
- const tempConfigObj = {}
- if (data) {
- // Decode base64 and format as array of objects with name and content
- Object.keys(data).forEach((fileName) => {
- try {
- // Decode base64 string
- const decodedString = atob(data[fileName])
- configObj.push({
- name: fileName,
- content: decodedString,
- })
- tempConfigObj[fileName] = decodedString
- } catch (e) {
- console.error(`Error decoding ${fileName}:`, e)
- configObj.push({
- name: fileName,
- content: data[fileName], // Fallback to original if decode fails
- })
- }
+ if (applyConfig) {
+ Object.keys(applyConfig).forEach((fileName) => {
+ configObj.push({
+ name: fileName,
+ content: applyConfig[fileName],
+ })
})
} else {
- configObj.push({ name: selectedConfig, content: '' })
+ configObj.push({ name: '', content: '' })
}
return configObj
}
function onRemoveConfigChange(type) {
- const configPath = `/${type}/selectedConfigurationRemove`
+ type = type ? type + '/' : ''
+ const configPath = `/${type}selectedConfigurationRemove`
const selectedConfig = getValue(discriminator, configPath)
if (!selectedConfig) {
- commit('wizard/model$delete', `/spec/configuration/${type}/removeCustomConfig`)
+ commit('wizard/model$delete', `/spec/configuration/${type}removeCustomConfig`)
return [{ name: '', content: '' }]
}
commit('wizard/model$update', {
- path: `/spec/configuration/${type}/removeCustomConfig`,
+ path: `/spec/configuration/${type}removeCustomConfig`,
value: true,
})
- const configuration = ConfigurationsData.find((item) => item.componentName === selectedConfig)
+ const configuration = secretConfigData.find((item) => item.componentName === selectedConfig)
if (!configuration.data) {
return [{ name: '', content: '' }]
@@ -1432,13 +1260,15 @@ export const useFunc = (model) => {
}
async function onNewConfigSecretChange(type) {
- const path = `/spec/configuration/${type}/configSecret/name`
+ type = type ? type + '/' : ''
+ const path = `/spec/configuration/${type}configSecret/name`
const selectedSecret = getValue(model, path)
if (!selectedSecret) {
- commit('wizard/model$delete', `/spec/configuration/${type}/configSecret`)
+ commit('wizard/model$delete', `/spec/configuration/${type}configSecret`)
return [{ name: '', content: '' }]
}
+ if (selectedSecret === 'Create') return [{ name: '', content: '' }]
const owner = storeGet('/route/params/user')
const cluster = storeGet('/route/params/cluster')
@@ -1479,12 +1309,13 @@ export const useFunc = (model) => {
}
function onSelectedSecretChange(type, index) {
- const secretData = getValue(discriminator, `${type}/createSecret/data`) || []
+ type = type ? type + '/' : ''
+ const secretData = getValue(discriminator, `${type}createSecret/data`) || []
const selfSecrets = secretData.map((item) => item.key)
const remainingSecrets = configSecretKeys.filter((item) => !selfSecrets.includes(item))
- const selfKey = getValue(discriminator, `${type}/createSecret/data/${index}/key`)
+ const selfKey = getValue(discriminator, `${type}createSecret/data/${index}/key`)
if (selfKey) {
remainingSecrets.push(selfKey)
}
@@ -1494,6 +1325,16 @@ export const useFunc = (model) => {
return resSecret
}
+ function ifReconfigurationTypeEqualsTo(value, property, isShard) {
+ let path = '/reconfigurationType'
+ if (isShard) path += `-${property}`
+ const reconfigurationType = getValue(discriminator, path)
+
+ const watchPath = `discriminator#${path}`
+ // watchDependency(watchPath)
+ return reconfigurationType === value
+ }
+
function onReconfigurationTypeChange(property, isShard) {
setDiscriminatorValue(`/${property}/applyConfig`, [])
let path = '/reconfigurationType'
@@ -1514,6 +1355,105 @@ export const useFunc = (model) => {
}
}
+ function isEqualToValueFromType(value) {
+ // watchDependency('discriminator#/valueFromType')
+ const valueFrom = getValue(discriminator, '/valueFromType')
+ return valueFrom === value
+ }
+
+ async function getNamespacedResourceList({ namespace, group, version, resource }) {
+ const owner = storeGet('/route/params/user')
+ const cluster = storeGet('/route/params/cluster')
+
+ const url = `/clusters/${owner}/${cluster}/proxy/${group}/${version}/namespaces/${namespace}/${resource}`
+
+ let ans = []
+ try {
+ const resp = await axios.get(url, {
+ params: {
+ filter: { items: { metadata: { name: null }, type: null } },
+ },
+ })
+
+ const items = (resp && resp.data && resp.data.items) || []
+ ans = items
+ } catch (e) {
+ console.log(e)
+ }
+
+ return ans
+ }
+ async function getResourceList({ group, version, resource }) {
+ const owner = storeGet('/route/params/user')
+ const cluster = storeGet('/route/params/cluster')
+
+ const url = `/clusters/${owner}/${cluster}/proxy/${group}/${version}/${resource}`
+
+ let ans = []
+ try {
+ const resp = await axios.get(url, {
+ params: {
+ filter: { items: { metadata: { name: null }, type: null } },
+ },
+ })
+
+ const items = (resp && resp.data && resp.data.items) || []
+ ans = items
+ } catch (e) {
+ console.log(e)
+ }
+
+ return ans
+ }
+ async function resourceNames(group, version, resource) {
+ const namespace = getValue(model, '/metadata/namespace')
+ // watchDependency('model#/metadata/namespace')
+
+ let resources = await getNamespacedResourceList({
+ namespace,
+ group,
+ version,
+ resource,
+ })
+
+ if (resource === 'secrets') {
+ resources = resources.filter((item) => {
+ const validType = ['kubernetes.io/service-account-token', 'Opaque']
+ return validType.includes(item.type)
+ })
+ }
+
+ return resources.map((resource) => {
+ const name = (resource.metadata && resource.metadata.name) || ''
+ return {
+ text: name,
+ value: name,
+ }
+ })
+ }
+ async function unNamespacedResourceNames(group, version, resource) {
+ let resources = await getResourceList({
+ group,
+ version,
+ resource,
+ })
+
+ if (resource === 'secrets') {
+ resources = resources.filter((item) => {
+ const validType = ['kubernetes.io/service-account-token', 'Opaque']
+ return validType.includes(item.type)
+ })
+ }
+
+ return resources.map((resource) => {
+ const name = (resource.metadata && resource.metadata.name) || ''
+ return {
+ text: name,
+ value: name,
+ }
+ })
+ }
+
// for tls
function hasTlsField() {
const tls = getDbTls()
@@ -1764,14 +1704,6 @@ export const useFunc = (model) => {
return !!(model && model.alias)
}
- function getSelectedConfigSecret(type) {
- const path = `/spec/configuration/${type}/configSecret/name`
- const selectedSecret = getValue(model, path)
- // watchDependency(`model#${path}`)
- if (selectedSecret) return { subtitle: `You have selected ${selectedSecret} secret` }
- return { subtitle: 'No secret selected' }
- }
-
function objectToYaml(obj, indent = 0) {
if (obj === null || obj === undefined) return 'null'
if (typeof obj !== 'object') return JSON.stringify(obj)
@@ -1901,7 +1833,7 @@ export const useFunc = (model) => {
showAndInitOpsRequestType,
ifDbTypeEqualsTo,
getConfigSecrets,
- getSelectedConfigSecret,
+ fetchConfigSecrets,
createSecretUrl,
isEqualToValueFromType,
getNamespacedResourceList,
@@ -1941,7 +1873,6 @@ export const useFunc = (model) => {
getSelectedConfigurationValueForRemove,
onRemoveConfigChange,
onNewConfigSecretChange,
- getSelectedApplyConfigName,
createNewConfigSecret,
isCreateSecret,
isNotCreateSecret,
diff --git a/charts/opskubedbcom-mssqlserveropsrequest-editor/ui/create-ui.yaml b/charts/opskubedbcom-mssqlserveropsrequest-editor/ui/create-ui.yaml
index 0b7e55b998..e8665373b9 100644
--- a/charts/opskubedbcom-mssqlserveropsrequest-editor/ui/create-ui.yaml
+++ b/charts/opskubedbcom-mssqlserveropsrequest-editor/ui/create-ui.yaml
@@ -295,102 +295,177 @@ step:
if:
name: ifRequestTypeEqualsTo|Reconfigure
type: function
+ loader:
+ name: fetchConfigSecrets
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
elements:
- type: block-layout
label: Configuration
elements:
- - type: radio
- label: Reconfigure Type
- subtitle: Choose the action you'd like to perform. Select a new configuration secret, apply a custom configuration, or remove an existing setup.
- validation:
- type: required
+ - type: label-element
+ label: ''
+ subtitle: Select a new configuration secret, apply a custom configuration, or remove an existing setup to update your database settings
+ - type: tab-layout
+ label: New Config Secret
+ schema: temp/properties/reconfigurationType
options:
- - text: New Config Secret
+ - text: NEW CONFIG SECRET
value: selectNewConfigSecret
- - text: Apply Config
+ - text: APPLY CONFIG
value: applyConfig
- - text: Remove
+ - text: REMOVE
value: remove
- schema: temp/properties/reconfigurationType
- watcher:
- func: onReconfigurationTypeChange
- paths:
- - temp/properties/reconfigurationType
- - type: block-layout
- label: Config Secret
- showLabels: true
- if:
- name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret
- type: function
elements:
- - type: label-element
- label: Select a configuration secret from the available list to update your database settings
- customClass: mb-10
- - type: select
- addNewButton:
- label: Create Secret
- target: _blank
- url:
- function: createSecretUrl
- schema: schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- validation:
- type: required
- refresh: true
- label: Config Secret
- loader:
- name: getConfigSecrets
- watchPaths:
- - schema/properties/metadata/properties/namespace
- init:
- type: func
- value: setValueFromDbDetails|/spec/configSecret/name
- - type: label-element
- label: Selected Config Secret
- loader: getSelectedConfigSecret
- watcher:
- func: getSelectedConfigSecret
- paths:
- - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- - type: editor
- label: Value
- readonly: true
- hasCopy: false
- watcher:
- func: getSelectedConfigSecretValue
- paths:
- - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- validation:
- type: required
- schema: temp/properties/configArray/items/properties/value
- - type: array-object-form
- label: ApplyConfig
- buttonClass: is-light is-outlined
- schema: temp/properties/applyConfig
- validation:
- type: required
- if:
- name: ifReconfigurationTypeEqualsTo|applyConfig
- type: function
- watcher:
- func: onApplyconfigChange
- paths:
- - temp/properties/applyConfig
- elements:
- - type: label-element
- label: New Apply Config
- subtitle: Define custom configurations for your database using key-value pairs. These parameters will overwrite existing settings. Enter the parameter you want to configure (e.g., max_connections).
- customClass: mb-10
- - type: input
- label: key
- validation:
- type: required
- schema: key
- - type: editor
- label: value
- hasCopy: false
- validation:
- type: required
- schema: value
+ - type: block-layout
+ label: Config Secret
+ if:
+ name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret
+ type: function
+ elements:
+ - type: label-element
+ label: Config Secret
+ subtitle: Select a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ refresh: true
+ label: Config Secret
+ loader:
+ name: getConfigSecrets
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - temp/properties/createSecret/properties/status
+ init:
+ type: func
+ value: setValueFromDbDetails|/spec/configSecret/name
+ watcher:
+ func: onCreateSecretChange
+ paths:
+ - temp/properties/createSecret/properties/status
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|create
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ - type: block-layout
+ label: Create a New Config Secret
+ showLabels: true
+ if:
+ type: function
+ name: isCreateSecret
+ hasButton:
+ text: Save
+ hasCancel: cancelCreateSecret
+ action: createNewConfigSecret
+ elements:
+ - type: input
+ label: Secret Name
+ schema: temp/properties/createSecret/properties/name
+ validation:
+ type: required
+ - type: array-object-form
+ label: String Data
+ schema: temp/properties/createSecret/properties/data
+ buttonClass: is-light is-outlined
+ validation:
+ type: required
+ elements:
+ - type: select
+ label: Key
+ schema: key
+ loader:
+ name: onSelectedSecretChange
+ watchPaths:
+ - temp/properties/createSecret/properties/data
+ validation:
+ type: required
+ - type: textarea
+ label: Value
+ schema: value
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ if:
+ type: function
+ name: isNotCreateSecret
+ loader:
+ name: onNewConfigSecretChange
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ schema: temp/properties/newConfigSecret
+ - type: block-layout
+ label: ApplyConfig
+ if:
+ name: ifReconfigurationTypeEqualsTo|applyConfig
+ type: function
+ elements:
+ - type: label-element
+ label: New Apply Config
+ subtitle: Define custom configurations for your database using key-value pairs. These parameters will overwrite existing settings.
Enter the parameter you want to configure (e.g., max_connections).
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/selectedConfiguration
+ refresh: true
+ label: Configuration
+ loader: getConfigSecretsforAppyConfig
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|apply
+ watchPaths:
+ - temp/properties/selectedConfiguration
+ - type: multi-file-editor
+ editorHeight: 500px
+ schema: temp/properties/applyConfig
+ loader:
+ name: setApplyConfig
+ watchPaths:
+ - temp/properties/selectedConfiguration
+ watcher:
+ func: onApplyconfigChange
+ paths:
+ - temp/properties/applyConfig
+ - type: block-layout
+ label: Remove
+ if:
+ name: ifReconfigurationTypeEqualsTo|remove
+ type: function
+ elements:
+ - type: label-element
+ label: Remove
+ subtitle: Selected a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/selectedConfigurationRemove
+ refresh: true
+ label: Configuration
+ loader:
+ name: getConfigSecretsforAppyConfig
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|remove
+ watchPaths:
+ - temp/properties/selectedConfigurationRemove
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ init:
+ type: func
+ value: onRemoveConfigChange
+ watcher:
+ func: onRemoveConfigChange
+ paths:
+ - temp/properties/selectedConfigurationRemove
+ schema: temp/properties/removeConfig
+
# common
- type: block-layout
label: OpsRequest Options
diff --git a/charts/opskubedbcom-mssqlserveropsrequest-editor/ui/functions.js b/charts/opskubedbcom-mssqlserveropsrequest-editor/ui/functions.js
index c856354745..96102b60f6 100644
--- a/charts/opskubedbcom-mssqlserveropsrequest-editor/ui/functions.js
+++ b/charts/opskubedbcom-mssqlserveropsrequest-editor/ui/functions.js
@@ -1,4 +1,4 @@
-const { axios, useOperator, store } = window.vueHelpers || {}
+const { axios, useOperator, store, useToast } = window.vueHelpers || {}
const machines = {
'db.t.micro': {
resources: {
@@ -306,9 +306,11 @@ const machineList = [
]
let machinesFromPreset = []
+const configSecretKeys = ['mssql.conf']
export const useFunc = (model) => {
const route = store.state?.route
+ const toast = useToast()
const { getValue, storeGet, discriminator, setDiscriminatorValue, commit } = useOperator(
model,
@@ -828,32 +830,419 @@ export const useFunc = (model) => {
return machine === 'custom'
}
- // for config secret
- let secretArray = []
- async function getConfigSecrets() {
+ // Fetch and store database Infos
+ // for secret configurations in reconfigure
+ let configSecrets = []
+ let secretConfigData = []
+
+ async function fetchConfigSecrets() {
const owner = storeGet('/route/params/user')
const cluster = storeGet('/route/params/cluster')
const namespace = getValue(model, '/metadata/namespace')
// watchDependency('model#/metadata/namespace')
- const resp = await axios.get(
- `/clusters/${owner}/${cluster}/proxy/core/v1/namespaces/${namespace}/secrets`,
+ const name = getValue(model, '/spec/databaseRef/name')
+ const dbGroup = getValue(model, '/route/params/group')
+ const dbKind = getValue(store.state, '/resource/definition/result/kind')
+ const dbResource = getValue(model, '/route/params/resource')
+ const dbVersion = getValue(model, '/route/params/version')
+
+ try {
+ const resp = await axios.post(
+ `/clusters/${owner}/${cluster}/proxy/ui.kubedb.com/v1alpha1/databaseinfos`,
+ {
+ apiVersion: 'ui.kubedb.com/v1alpha1',
+ kind: 'DatabaseInfo',
+ request: {
+ source: {
+ ref: {
+ name: name,
+ namespace: namespace,
+ },
+ resource: {
+ group: dbGroup,
+ kind: dbKind,
+ name: dbResource,
+ version: dbVersion,
+ },
+ },
+ keys: ['mssql.conf'],
+ },
+ },
+ )
+ configSecrets = resp?.data?.response?.availableSecrets || []
+ secretConfigData = resp?.data?.response?.configurations || []
+ } catch (e) {
+ console.log(e)
+ }
+ }
+
+ async function getConfigSecrets(type) {
+ type = type ? type + '/' : ''
+ const secretStatus = getValue(discriminator, `${type}createSecret/status`)
+ if (secretStatus === 'success') {
+ await fetchConfigSecrets()
+ }
+ const mappedSecrets = configSecrets.map((item) => {
+ return { text: item, value: item }
+ })
+ mappedSecrets.push({ text: '+ Create a new Secret', value: 'Create' })
+ return mappedSecrets
+ }
+
+ async function getConfigSecretsforAppyConfig() {
+ const secrets = secretConfigData.map((item) => {
+ return { text: item.componentName, value: item.componentName }
+ })
+ return secrets
+ }
+
+ function getSelectedConfigurationData(type) {
+ type = type ? type + '/' : ''
+ const path = `/${type}selectedConfiguration`
+ const selectedConfiguration = getValue(discriminator, path)
+
+ if (!selectedConfiguration) {
+ return []
+ }
+
+ const configuration = secretConfigData.find(
+ (item) => item.componentName === selectedConfiguration,
)
- const secrets = (resp && resp.data && resp.data.items) || []
- secretArray = secrets
+ if (!configuration) {
+ return []
+ }
- const filteredSecrets = secrets
+ const result = []
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(configuration.data).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedContent = atob(configuration.data[fileName])
+ result.push({
+ name: fileName,
+ content: decodedContent,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ result.push({
+ name: fileName,
+ content: configuration.data[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
- filteredSecrets.map((item) => {
- const name = (item.metadata && item.metadata.name) || ''
- item.text = name
- item.value = name
- return true
+ // Set the value to the model
+ commit('wizard/model$update', {
+ path: `/temp/${type}applyConfig`,
+ value: result,
+ force: true,
+ })
+
+ return result
+ }
+
+ function getSelectedConfigurationName(configType, type) {
+ type = type ? type + '/' : ''
+ let path = ''
+ if (configType === 'create') path = `/spec/configuration/${type}/configSecret/name`
+ else if (configType === 'apply') path = `/${type}selectedConfiguration`
+ else if (configType === 'remove') path = `/${type}selectedConfigurationRemove`
+
+ const selectedConfiguration =
+ configType === 'create' ? getValue(model, path) : getValue(discriminator, path)
+
+ if (selectedConfiguration)
+ return { subtitle: ` You have selected ${selectedConfiguration} secret` }
+ else return { subtitle: 'No secret selected' }
+ }
+
+ function getSelectedConfigurationValueForRemove(type) {
+ type = type ? type + '/' : ''
+ const path = `/${type}selectedConfigurationRemove`
+ const selectedConfiguration = getValue(discriminator, path)
+
+ if (!selectedConfiguration) {
+ return ''
+ }
+
+ const configuration = secretConfigData.find(
+ (item) => item.componentName === selectedConfiguration,
+ )
+
+ if (!configuration) {
+ return ''
+ }
+
+ let data = {}
+ // Decode base64 and parse YAML for each key in the secret data
+ Object.keys(configuration.data).forEach((item) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(configuration.data[item])
+ // Parse YAML string to object
+ const parsedYaml = yaml.load(decodedString)
+ // Store the parsed object with the filename as key
+ data[item] = parsedYaml
+ } catch (e) {
+ console.error(`Error parsing ${item}:`, e)
+ data[item] = atob(configuration.data[item]) // Fallback to decoded string
+ }
+ })
+
+ // Convert data object back to YAML string
+ return yaml.dump(data)
+ }
+
+ async function createNewConfigSecret(type) {
+ type = type ? type + '/' : ''
+ const { user, cluster } = route.params
+ const url = `/clusters/${user}/${cluster}/resources`
+ const namespace = storeGet('/route/query/namespace') || getValue(model, '/metadata/namespace')
+ const secretName = getValue(discriminator, `${type}createSecret/name`)
+ const secretData = getValue(discriminator, `${type}createSecret/data`)
+ const secretDataObj = Object.fromEntries(secretData.map((item) => [item.key, item.value]))
+
+ try {
+ const res = await axios.post(url, {
+ apiVersion: 'v1',
+ stringData: secretDataObj,
+ kind: 'Secret',
+ metadata: {
+ name: secretName,
+ namespace: namespace,
+ },
+ type: 'Opaque',
+ })
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'success',
+ })
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/lastCreatedSecret`,
+ value: secretName,
+ })
+ toast.success('Secret created successfully')
+ } catch (error) {
+ const errMsg = decodeError(error, 'Failed to create secret')
+ toast.error(errMsg, { timeout: 5000 })
+ cancelCreateSecret()
+ }
+ }
+
+ function decodeError(msg, defaultMsg) {
+ if (typeof msg === 'string') {
+ return msg || defaultMsg
+ }
+ return (
+ (msg.response && msg.response.data && msg.response.data.message) ||
+ (msg.response && msg.response.data) ||
+ (msg.status && msg.status.status) ||
+ defaultMsg
+ )
+ }
+
+ function isCreateSecret(type) {
+ type = type ? type + '/' : ''
+ const selectedSecret = getValue(model, `spec/configuration/${type}configSecret/name`)
+ const res = selectedSecret === 'Create'
+
+ if (res === true) {
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'pending',
+ })
+ }
+ return res
+ }
+
+ function isNotCreateSecret(type) {
+ return !isCreateSecret(type)
+ }
+
+ function onCreateSecretChange(type) {
+ type = type ? type + '/' : ''
+ const secretStatus = getValue(discriminator, `${type}createSecret/status`)
+ if (secretStatus === 'cancelled') return ''
+ else if (secretStatus === 'success') {
+ const name = getValue(discriminator, `${type}createSecret/lastCreatedSecret`)
+
+ const configFound = configSecrets.find((item) => item === name)
+ return configFound ? { text: name, value: name } : ''
+ }
+ }
+
+ function cancelCreateSecret(type) {
+ type = type ? type + '/' : ''
+ commit('wizard/temp$delete', `${type}createSecret/name`)
+ commit('wizard/temp$delete', `${type}createSecret/data`)
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'cancelled',
+ })
+ }
+
+ async function onApplyconfigChange(type) {
+ type = type ? type + '/' : ''
+ const configValue = getValue(discriminator, `${type}applyConfig`)
+
+ if (!configValue) {
+ commit('wizard/model$delete', `/spec/configuration/${type}applyConfig`)
+ return
+ }
+ const tempConfigObj = {}
+ configValue.forEach((item) => {
+ if (item.name) {
+ tempConfigObj[item.name] = item.content
+ }
+ })
+ if (Object.keys(tempConfigObj).length === 0) {
+ commit('wizard/model$delete', `/spec/configuration/${type}applyConfig`)
+ return
+ }
+ commit('wizard/model$update', {
+ path: `/spec/configuration/${type}applyConfig`,
+ value: tempConfigObj,
+ })
+ }
+
+ function setApplyConfig(type) {
+ type = type ? type + '/' : ''
+ const configPath = `/${type}selectedConfiguration`
+ const selectedConfig = getValue(discriminator, configPath)
+ if (!selectedConfig) {
+ return [{ name: '', content: '' }]
+ }
+ const applyconfigData = secretConfigData.find((item) => {
+ if (item.componentName === selectedConfig) {
+ return item
+ }
+ })
+ const { applyConfig } = applyconfigData
+ const configObj = []
+
+ if (applyConfig) {
+ Object.keys(applyConfig).forEach((fileName) => {
+ configObj.push({
+ name: fileName,
+ content: applyConfig[fileName],
+ })
+ })
+ } else {
+ configObj.push({ name: '', content: '' })
+ }
+ return configObj
+ }
+
+ function onRemoveConfigChange(type) {
+ type = type ? type + '/' : ''
+ const configPath = `/${type}selectedConfigurationRemove`
+ const selectedConfig = getValue(discriminator, configPath)
+
+ if (!selectedConfig) {
+ commit('wizard/model$delete', `/spec/configuration/${type}removeCustomConfig`)
+ return [{ name: '', content: '' }]
+ }
+ commit('wizard/model$update', {
+ path: `/spec/configuration/${type}removeCustomConfig`,
+ value: true,
+ })
+
+ const configuration = secretConfigData.find((item) => item.componentName === selectedConfig)
+
+ if (!configuration.data) {
+ return [{ name: '', content: '' }]
+ }
+
+ const configObj = []
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(configuration.data).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(configuration.data[fileName])
+ configObj.push({
+ name: fileName,
+ content: decodedString,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ configObj.push({
+ name: fileName,
+ content: configuration.data[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
+ return configObj
+ }
+
+ async function onNewConfigSecretChange(type) {
+ type = type ? type + '/' : ''
+ const path = `/spec/configuration/${type}configSecret/name`
+ const selectedSecret = getValue(model, path)
+
+ if (!selectedSecret) {
+ commit('wizard/model$delete', `/spec/configuration/${type}configSecret`)
+ return [{ name: '', content: '' }]
+ }
+ if (selectedSecret === 'Create') return [{ name: '', content: '' }]
+
+ const owner = storeGet('/route/params/user')
+ const cluster = storeGet('/route/params/cluster')
+ const namespace = storeGet('/route/query/namespace') || getValue(model, '/metadata/namespace')
+
+ try {
+ // Fetch the secret data from API
+ const secretResp = await axios.get(
+ `/clusters/${owner}/${cluster}/proxy/core/v1/namespaces/${namespace}/secrets/${selectedSecret}`,
+ )
+
+ const secretData = secretResp.data?.data || {}
+ const configObj = []
+
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(secretData).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(secretData[fileName])
+ configObj.push({
+ name: fileName,
+ content: decodedString,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ configObj.push({
+ name: fileName,
+ content: secretData[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
+
+ return configObj
+ } catch (e) {
+ console.error('Error fetching secret:', e)
+ return [{ name: '', content: '' }]
+ }
+ }
+
+ function onSelectedSecretChange(index) {
+ const secretData = getValue(discriminator, 'createSecret/data') || []
+ const selfSecrets = secretData.map((item) => item.key)
+
+ const remainingSecrets = configSecretKeys.filter((item) => !selfSecrets.includes(item))
+
+ const selfKey = getValue(discriminator, `createSecret/data/${index}/key`)
+ if (selfKey) {
+ remainingSecrets.push(selfKey)
+ }
+ const resSecret = remainingSecrets.map((item) => {
+ return { text: item, value: item }
})
- return filteredSecrets
+ return resSecret
}
+ let secretArray = []
+
function createSecretUrl() {
const user = storeGet('/route/params/user')
const cluster = storeGet('/route/params/cluster')
@@ -977,24 +1366,6 @@ export const useFunc = (model) => {
return reconfigurationType === value
}
- function onApplyconfigChange() {
- const applyconfig = getValue(discriminator, '/applyConfig')
-
- const configObj = {}
- if (applyconfig) {
- applyconfig.forEach((item) => {
- const { key, value } = item
- configObj[key] = value
- })
- }
-
- commit('wizard/model$update', {
- path: '/spec/configuration/applyConfig',
- value: configObj,
- force: true,
- })
- }
-
function onReconfigurationTypeChange() {
const reconfigurationType = getValue(discriminator, '/reconfigurationType')
setDiscriminatorValue('/applyConfig', [])
@@ -1389,5 +1760,20 @@ export const useFunc = (model) => {
onMachineChange,
isMachineCustom,
checkVolume,
+ fetchConfigSecrets,
+ getConfigSecretsforAppyConfig,
+ getSelectedConfigurationData,
+ getSelectedConfigurationName,
+ getSelectedConfigurationValueForRemove,
+ createNewConfigSecret,
+ decodeError,
+ isCreateSecret,
+ isNotCreateSecret,
+ onCreateSecretChange,
+ cancelCreateSecret,
+ setApplyConfig,
+ onRemoveConfigChange,
+ onNewConfigSecretChange,
+ onSelectedSecretChange,
}
}
diff --git a/charts/opskubedbcom-mysqlopsrequest-editor/ui/create-ui.yaml b/charts/opskubedbcom-mysqlopsrequest-editor/ui/create-ui.yaml
index f7958aff64..f88e2eab6e 100644
--- a/charts/opskubedbcom-mysqlopsrequest-editor/ui/create-ui.yaml
+++ b/charts/opskubedbcom-mysqlopsrequest-editor/ui/create-ui.yaml
@@ -302,115 +302,176 @@ step:
if:
name: ifRequestTypeEqualsTo|Reconfigure
type: function
+ loader:
+ name: fetchConfigSecrets
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
elements:
- type: block-layout
label: Configuration
elements:
- - type: radio
- label: Reconfigure Type
- subtitle: Choose the action you'd like to perform. Select a new configuration secret, apply a custom configuration , or remove an existing setup.
- validation:
- type: required
+ - type: label-element
+ label: ''
+ subtitle: Select a new configuration secret, apply a custom configuration, or remove an existing setup to update your database settings
+ - type: tab-layout
+ label: New Config Secret
+ schema: temp/properties/reconfigurationType
options:
- - text: New Config Secret
+ - text: NEW CONFIG SECRET
value: selectNewConfigSecret
- - text: Apply Config
+ - text: APPLY CONFIG
value: applyConfig
- - text: Remove
+ - text: REMOVE
value: remove
- schema: temp/properties/reconfigurationType
- watcher:
- func: onReconfigurationTypeChange
- paths:
- - temp/properties/reconfigurationType
- - type: block-layout
- label: Config Secret
- showLabels: true
- if:
- name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret
- type: function
- elements:
- - type: label-element
- label: Select a configuration secret from the available list to update your database settings
- customClass: mb-10
- - type: select
- addNewButton:
- label: Create a new Secret
- target: _blank
- url:
- function: createSecretUrl
- schema: schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- validation:
- type: required
- refresh: true
- label: Config Secret
- loader:
- name: getConfigSecrets
- watchPaths:
- - schema/properties/metadata/properties/namespace
- init:
- type: func
- value: setValueFromDbDetails|/spec/configSecret/name
- - type: label-element
- label: Selected Config Secret
- loader: getSelectedConfigSecret|mysql
- if:
- name: isConfigSelected
- type: function
- watcher:
- func: getSelectedConfigSecret|mysql
- paths:
- - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- - type: editor
- label: Value
- readonly: true
- hasCopy: false
- if:
- name: isConfigSelected
- type: function
- loader:
- name: getSelectedConfigSecretValue|mysql
- watchPaths:
- - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- validation:
- type: required
- schema: temp/properties/configArray/items/properties/value
- - type: array-object-form
- label: ApplyConfig
- buttonClass: is-light is-outlined
- schema: temp/properties/applyConfig
- validation:
- type: required
- if:
- name: ifReconfigurationTypeEqualsTo|applyConfig
- type: function
- watcher:
- func: onApplyconfigChange
- paths:
- - temp/properties/applyConfig
elements:
- - type: label-element
- label: New Apply Config
- subtitle: Define custom configurations for your database using key-value pairs. These parameters will overwrite existing settings. Enter the parameter you want to configure (e.g., max_connections).
- customClass: mb-10
- - type: input
- label: key
- validation:
- type: required
- schema: key
- - type: editor
- label: value
- hasCopy: false
- validation:
- type: required
- schema: value
- - type: switch
- schema: schema/properties/spec/properties/configuration/properties/removeCustomConfig
- label: Remove CustomConfig
- fullwidth: true
- if:
- name: returnFalse
- type: function
+ - type: block-layout
+ label: Config Secret
+ if:
+ name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret
+ type: function
+ elements:
+ - type: label-element
+ label: Config Secret
+ subtitle: Select a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ refresh: true
+ label: Config Secret
+ loader:
+ name: getConfigSecrets
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - temp/properties/createSecret/properties/status
+ init:
+ type: func
+ value: setValueFromDbDetails|/spec/configSecret/name
+ watcher:
+ func: onCreateSecretChange
+ paths:
+ - temp/properties/createSecret/properties/status
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|create
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ - type: block-layout
+ label: Create a New Config Secret
+ showLabels: true
+ if:
+ type: function
+ name: isCreateSecret
+ hasButton:
+ text: Save
+ hasCancel: cancelCreateSecret
+ action: createNewConfigSecret
+ elements:
+ - type: input
+ label: Secret Name
+ schema: temp/properties/createSecret/properties/name
+ validation:
+ type: required
+ - type: array-object-form
+ label: String Data
+ schema: temp/properties/createSecret/properties/data
+ buttonClass: is-light is-outlined
+ validation:
+ type: required
+ elements:
+ - type: select
+ label: Key
+ schema: key
+ loader:
+ name: onSelectedSecretChange
+ watchPaths:
+ - temp/properties/createSecret/properties/data
+ validation:
+ type: required
+ - type: textarea
+ label: Value
+ schema: value
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ if:
+ type: function
+ name: isNotCreateSecret
+ loader:
+ name: onNewConfigSecretChange
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ schema: temp/properties/newConfigSecret
+ - type: block-layout
+ label: ApplyConfig
+ if:
+ name: ifReconfigurationTypeEqualsTo|applyConfig
+ type: function
+ elements:
+ - type: label-element
+ label: New Apply Config
+ subtitle: Define custom configurations for your database using key-value pairs. These parameters will overwrite existing settings.
Enter the parameter you want to configure (e.g., max_connections).
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/selectedConfiguration
+ refresh: true
+ label: Configuration
+ loader: getConfigSecretsforAppyConfig
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|apply
+ watchPaths:
+ - temp/properties/selectedConfiguration
+ - type: multi-file-editor
+ editorHeight: 500px
+ schema: temp/properties/applyConfig
+ loader:
+ name: setApplyConfig
+ watchPaths:
+ - temp/properties/selectedConfiguration
+ watcher:
+ func: onApplyconfigChange
+ paths:
+ - temp/properties/applyConfig
+ - type: block-layout
+ label: Remove
+ if:
+ name: ifReconfigurationTypeEqualsTo|remove
+ type: function
+ elements:
+ - type: label-element
+ label: Remove
+ subtitle: Selected a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/selectedConfigurationRemove
+ refresh: true
+ label: Configuration
+ loader:
+ name: getConfigSecretsforAppyConfig
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|remove
+ watchPaths:
+ - temp/properties/selectedConfigurationRemove
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ init:
+ type: func
+ value: onRemoveConfigChange
+ watcher:
+ func: onRemoveConfigChange
+ paths:
+ - temp/properties/selectedConfigurationRemove
+ schema: temp/properties/removeConfig
# Reconfigure TLS
- type: block-layout
label: TLS
diff --git a/charts/opskubedbcom-mysqlopsrequest-editor/ui/functions.js b/charts/opskubedbcom-mysqlopsrequest-editor/ui/functions.js
index eee03f36e7..3d89d34b4b 100644
--- a/charts/opskubedbcom-mysqlopsrequest-editor/ui/functions.js
+++ b/charts/opskubedbcom-mysqlopsrequest-editor/ui/functions.js
@@ -1,4 +1,4 @@
-const { axios, useOperator, store } = window.vueHelpers || {}
+const { axios, useOperator, store, useToast } = window.vueHelpers || {}
const machines = {
'db.t.micro': {
resources: {
@@ -306,9 +306,11 @@ const machineList = [
]
let machinesFromPreset = []
+const configSecretKeys = ['kubedb-user.cnf']
export const useFunc = (model) => {
const route = store.state?.route
+ const toast = useToast()
const { getValue, storeGet, discriminator, setDiscriminatorValue, commit } = useOperator(
model,
@@ -838,32 +840,419 @@ export const useFunc = (model) => {
return machine === 'custom'
}
- // for config secret
- let secretArray = []
- async function getConfigSecrets() {
+ // Fetch and store database Infos
+ // for secret configurations in reconfigure
+ let configSecrets = []
+ let secretConfigData = []
+
+ async function fetchConfigSecrets() {
const owner = storeGet('/route/params/user')
const cluster = storeGet('/route/params/cluster')
const namespace = getValue(model, '/metadata/namespace')
// watchDependency('model#/metadata/namespace')
- const resp = await axios.get(
- `/clusters/${owner}/${cluster}/proxy/core/v1/namespaces/${namespace}/secrets`,
+ const name = getValue(model, '/spec/databaseRef/name')
+ const dbGroup = getValue(model, '/route/params/group')
+ const dbKind = getValue(store.state, '/resource/definition/result/kind')
+ const dbResource = getValue(model, '/route/params/resource')
+ const dbVersion = getValue(model, '/route/params/version')
+
+ try {
+ const resp = await axios.post(
+ `/clusters/${owner}/${cluster}/proxy/ui.kubedb.com/v1alpha1/databaseinfos`,
+ {
+ apiVersion: 'ui.kubedb.com/v1alpha1',
+ kind: 'DatabaseInfo',
+ request: {
+ source: {
+ ref: {
+ name: name,
+ namespace: namespace,
+ },
+ resource: {
+ group: dbGroup,
+ kind: dbKind,
+ name: dbResource,
+ version: dbVersion,
+ },
+ },
+ keys: ['kubedb-user.cnf'],
+ },
+ },
+ )
+ configSecrets = resp?.data?.response?.availableSecrets || []
+ secretConfigData = resp?.data?.response?.configurations || []
+ } catch (e) {
+ console.log(e)
+ }
+ }
+
+ async function getConfigSecrets(type) {
+ type = type ? type + '/' : ''
+ const secretStatus = getValue(discriminator, `${type}createSecret/status`)
+ if (secretStatus === 'success') {
+ await fetchConfigSecrets()
+ }
+ const mappedSecrets = configSecrets.map((item) => {
+ return { text: item, value: item }
+ })
+ mappedSecrets.push({ text: '+ Create a new Secret', value: 'Create' })
+ return mappedSecrets
+ }
+
+ async function getConfigSecretsforAppyConfig() {
+ const secrets = secretConfigData.map((item) => {
+ return { text: item.componentName, value: item.componentName }
+ })
+ return secrets
+ }
+
+ function getSelectedConfigurationData(type) {
+ type = type ? type + '/' : ''
+ const path = `/${type}selectedConfiguration`
+ const selectedConfiguration = getValue(discriminator, path)
+
+ if (!selectedConfiguration) {
+ return []
+ }
+
+ const configuration = secretConfigData.find(
+ (item) => item.componentName === selectedConfiguration,
)
- const secrets = (resp && resp.data && resp.data.items) || []
- secretArray = secrets
+ if (!configuration) {
+ return []
+ }
- const filteredSecrets = secrets
+ const result = []
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(configuration.data).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedContent = atob(configuration.data[fileName])
+ result.push({
+ name: fileName,
+ content: decodedContent,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ result.push({
+ name: fileName,
+ content: configuration.data[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
- filteredSecrets.map((item) => {
- const name = (item.metadata && item.metadata.name) || ''
- item.text = name
- item.value = name
- return true
+ // Set the value to the model
+ commit('wizard/model$update', {
+ path: `/temp/${type}applyConfig`,
+ value: result,
+ force: true,
})
- return filteredSecrets
+
+ return result
+ }
+
+ function getSelectedConfigurationName(configType, type) {
+ type = type ? type + '/' : ''
+ let path = ''
+ if (configType === 'create') path = `/spec/configuration/${type}/configSecret/name`
+ else if (configType === 'apply') path = `/${type}selectedConfiguration`
+ else if (configType === 'remove') path = `/${type}selectedConfigurationRemove`
+
+ const selectedConfiguration =
+ configType === 'create' ? getValue(model, path) : getValue(discriminator, path)
+
+ if (selectedConfiguration)
+ return { subtitle: ` You have selected ${selectedConfiguration} secret` }
+ else return { subtitle: 'No secret selected' }
+ }
+
+ function getSelectedConfigurationValueForRemove(type) {
+ type = type ? type + '/' : ''
+ const path = `/${type}selectedConfigurationRemove`
+ const selectedConfiguration = getValue(discriminator, path)
+
+ if (!selectedConfiguration) {
+ return ''
+ }
+
+ const configuration = secretConfigData.find(
+ (item) => item.componentName === selectedConfiguration,
+ )
+
+ if (!configuration) {
+ return ''
+ }
+
+ let data = {}
+ // Decode base64 and parse YAML for each key in the secret data
+ Object.keys(configuration.data).forEach((item) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(configuration.data[item])
+ // Parse YAML string to object
+ const parsedYaml = yaml.load(decodedString)
+ // Store the parsed object with the filename as key
+ data[item] = parsedYaml
+ } catch (e) {
+ console.error(`Error parsing ${item}:`, e)
+ data[item] = atob(configuration.data[item]) // Fallback to decoded string
+ }
+ })
+
+ // Convert data object back to YAML string
+ return yaml.dump(data)
+ }
+
+ async function createNewConfigSecret(type) {
+ type = type ? type + '/' : ''
+ const { user, cluster } = route.params
+ const url = `/clusters/${user}/${cluster}/resources`
+ const namespace = storeGet('/route/query/namespace') || getValue(model, '/metadata/namespace')
+ const secretName = getValue(discriminator, `${type}createSecret/name`)
+ const secretData = getValue(discriminator, `${type}createSecret/data`)
+ const secretDataObj = Object.fromEntries(secretData.map((item) => [item.key, item.value]))
+
+ try {
+ const res = await axios.post(url, {
+ apiVersion: 'v1',
+ stringData: secretDataObj,
+ kind: 'Secret',
+ metadata: {
+ name: secretName,
+ namespace: namespace,
+ },
+ type: 'Opaque',
+ })
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'success',
+ })
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/lastCreatedSecret`,
+ value: secretName,
+ })
+ toast.success('Secret created successfully')
+ } catch (error) {
+ const errMsg = decodeError(error, 'Failed to create secret')
+ toast.error(errMsg, { timeout: 5000 })
+ cancelCreateSecret()
+ }
+ }
+
+ function decodeError(msg, defaultMsg) {
+ if (typeof msg === 'string') {
+ return msg || defaultMsg
+ }
+ return (
+ (msg.response && msg.response.data && msg.response.data.message) ||
+ (msg.response && msg.response.data) ||
+ (msg.status && msg.status.status) ||
+ defaultMsg
+ )
+ }
+
+ function isCreateSecret(type) {
+ type = type ? type + '/' : ''
+ const selectedSecret = getValue(model, `spec/configuration/${type}configSecret/name`)
+ const res = selectedSecret === 'Create'
+
+ if (res === true) {
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'pending',
+ })
+ }
+ return res
+ }
+
+ function isNotCreateSecret(type) {
+ return !isCreateSecret(type)
}
+ function onCreateSecretChange(type) {
+ type = type ? type + '/' : ''
+ const secretStatus = getValue(discriminator, `${type}createSecret/status`)
+ if (secretStatus === 'cancelled') return ''
+ else if (secretStatus === 'success') {
+ const name = getValue(discriminator, `${type}createSecret/lastCreatedSecret`)
+
+ const configFound = configSecrets.find((item) => item === name)
+ return configFound ? { text: name, value: name } : ''
+ }
+ }
+
+ function cancelCreateSecret(type) {
+ type = type ? type + '/' : ''
+ commit('wizard/temp$delete', `${type}createSecret/name`)
+ commit('wizard/temp$delete', `${type}createSecret/data`)
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'cancelled',
+ })
+ }
+
+ async function onApplyconfigChange(type) {
+ type = type ? type + '/' : ''
+ const configValue = getValue(discriminator, `${type}applyConfig`)
+
+ if (!configValue) {
+ commit('wizard/model$delete', `/spec/configuration/${type}applyConfig`)
+ return
+ }
+ const tempConfigObj = {}
+ configValue.forEach((item) => {
+ if (item.name) {
+ tempConfigObj[item.name] = item.content
+ }
+ })
+ if (Object.keys(tempConfigObj).length === 0) {
+ commit('wizard/model$delete', `/spec/configuration/${type}applyConfig`)
+ return
+ }
+ commit('wizard/model$update', {
+ path: `/spec/configuration/${type}applyConfig`,
+ value: tempConfigObj,
+ })
+ }
+
+ function setApplyConfig(type) {
+ type = type ? type + '/' : ''
+ const configPath = `/${type}selectedConfiguration`
+ const selectedConfig = getValue(discriminator, configPath)
+ if (!selectedConfig) {
+ return [{ name: '', content: '' }]
+ }
+ const applyconfigData = secretConfigData.find((item) => {
+ if (item.componentName === selectedConfig) {
+ return item
+ }
+ })
+ const { applyConfig } = applyconfigData
+ const configObj = []
+
+ if (applyConfig) {
+ Object.keys(applyConfig).forEach((fileName) => {
+ configObj.push({
+ name: fileName,
+ content: applyConfig[fileName],
+ })
+ })
+ } else {
+ configObj.push({ name: '', content: '' })
+ }
+ return configObj
+ }
+
+ function onRemoveConfigChange(type) {
+ type = type ? type + '/' : ''
+ const configPath = `/${type}selectedConfigurationRemove`
+ const selectedConfig = getValue(discriminator, configPath)
+
+ if (!selectedConfig) {
+ commit('wizard/model$delete', `/spec/configuration/${type}removeCustomConfig`)
+ return [{ name: '', content: '' }]
+ }
+ commit('wizard/model$update', {
+ path: `/spec/configuration/${type}removeCustomConfig`,
+ value: true,
+ })
+
+ const configuration = secretConfigData.find((item) => item.componentName === selectedConfig)
+
+ if (!configuration.data) {
+ return [{ name: '', content: '' }]
+ }
+
+ const configObj = []
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(configuration.data).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(configuration.data[fileName])
+ configObj.push({
+ name: fileName,
+ content: decodedString,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ configObj.push({
+ name: fileName,
+ content: configuration.data[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
+ return configObj
+ }
+
+ async function onNewConfigSecretChange(type) {
+ type = type ? type + '/' : ''
+ const path = `/spec/configuration/${type}configSecret/name`
+ const selectedSecret = getValue(model, path)
+
+ if (!selectedSecret) {
+ commit('wizard/model$delete', `/spec/configuration/${type}configSecret`)
+ return [{ name: '', content: '' }]
+ }
+ if (selectedSecret === 'Create') return [{ name: '', content: '' }]
+
+ const owner = storeGet('/route/params/user')
+ const cluster = storeGet('/route/params/cluster')
+ const namespace = storeGet('/route/query/namespace') || getValue(model, '/metadata/namespace')
+
+ try {
+ // Fetch the secret data from API
+ const secretResp = await axios.get(
+ `/clusters/${owner}/${cluster}/proxy/core/v1/namespaces/${namespace}/secrets/${selectedSecret}`,
+ )
+
+ const secretData = secretResp.data?.data || {}
+ const configObj = []
+
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(secretData).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(secretData[fileName])
+ configObj.push({
+ name: fileName,
+ content: decodedString,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ configObj.push({
+ name: fileName,
+ content: secretData[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
+
+ return configObj
+ } catch (e) {
+ console.error('Error fetching secret:', e)
+ return [{ name: '', content: '' }]
+ }
+ }
+
+ function onSelectedSecretChange(index) {
+ const secretData = getValue(discriminator, 'createSecret/data') || []
+ const selfSecrets = secretData.map((item) => item.key)
+
+ const remainingSecrets = configSecretKeys.filter((item) => !selfSecrets.includes(item))
+
+ const selfKey = getValue(discriminator, `createSecret/data/${index}/key`)
+ if (selfKey) {
+ remainingSecrets.push(selfKey)
+ }
+ const resSecret = remainingSecrets.map((item) => {
+ return { text: item, value: item }
+ })
+ return resSecret
+ }
+
+ let secretArray = []
+
function createSecretUrl() {
const user = storeGet('/route/params/user')
const cluster = storeGet('/route/params/cluster')
@@ -989,25 +1378,6 @@ export const useFunc = (model) => {
return reconfigurationType === value
}
- function onApplyconfigChange() {
- const applyconfig = getValue(discriminator, '/applyConfig')
-
- const configObj = {}
-
- if (applyconfig) {
- applyconfig.forEach((item) => {
- const { key, value } = item
- configObj[key] = value
- })
- }
-
- commit('wizard/model$update', {
- path: '/spec/configuration/applyConfig',
- value: configObj,
- force: true,
- })
- }
-
function onReconfigurationTypeChange() {
const reconfigurationType = getValue(discriminator, '/reconfigurationType')
setDiscriminatorValue('/applyConfig', [])
@@ -1442,5 +1812,20 @@ export const useFunc = (model) => {
checkVolume,
setConfigFiles,
isConfigSelected,
+ fetchConfigSecrets,
+ getConfigSecretsforAppyConfig,
+ getSelectedConfigurationData,
+ getSelectedConfigurationName,
+ getSelectedConfigurationValueForRemove,
+ createNewConfigSecret,
+ decodeError,
+ isCreateSecret,
+ isNotCreateSecret,
+ onCreateSecretChange,
+ cancelCreateSecret,
+ setApplyConfig,
+ onRemoveConfigChange,
+ onNewConfigSecretChange,
+ onSelectedSecretChange,
}
}
diff --git a/charts/opskubedbcom-perconaxtradbopsrequest-editor/ui/create-ui.yaml b/charts/opskubedbcom-perconaxtradbopsrequest-editor/ui/create-ui.yaml
index 35cd01b68e..ba9ee4a19d 100644
--- a/charts/opskubedbcom-perconaxtradbopsrequest-editor/ui/create-ui.yaml
+++ b/charts/opskubedbcom-perconaxtradbopsrequest-editor/ui/create-ui.yaml
@@ -280,106 +280,176 @@ step:
if:
name: ifRequestTypeEqualsTo|Reconfigure
type: function
+ loader:
+ name: fetchConfigSecrets
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
elements:
- - type: radio
- label: Reconfigure Type
- subtitle: Choose the action you'd like to perform. Select a new configuration secret, apply a custom configuration , or remove an existing setup.
- validation:
- type: required
- options:
- - text: New Config Secret
- value: selectNewConfigSecret
- - text: Apply Config
- value: applyConfig
- - text: Remove testing
- value: remove
- schema: temp/properties/reconfigurationType
- watcher:
- func: onReconfigurationTypeChange|perconaxtradb
- paths:
- - temp/properties/reconfigurationType
- type: block-layout
- label: Config Secret
- showLabels: true
- if:
- name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret
- type: function
- elements:
- - type: label-element
- label: Select a configuration secret from the available list to update your database settings
- customClass: mb-10
- - type: select
- addNewButton:
- label: Create Secret
- target: _blank
- url:
- function: createSecretUrl
- schema: schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- validation:
- type: required
- refresh: true
- label: Config Secret
- loader:
- name: getConfigSecrets
- watchPaths:
- - schema/properties/metadata/properties/namespace
- init:
- type: func
- value: setValueFromDbDetails|/spec/configSecret/name
- - type: label-element
- label: Selected Config Secret
- loader: getSelectedConfigSecret|perconaxtradb
- watcher:
- func: getSelectedConfigSecret|perconaxtradb
- paths:
- - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- - type: editor
- label: Value
- readonly: true
- hasCopy: false
- watcher:
- func: getSelectedConfigSecretValue|perconaxtradb
- paths:
- - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- validation:
- type: required
- schema: temp/properties/configArray/items/properties/value
- - type: array-object-form
- label: ApplyConfig
- buttonClass: is-light is-outlined
- schema: temp/properties/perconaxtradb/applyConfig
- validation:
- type: required
- if:
- name: ifReconfigurationTypeEqualsTo|applyConfig
- type: function
- watcher:
- func: onApplyconfigChange|perconaxtradb
- paths:
- - temp/properties/perconaxtradb/applyConfig
+ label: Configuration
elements:
- type: label-element
- label: New Apply Config
- subtitle: Define custom configurations for your database using key-value pairs. These parameters will overwrite existing settings. Enter the parameter you want to configure (e.g., max_connections).
- customClass: mb-10
- - type: input
- label: key
- validation:
- type: required
- schema: key
- - type: editor
- label: value
- hasCopy: false
- validation:
- type: required
- schema: temp/properties/configArray/items/properties/value
- - type: switch
- schema: schema/properties/spec/properties/configuration/properties/removeCustomConfig
- label: Remove CustomConfig
- fullwidth: true
- if:
- name: returnFalse
- type: function
+ label: ''
+ subtitle: Select a new configuration secret, apply a custom configuration, or remove an existing setup to update your database settings
+ - type: tab-layout
+ label: New Config Secret
+ schema: temp/properties/reconfigurationType
+ options:
+ - text: NEW CONFIG SECRET
+ value: selectNewConfigSecret
+ - text: APPLY CONFIG
+ value: applyConfig
+ - text: REMOVE
+ value: remove
+ elements:
+ - type: block-layout
+ label: Config Secret
+ if:
+ name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret
+ type: function
+ elements:
+ - type: label-element
+ label: Config Secret
+ subtitle: Select a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ refresh: true
+ label: Config Secret
+ loader:
+ name: getConfigSecrets
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - temp/properties/createSecret/properties/status
+ init:
+ type: func
+ value: setValueFromDbDetails|/spec/configSecret/name
+ watcher:
+ func: onCreateSecretChange
+ paths:
+ - temp/properties/createSecret/properties/status
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|create
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ - type: block-layout
+ label: Create a New Config Secret
+ showLabels: true
+ if:
+ type: function
+ name: isCreateSecret
+ hasButton:
+ text: Save
+ hasCancel: cancelCreateSecret
+ action: createNewConfigSecret
+ elements:
+ - type: input
+ label: Secret Name
+ schema: temp/properties/createSecret/properties/name
+ validation:
+ type: required
+ - type: array-object-form
+ label: String Data
+ schema: temp/properties/createSecret/properties/data
+ buttonClass: is-light is-outlined
+ validation:
+ type: required
+ elements:
+ - type: select
+ label: Key
+ schema: key
+ loader:
+ name: onSelectedSecretChange
+ watchPaths:
+ - temp/properties/createSecret/properties/data
+ validation:
+ type: required
+ - type: textarea
+ label: Value
+ schema: value
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ if:
+ type: function
+ name: isNotCreateSecret
+ loader:
+ name: onNewConfigSecretChange
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ schema: temp/properties/newConfigSecret
+ - type: block-layout
+ label: ApplyConfig
+ if:
+ name: ifReconfigurationTypeEqualsTo|applyConfig
+ type: function
+ elements:
+ - type: label-element
+ label: New Apply Config
+ subtitle: Define custom configurations for your database using key-value pairs. These parameters will overwrite existing settings.
Enter the parameter you want to configure (e.g., max_connections).
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/selectedConfiguration
+ refresh: true
+ label: Configuration
+ loader: getConfigSecretsforAppyConfig
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|apply
+ watchPaths:
+ - temp/properties/selectedConfiguration
+ - type: multi-file-editor
+ editorHeight: 500px
+ schema: temp/properties/applyConfig
+ loader:
+ name: setApplyConfig
+ watchPaths:
+ - temp/properties/selectedConfiguration
+ watcher:
+ func: onApplyconfigChange
+ paths:
+ - temp/properties/applyConfig
+ - type: block-layout
+ label: Remove
+ if:
+ name: ifReconfigurationTypeEqualsTo|remove
+ type: function
+ elements:
+ - type: label-element
+ label: Remove
+ subtitle: Selected a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/selectedConfigurationRemove
+ refresh: true
+ label: Configuration
+ loader:
+ name: getConfigSecretsforAppyConfig
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|remove
+ watchPaths:
+ - temp/properties/selectedConfigurationRemove
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ init:
+ type: func
+ value: onRemoveConfigChange
+ watcher:
+ func: onRemoveConfigChange
+ paths:
+ - temp/properties/selectedConfigurationRemove
+ schema: temp/properties/removeConfig
# Reconfigure TLS
- type: block-layout
label: TLS
diff --git a/charts/opskubedbcom-perconaxtradbopsrequest-editor/ui/functions.js b/charts/opskubedbcom-perconaxtradbopsrequest-editor/ui/functions.js
index 9f59f7cc6d..b575be8271 100644
--- a/charts/opskubedbcom-perconaxtradbopsrequest-editor/ui/functions.js
+++ b/charts/opskubedbcom-perconaxtradbopsrequest-editor/ui/functions.js
@@ -1,4 +1,4 @@
-const { axios, useOperator, store } = window.vueHelpers || {}
+const { axios, useOperator, store, useToast } = window.vueHelpers || {}
const machines = {
'db.t.micro': {
resources: {
@@ -305,11 +305,13 @@ const machineList = [
'db.r.24xlarge',
]
+const configSecretKeys = ['kubedb-user.cnf']
let machinesFromPreset = []
let secretArray = []
export const useFunc = (model) => {
const route = store.state?.route
+ const toast = useToast()
const { getValue, storeGet, discriminator, setDiscriminatorValue, commit } = useOperator(
model,
@@ -837,29 +839,415 @@ export const useFunc = (model) => {
return machine === 'custom'
}
- // for config secret
- async function getConfigSecrets() {
+ // Fetch and store database Infos
+ // for secret configurations in reconfigure
+ let configSecrets = []
+ let secretConfigData = []
+
+ async function fetchConfigSecrets() {
const owner = storeGet('/route/params/user')
const cluster = storeGet('/route/params/cluster')
const namespace = getValue(model, '/metadata/namespace')
// watchDependency('model#/metadata/namespace')
- const resp = await axios.get(
- `/clusters/${owner}/${cluster}/proxy/core/v1/namespaces/${namespace}/secrets`,
+ const name = getValue(model, '/spec/databaseRef/name')
+ const dbGroup = getValue(model, '/route/params/group')
+ const dbKind = getValue(store.state, '/resource/definition/result/kind')
+ const dbResource = getValue(model, '/route/params/resource')
+ const dbVersion = getValue(model, '/route/params/version')
+
+ try {
+ const resp = await axios.post(
+ `/clusters/${owner}/${cluster}/proxy/ui.kubedb.com/v1alpha1/databaseinfos`,
+ {
+ apiVersion: 'ui.kubedb.com/v1alpha1',
+ kind: 'DatabaseInfo',
+ request: {
+ source: {
+ ref: {
+ name: name,
+ namespace: namespace,
+ },
+ resource: {
+ group: dbGroup,
+ kind: dbKind,
+ name: dbResource,
+ version: dbVersion,
+ },
+ },
+ keys: ['kubedb-user.cnf'],
+ },
+ },
+ )
+ configSecrets = resp?.data?.response?.availableSecrets || []
+ secretConfigData = resp?.data?.response?.configurations || []
+ } catch (e) {
+ console.log(e)
+ }
+ }
+
+ async function getConfigSecrets(type) {
+ type = type ? type + '/' : ''
+ const secretStatus = getValue(discriminator, `${type}createSecret/status`)
+ if (secretStatus === 'success') {
+ await fetchConfigSecrets()
+ }
+ const mappedSecrets = configSecrets.map((item) => {
+ return { text: item, value: item }
+ })
+ mappedSecrets.push({ text: '+ Create a new Secret', value: 'Create' })
+ return mappedSecrets
+ }
+
+ async function getConfigSecretsforAppyConfig() {
+ const secrets = secretConfigData.map((item) => {
+ return { text: item.componentName, value: item.componentName }
+ })
+ return secrets
+ }
+
+ function getSelectedConfigurationData(type) {
+ type = type ? type + '/' : ''
+ const path = `/${type}selectedConfiguration`
+ const selectedConfiguration = getValue(discriminator, path)
+
+ if (!selectedConfiguration) {
+ return []
+ }
+
+ const configuration = secretConfigData.find(
+ (item) => item.componentName === selectedConfiguration,
)
- const secrets = (resp && resp.data && resp.data.items) || []
- secretArray = secrets
+ if (!configuration) {
+ return []
+ }
+
+ const result = []
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(configuration.data).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedContent = atob(configuration.data[fileName])
+ result.push({
+ name: fileName,
+ content: decodedContent,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ result.push({
+ name: fileName,
+ content: configuration.data[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
- const filteredSecrets = secrets
+ // Set the value to the model
+ commit('wizard/model$update', {
+ path: `/temp/${type}applyConfig`,
+ value: result,
+ force: true,
+ })
- filteredSecrets.map((item) => {
- const name = (item.metadata && item.metadata.name) || ''
- item.text = name
- item.value = name
- return true
+ return result
+ }
+
+ function getSelectedConfigurationName(configType, type) {
+ type = type ? type + '/' : ''
+ let path = ''
+ if (configType === 'create') path = `/spec/configuration/${type}/configSecret/name`
+ else if (configType === 'apply') path = `/${type}selectedConfiguration`
+ else if (configType === 'remove') path = `/${type}selectedConfigurationRemove`
+
+ const selectedConfiguration =
+ configType === 'create' ? getValue(model, path) : getValue(discriminator, path)
+
+ if (selectedConfiguration)
+ return { subtitle: ` You have selected ${selectedConfiguration} secret` }
+ else return { subtitle: 'No secret selected' }
+ }
+
+ function getSelectedConfigurationValueForRemove(type) {
+ type = type ? type + '/' : ''
+ const path = `/${type}selectedConfigurationRemove`
+ const selectedConfiguration = getValue(discriminator, path)
+
+ if (!selectedConfiguration) {
+ return ''
+ }
+
+ const configuration = secretConfigData.find(
+ (item) => item.componentName === selectedConfiguration,
+ )
+
+ if (!configuration) {
+ return ''
+ }
+
+ let data = {}
+ // Decode base64 and parse YAML for each key in the secret data
+ Object.keys(configuration.data).forEach((item) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(configuration.data[item])
+ // Parse YAML string to object
+ const parsedYaml = yaml.load(decodedString)
+ // Store the parsed object with the filename as key
+ data[item] = parsedYaml
+ } catch (e) {
+ console.error(`Error parsing ${item}:`, e)
+ data[item] = atob(configuration.data[item]) // Fallback to decoded string
+ }
+ })
+
+ // Convert data object back to YAML string
+ return yaml.dump(data)
+ }
+
+ async function createNewConfigSecret(type) {
+ type = type ? type + '/' : ''
+ const { user, cluster } = route.params
+ const url = `/clusters/${user}/${cluster}/resources`
+ const namespace = storeGet('/route/query/namespace') || getValue(model, '/metadata/namespace')
+ const secretName = getValue(discriminator, `${type}createSecret/name`)
+ const secretData = getValue(discriminator, `${type}createSecret/data`)
+ const secretDataObj = Object.fromEntries(secretData.map((item) => [item.key, item.value]))
+
+ try {
+ const res = await axios.post(url, {
+ apiVersion: 'v1',
+ stringData: secretDataObj,
+ kind: 'Secret',
+ metadata: {
+ name: secretName,
+ namespace: namespace,
+ },
+ type: 'Opaque',
+ })
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'success',
+ })
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/lastCreatedSecret`,
+ value: secretName,
+ })
+ toast.success('Secret created successfully')
+ } catch (error) {
+ const errMsg = decodeError(error, 'Failed to create secret')
+ toast.error(errMsg, { timeout: 5000 })
+ cancelCreateSecret()
+ }
+ }
+
+ function decodeError(msg, defaultMsg) {
+ if (typeof msg === 'string') {
+ return msg || defaultMsg
+ }
+ return (
+ (msg.response && msg.response.data && msg.response.data.message) ||
+ (msg.response && msg.response.data) ||
+ (msg.status && msg.status.status) ||
+ defaultMsg
+ )
+ }
+
+ function isCreateSecret(type) {
+ type = type ? type + '/' : ''
+ const selectedSecret = getValue(model, `spec/configuration/${type}configSecret/name`)
+ const res = selectedSecret === 'Create'
+
+ if (res === true) {
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'pending',
+ })
+ }
+ return res
+ }
+
+ function isNotCreateSecret(type) {
+ return !isCreateSecret(type)
+ }
+
+ function onCreateSecretChange(type) {
+ type = type ? type + '/' : ''
+ const secretStatus = getValue(discriminator, `${type}createSecret/status`)
+ if (secretStatus === 'cancelled') return ''
+ else if (secretStatus === 'success') {
+ const name = getValue(discriminator, `${type}createSecret/lastCreatedSecret`)
+
+ const configFound = configSecrets.find((item) => item === name)
+ return configFound ? { text: name, value: name } : ''
+ }
+ }
+
+ function cancelCreateSecret(type) {
+ type = type ? type + '/' : ''
+ commit('wizard/temp$delete', `${type}createSecret/name`)
+ commit('wizard/temp$delete', `${type}createSecret/data`)
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'cancelled',
+ })
+ }
+
+ async function onApplyconfigChange(type) {
+ type = type ? type + '/' : ''
+ const configValue = getValue(discriminator, `${type}applyConfig`)
+
+ if (!configValue) {
+ commit('wizard/model$delete', `/spec/configuration/${type}applyConfig`)
+ return
+ }
+ const tempConfigObj = {}
+ configValue.forEach((item) => {
+ if (item.name) {
+ tempConfigObj[item.name] = item.content
+ }
+ })
+ if (Object.keys(tempConfigObj).length === 0) {
+ commit('wizard/model$delete', `/spec/configuration/${type}applyConfig`)
+ return
+ }
+ commit('wizard/model$update', {
+ path: `/spec/configuration/${type}applyConfig`,
+ value: tempConfigObj,
+ })
+ }
+
+ function setApplyConfig(type) {
+ type = type ? type + '/' : ''
+ const configPath = `/${type}selectedConfiguration`
+ const selectedConfig = getValue(discriminator, configPath)
+ if (!selectedConfig) {
+ return [{ name: '', content: '' }]
+ }
+ const applyconfigData = secretConfigData.find((item) => {
+ if (item.componentName === selectedConfig) {
+ return item
+ }
})
- return filteredSecrets
+ const { applyConfig } = applyconfigData
+ const configObj = []
+
+ if (applyConfig) {
+ Object.keys(applyConfig).forEach((fileName) => {
+ configObj.push({
+ name: fileName,
+ content: applyConfig[fileName],
+ })
+ })
+ } else {
+ configObj.push({ name: '', content: '' })
+ }
+ return configObj
+ }
+
+ function onRemoveConfigChange(type) {
+ type = type ? type + '/' : ''
+ const configPath = `/${type}selectedConfigurationRemove`
+ const selectedConfig = getValue(discriminator, configPath)
+
+ if (!selectedConfig) {
+ commit('wizard/model$delete', `/spec/configuration/${type}removeCustomConfig`)
+ return [{ name: '', content: '' }]
+ }
+ commit('wizard/model$update', {
+ path: `/spec/configuration/${type}removeCustomConfig`,
+ value: true,
+ })
+
+ const configuration = secretConfigData.find((item) => item.componentName === selectedConfig)
+
+ if (!configuration.data) {
+ return [{ name: '', content: '' }]
+ }
+
+ const configObj = []
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(configuration.data).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(configuration.data[fileName])
+ configObj.push({
+ name: fileName,
+ content: decodedString,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ configObj.push({
+ name: fileName,
+ content: configuration.data[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
+ return configObj
+ }
+
+ async function onNewConfigSecretChange(type) {
+ type = type ? type + '/' : ''
+ const path = `/spec/configuration/${type}configSecret/name`
+ const selectedSecret = getValue(model, path)
+
+ if (!selectedSecret) {
+ commit('wizard/model$delete', `/spec/configuration/${type}configSecret`)
+ return [{ name: '', content: '' }]
+ }
+ if (selectedSecret === 'Create') return [{ name: '', content: '' }]
+
+ const owner = storeGet('/route/params/user')
+ const cluster = storeGet('/route/params/cluster')
+ const namespace = storeGet('/route/query/namespace') || getValue(model, '/metadata/namespace')
+
+ try {
+ // Fetch the secret data from API
+ const secretResp = await axios.get(
+ `/clusters/${owner}/${cluster}/proxy/core/v1/namespaces/${namespace}/secrets/${selectedSecret}`,
+ )
+
+ const secretData = secretResp.data?.data || {}
+ const configObj = []
+
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(secretData).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(secretData[fileName])
+ configObj.push({
+ name: fileName,
+ content: decodedString,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ configObj.push({
+ name: fileName,
+ content: secretData[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
+
+ return configObj
+ } catch (e) {
+ console.error('Error fetching secret:', e)
+ return [{ name: '', content: '' }]
+ }
+ }
+
+ function onSelectedSecretChange(index) {
+ const secretData = getValue(discriminator, 'createSecret/data') || []
+ const selfSecrets = secretData.map((item) => item.key)
+
+ const remainingSecrets = configSecretKeys.filter((item) => !selfSecrets.includes(item))
+
+ const selfKey = getValue(discriminator, `createSecret/data/${index}/key`)
+ if (selfKey) {
+ remainingSecrets.push(selfKey)
+ }
+ const resSecret = remainingSecrets.map((item) => {
+ return { text: item, value: item }
+ })
+ return resSecret
}
function createSecretUrl() {
@@ -983,26 +1371,6 @@ export const useFunc = (model) => {
return reconfigurationType === value
}
- function onApplyconfigChange() {
- const configPath = `/applyConfig`
- const applyconfig = getValue(discriminator, configPath)
-
- const configObj = {}
-
- if (applyconfig) {
- applyconfig.forEach((item) => {
- const { key, value } = item
- configObj[key] = value
- })
- }
-
- commit('wizard/model$update', {
- path: `/spec/configuration/applyConfig`,
- value: configObj,
- force: true,
- })
- }
-
function onReconfigurationTypeChange() {
setDiscriminatorValue(`/applyConfig`, [])
const reconfigurationType = getValue(discriminator, '/reconfigurationType')
@@ -1407,5 +1775,20 @@ export const useFunc = (model) => {
onMachineChange,
isMachineCustom,
checkVolume,
+ fetchConfigSecrets,
+ getConfigSecretsforAppyConfig,
+ getSelectedConfigurationData,
+ getSelectedConfigurationName,
+ getSelectedConfigurationValueForRemove,
+ createNewConfigSecret,
+ decodeError,
+ isCreateSecret,
+ isNotCreateSecret,
+ onCreateSecretChange,
+ cancelCreateSecret,
+ setApplyConfig,
+ onRemoveConfigChange,
+ onNewConfigSecretChange,
+ onSelectedSecretChange,
}
}
diff --git a/charts/opskubedbcom-pgbounceropsrequest-editor/ui/create-ui.yaml b/charts/opskubedbcom-pgbounceropsrequest-editor/ui/create-ui.yaml
index b5817a0bc5..b162b03c98 100644
--- a/charts/opskubedbcom-pgbounceropsrequest-editor/ui/create-ui.yaml
+++ b/charts/opskubedbcom-pgbounceropsrequest-editor/ui/create-ui.yaml
@@ -219,109 +219,176 @@ step:
if:
name: ifRequestTypeEqualsTo|Reconfigure
type: function
+ loader:
+ name: fetchConfigSecrets
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
elements:
- type: block-layout
- label: PgBouncer Configuration
+ label: Configuration
elements:
- - type: radio
- label: Reconfigure Type
- subtitle: Choose the action you'd like to perform. Select a new configuration secret, apply a custom configuration , or remove an existing setup.
+ - type: label-element
+ label: ''
+ subtitle: Select a new configuration secret, apply a custom configuration, or remove an existing setup to update your database settings
+ - type: tab-layout
+ label: New Config Secret
+ schema: temp/properties/reconfigurationType
options:
- - text: Select New Config Secret
+ - text: NEW CONFIG SECRET
value: selectNewConfigSecret
- - text: Apply Config
+ - text: APPLY CONFIG
value: applyConfig
- - text: Remove
+ - text: REMOVE
value: remove
- watcher:
- func: onReconfigurationTypeChange
- paths:
- - temp/properties/reconfigurationType
- validation:
- type: required
- schema: temp/properties/reconfigurationType
- - type: block-layout
- label: PgBouncer config secret
- showLabels: true
- if:
- name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret
- type: function
elements:
- - type: label-element
- label: Select a configuration secret from the available list to update your database settings
- customClass: mb-10
- - type: select
- addNewButton:
- label: Create a new Secret
- target: _blank
- url:
- function: createSecretUrl
+ - type: block-layout
label: Config Secret
- loader:
- name: getConfigSecrets
- watchPaths:
- - schema/properties/metadata/properties/namespace
- init:
- type: func
- value: setValueFromDbDetails|/spec/configuration/pgbouncer/configSecret/name
- refresh: true
- validation:
- type: required
- schema: schema/properties/spec/properties/configuration/properties/pgbouncer/properties/configSecret/properties/name
- - type: label-element
- label: Selected Config Secret
- loader: getSelectedConfigSecret
- watcher:
- func: getSelectedConfigSecret
- paths:
- - schema/properties/spec/properties/configuration/properties/pgbouncer/properties/configSecret/properties/name
- - type: editor
- label: Value
- readonly: true
- hasCopy: false
- watcher:
- func: getSelectedConfigSecretValue
- paths:
- - schema/properties/spec/properties/configuration/properties/pgbouncer/properties/configSecret/properties/name
- validation:
- type: required
- schema: temp/properties/configArray/items/properties/value
- - type: array-object-form
- label: Apply Config
- buttonClass: is-light is-outlined
- if:
- name: ifReconfigurationTypeEqualsTo|applyConfig
- type: function
- validation:
- type: required
- watcher:
- func: onApplyconfigChange
- paths:
- - temp/properties/applyConfig
- schema: temp/properties/applyConfig
- elements:
- - type: label-element
- label: New Apply Config
- subtitle: Define custom configurations for your database using key-value pairs. These parameters will overwrite existing settings. Enter the parameter you want to configure (e.g., max_connections).
- customClass: mb-10
- - type: input
- label: key
- validation:
- type: required
- schema: key
- - type: editor
- hasCopy: false
- label: value
- validation:
- type: required
- schema: value
- - type: switch
- schema: schema/properties/spec/properties/configuration/properties/pgbouncer/properties/removeCustomConfig
- label: Remove CustomConfig
- fullwidth: true
- if:
- name: returnFalse
- type: function
+ if:
+ name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret
+ type: function
+ elements:
+ - type: label-element
+ label: Config Secret
+ subtitle: Select a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ refresh: true
+ label: Config Secret
+ loader:
+ name: getConfigSecrets
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - temp/properties/createSecret/properties/status
+ init:
+ type: func
+ value: setValueFromDbDetails|/spec/configSecret/name
+ watcher:
+ func: onCreateSecretChange
+ paths:
+ - temp/properties/createSecret/properties/status
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|create
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ - type: block-layout
+ label: Create a New Config Secret
+ showLabels: true
+ if:
+ type: function
+ name: isCreateSecret
+ hasButton:
+ text: Save
+ hasCancel: cancelCreateSecret
+ action: createNewConfigSecret
+ elements:
+ - type: input
+ label: Secret Name
+ schema: temp/properties/createSecret/properties/name
+ validation:
+ type: required
+ - type: array-object-form
+ label: String Data
+ schema: temp/properties/createSecret/properties/data
+ buttonClass: is-light is-outlined
+ validation:
+ type: required
+ elements:
+ - type: select
+ label: Key
+ schema: key
+ loader:
+ name: onSelectedSecretChange
+ watchPaths:
+ - temp/properties/createSecret/properties/data
+ validation:
+ type: required
+ - type: textarea
+ label: Value
+ schema: value
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ if:
+ type: function
+ name: isNotCreateSecret
+ loader:
+ name: onNewConfigSecretChange
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ schema: temp/properties/newConfigSecret
+ - type: block-layout
+ label: ApplyConfig
+ if:
+ name: ifReconfigurationTypeEqualsTo|applyConfig
+ type: function
+ elements:
+ - type: label-element
+ label: New Apply Config
+ subtitle: Define custom configurations for your database using key-value pairs. These parameters will overwrite existing settings.
Enter the parameter you want to configure (e.g., max_connections).
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/selectedConfiguration
+ refresh: true
+ label: Configuration
+ loader: getConfigSecretsforAppyConfig
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|apply
+ watchPaths:
+ - temp/properties/selectedConfiguration
+ - type: multi-file-editor
+ editorHeight: 500px
+ schema: temp/properties/applyConfig
+ loader:
+ name: setApplyConfig
+ watchPaths:
+ - temp/properties/selectedConfiguration
+ watcher:
+ func: onApplyconfigChange
+ paths:
+ - temp/properties/applyConfig
+ - type: block-layout
+ label: Remove
+ if:
+ name: ifReconfigurationTypeEqualsTo|remove
+ type: function
+ elements:
+ - type: label-element
+ label: Remove
+ subtitle: Selected a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/selectedConfigurationRemove
+ refresh: true
+ label: Configuration
+ loader:
+ name: getConfigSecretsforAppyConfig
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|remove
+ watchPaths:
+ - temp/properties/selectedConfigurationRemove
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ init:
+ type: func
+ value: onRemoveConfigChange
+ watcher:
+ func: onRemoveConfigChange
+ paths:
+ - temp/properties/selectedConfigurationRemove
+ schema: temp/properties/removeConfig
# common
- type: block-layout
label: OpsRequest Options
diff --git a/charts/opskubedbcom-pgbounceropsrequest-editor/ui/functions.js b/charts/opskubedbcom-pgbounceropsrequest-editor/ui/functions.js
index 0c8a7360e9..0b5c680993 100644
--- a/charts/opskubedbcom-pgbounceropsrequest-editor/ui/functions.js
+++ b/charts/opskubedbcom-pgbounceropsrequest-editor/ui/functions.js
@@ -1,4 +1,4 @@
-const { axios, useOperator, store } = window.vueHelpers || {}
+const { axios, useOperator, store, useToast } = window.vueHelpers || {}
const machines = {
'db.t.micro': {
resources: {
@@ -307,9 +307,11 @@ const machineList = [
let machinesFromPreset = []
let secretArray = []
+const configSecretKeys = ['kubedb-user.ini']
export const useFunc = (model) => {
const route = store.state?.route
+ const toast = useToast()
const { getValue, storeGet, discriminator, setDiscriminatorValue, commit } = useOperator(
model,
@@ -829,29 +831,415 @@ export const useFunc = (model) => {
return machine === 'custom'
}
- // for config secret
- async function getConfigSecrets() {
+ // Fetch and store database Infos
+ // for secret configurations in reconfigure
+ let configSecrets = []
+ let secretConfigData = []
+
+ async function fetchConfigSecrets() {
const owner = storeGet('/route/params/user')
const cluster = storeGet('/route/params/cluster')
const namespace = getValue(model, '/metadata/namespace')
// watchDependency('model#/metadata/namespace')
- const resp = await axios.get(
- `/clusters/${owner}/${cluster}/proxy/core/v1/namespaces/${namespace}/secrets`,
+ const name = getValue(model, '/spec/databaseRef/name')
+ const dbGroup = getValue(model, '/route/params/group')
+ const dbKind = getValue(store.state, '/resource/definition/result/kind')
+ const dbResource = getValue(model, '/route/params/resource')
+ const dbVersion = getValue(model, '/route/params/version')
+
+ try {
+ const resp = await axios.post(
+ `/clusters/${owner}/${cluster}/proxy/ui.kubedb.com/v1alpha1/databaseinfos`,
+ {
+ apiVersion: 'ui.kubedb.com/v1alpha1',
+ kind: 'DatabaseInfo',
+ request: {
+ source: {
+ ref: {
+ name: name,
+ namespace: namespace,
+ },
+ resource: {
+ group: dbGroup,
+ kind: dbKind,
+ name: dbResource,
+ version: dbVersion,
+ },
+ },
+ keys: ['kubedb-user.ini'],
+ },
+ },
+ )
+ configSecrets = resp?.data?.response?.availableSecrets || []
+ secretConfigData = resp?.data?.response?.configurations || []
+ } catch (e) {
+ console.log(e)
+ }
+ }
+
+ async function getConfigSecrets(type) {
+ type = type ? type + '/' : ''
+ const secretStatus = getValue(discriminator, `${type}createSecret/status`)
+ if (secretStatus === 'success') {
+ await fetchConfigSecrets()
+ }
+ const mappedSecrets = configSecrets.map((item) => {
+ return { text: item, value: item }
+ })
+ mappedSecrets.push({ text: '+ Create a new Secret', value: 'Create' })
+ return mappedSecrets
+ }
+
+ async function getConfigSecretsforAppyConfig() {
+ const secrets = secretConfigData.map((item) => {
+ return { text: item.componentName, value: item.componentName }
+ })
+ return secrets
+ }
+
+ function getSelectedConfigurationData(type) {
+ type = type ? type + '/' : ''
+ const path = `/${type}selectedConfiguration`
+ const selectedConfiguration = getValue(discriminator, path)
+
+ if (!selectedConfiguration) {
+ return []
+ }
+
+ const configuration = secretConfigData.find(
+ (item) => item.componentName === selectedConfiguration,
)
- const secrets = (resp && resp.data && resp.data.items) || []
- secretArray = secrets
+ if (!configuration) {
+ return []
+ }
+
+ const result = []
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(configuration.data).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedContent = atob(configuration.data[fileName])
+ result.push({
+ name: fileName,
+ content: decodedContent,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ result.push({
+ name: fileName,
+ content: configuration.data[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
- const filteredSecrets = secrets
+ // Set the value to the model
+ commit('wizard/model$update', {
+ path: `/temp/${type}applyConfig`,
+ value: result,
+ force: true,
+ })
- filteredSecrets.map((item) => {
- const name = (item.metadata && item.metadata.name) || ''
- item.text = name
- item.value = name
- return true
+ return result
+ }
+
+ function getSelectedConfigurationName(configType, type) {
+ type = type ? type + '/' : ''
+ let path = ''
+ if (configType === 'create') path = `/spec/configuration/${type}/configSecret/name`
+ else if (configType === 'apply') path = `/${type}selectedConfiguration`
+ else if (configType === 'remove') path = `/${type}selectedConfigurationRemove`
+
+ const selectedConfiguration =
+ configType === 'create' ? getValue(model, path) : getValue(discriminator, path)
+
+ if (selectedConfiguration)
+ return { subtitle: ` You have selected ${selectedConfiguration} secret` }
+ else return { subtitle: 'No secret selected' }
+ }
+
+ function getSelectedConfigurationValueForRemove(type) {
+ type = type ? type + '/' : ''
+ const path = `/${type}selectedConfigurationRemove`
+ const selectedConfiguration = getValue(discriminator, path)
+
+ if (!selectedConfiguration) {
+ return ''
+ }
+
+ const configuration = secretConfigData.find(
+ (item) => item.componentName === selectedConfiguration,
+ )
+
+ if (!configuration) {
+ return ''
+ }
+
+ let data = {}
+ // Decode base64 and parse YAML for each key in the secret data
+ Object.keys(configuration.data).forEach((item) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(configuration.data[item])
+ // Parse YAML string to object
+ const parsedYaml = yaml.load(decodedString)
+ // Store the parsed object with the filename as key
+ data[item] = parsedYaml
+ } catch (e) {
+ console.error(`Error parsing ${item}:`, e)
+ data[item] = atob(configuration.data[item]) // Fallback to decoded string
+ }
+ })
+
+ // Convert data object back to YAML string
+ return yaml.dump(data)
+ }
+
+ async function createNewConfigSecret(type) {
+ type = type ? type + '/' : ''
+ const { user, cluster } = route.params
+ const url = `/clusters/${user}/${cluster}/resources`
+ const namespace = storeGet('/route/query/namespace') || getValue(model, '/metadata/namespace')
+ const secretName = getValue(discriminator, `${type}createSecret/name`)
+ const secretData = getValue(discriminator, `${type}createSecret/data`)
+ const secretDataObj = Object.fromEntries(secretData.map((item) => [item.key, item.value]))
+
+ try {
+ const res = await axios.post(url, {
+ apiVersion: 'v1',
+ stringData: secretDataObj,
+ kind: 'Secret',
+ metadata: {
+ name: secretName,
+ namespace: namespace,
+ },
+ type: 'Opaque',
+ })
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'success',
+ })
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/lastCreatedSecret`,
+ value: secretName,
+ })
+ toast.success('Secret created successfully')
+ } catch (error) {
+ const errMsg = decodeError(error, 'Failed to create secret')
+ toast.error(errMsg, { timeout: 5000 })
+ cancelCreateSecret()
+ }
+ }
+
+ function decodeError(msg, defaultMsg) {
+ if (typeof msg === 'string') {
+ return msg || defaultMsg
+ }
+ return (
+ (msg.response && msg.response.data && msg.response.data.message) ||
+ (msg.response && msg.response.data) ||
+ (msg.status && msg.status.status) ||
+ defaultMsg
+ )
+ }
+
+ function isCreateSecret(type) {
+ type = type ? type + '/' : ''
+ const selectedSecret = getValue(model, `spec/configuration/${type}configSecret/name`)
+ const res = selectedSecret === 'Create'
+
+ if (res === true) {
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'pending',
+ })
+ }
+ return res
+ }
+
+ function isNotCreateSecret(type) {
+ return !isCreateSecret(type)
+ }
+
+ function onCreateSecretChange(type) {
+ type = type ? type + '/' : ''
+ const secretStatus = getValue(discriminator, `${type}createSecret/status`)
+ if (secretStatus === 'cancelled') return ''
+ else if (secretStatus === 'success') {
+ const name = getValue(discriminator, `${type}createSecret/lastCreatedSecret`)
+
+ const configFound = configSecrets.find((item) => item === name)
+ return configFound ? { text: name, value: name } : ''
+ }
+ }
+
+ function cancelCreateSecret(type) {
+ type = type ? type + '/' : ''
+ commit('wizard/temp$delete', `${type}createSecret/name`)
+ commit('wizard/temp$delete', `${type}createSecret/data`)
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'cancelled',
+ })
+ }
+
+ async function onApplyconfigChange(type) {
+ type = type ? type + '/' : ''
+ const configValue = getValue(discriminator, `${type}applyConfig`)
+
+ if (!configValue) {
+ commit('wizard/model$delete', `/spec/configuration/${type}applyConfig`)
+ return
+ }
+ const tempConfigObj = {}
+ configValue.forEach((item) => {
+ if (item.name) {
+ tempConfigObj[item.name] = item.content
+ }
+ })
+ if (Object.keys(tempConfigObj).length === 0) {
+ commit('wizard/model$delete', `/spec/configuration/${type}applyConfig`)
+ return
+ }
+ commit('wizard/model$update', {
+ path: `/spec/configuration/${type}applyConfig`,
+ value: tempConfigObj,
+ })
+ }
+
+ function setApplyConfig(type) {
+ type = type ? type + '/' : ''
+ const configPath = `/${type}selectedConfiguration`
+ const selectedConfig = getValue(discriminator, configPath)
+ if (!selectedConfig) {
+ return [{ name: '', content: '' }]
+ }
+ const applyconfigData = secretConfigData.find((item) => {
+ if (item.componentName === selectedConfig) {
+ return item
+ }
})
- return filteredSecrets
+ const { applyConfig } = applyconfigData
+ const configObj = []
+
+ if (applyConfig) {
+ Object.keys(applyConfig).forEach((fileName) => {
+ configObj.push({
+ name: fileName,
+ content: applyConfig[fileName],
+ })
+ })
+ } else {
+ configObj.push({ name: '', content: '' })
+ }
+ return configObj
+ }
+
+ function onRemoveConfigChange(type) {
+ type = type ? type + '/' : ''
+ const configPath = `/${type}selectedConfigurationRemove`
+ const selectedConfig = getValue(discriminator, configPath)
+
+ if (!selectedConfig) {
+ commit('wizard/model$delete', `/spec/configuration/${type}removeCustomConfig`)
+ return [{ name: '', content: '' }]
+ }
+ commit('wizard/model$update', {
+ path: `/spec/configuration/${type}removeCustomConfig`,
+ value: true,
+ })
+
+ const configuration = secretConfigData.find((item) => item.componentName === selectedConfig)
+
+ if (!configuration.data) {
+ return [{ name: '', content: '' }]
+ }
+
+ const configObj = []
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(configuration.data).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(configuration.data[fileName])
+ configObj.push({
+ name: fileName,
+ content: decodedString,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ configObj.push({
+ name: fileName,
+ content: configuration.data[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
+ return configObj
+ }
+
+ async function onNewConfigSecretChange(type) {
+ type = type ? type + '/' : ''
+ const path = `/spec/configuration/${type}configSecret/name`
+ const selectedSecret = getValue(model, path)
+
+ if (!selectedSecret) {
+ commit('wizard/model$delete', `/spec/configuration/${type}configSecret`)
+ return [{ name: '', content: '' }]
+ }
+ if (selectedSecret === 'Create') return [{ name: '', content: '' }]
+
+ const owner = storeGet('/route/params/user')
+ const cluster = storeGet('/route/params/cluster')
+ const namespace = storeGet('/route/query/namespace') || getValue(model, '/metadata/namespace')
+
+ try {
+ // Fetch the secret data from API
+ const secretResp = await axios.get(
+ `/clusters/${owner}/${cluster}/proxy/core/v1/namespaces/${namespace}/secrets/${selectedSecret}`,
+ )
+
+ const secretData = secretResp.data?.data || {}
+ const configObj = []
+
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(secretData).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(secretData[fileName])
+ configObj.push({
+ name: fileName,
+ content: decodedString,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ configObj.push({
+ name: fileName,
+ content: secretData[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
+
+ return configObj
+ } catch (e) {
+ console.error('Error fetching secret:', e)
+ return [{ name: '', content: '' }]
+ }
+ }
+
+ function onSelectedSecretChange(index) {
+ const secretData = getValue(discriminator, 'createSecret/data') || []
+ const selfSecrets = secretData.map((item) => item.key)
+
+ const remainingSecrets = configSecretKeys.filter((item) => !selfSecrets.includes(item))
+
+ const selfKey = getValue(discriminator, `createSecret/data/${index}/key`)
+ if (selfKey) {
+ remainingSecrets.push(selfKey)
+ }
+ const resSecret = remainingSecrets.map((item) => {
+ return { text: item, value: item }
+ })
+ return resSecret
}
function createSecretUrl() {
@@ -975,26 +1363,6 @@ export const useFunc = (model) => {
return reconfigurationType === value
}
- function onApplyconfigChange() {
- const configPath = '/applyConfig'
- const applyconfig = getValue(discriminator, configPath)
-
- const configObj = {}
-
- if (applyconfig) {
- applyconfig.forEach((item) => {
- const { key, value } = item
- configObj[key] = value
- })
- }
-
- commit('wizard/model$update', {
- path: `/spec/configuration/pgbouncer/applyConfig`,
- value: configObj,
- force: true,
- })
- }
-
function onReconfigurationTypeChange() {
setDiscriminatorValue('/applyConfig', [])
const reconfigurationType = getValue(discriminator, '/reconfigurationType')
@@ -1350,5 +1718,20 @@ export const useFunc = (model) => {
setMachine,
onMachineChange,
isMachineCustom,
+ fetchConfigSecrets,
+ getConfigSecretsforAppyConfig,
+ getSelectedConfigurationData,
+ getSelectedConfigurationName,
+ getSelectedConfigurationValueForRemove,
+ createNewConfigSecret,
+ decodeError,
+ isCreateSecret,
+ isNotCreateSecret,
+ onCreateSecretChange,
+ cancelCreateSecret,
+ setApplyConfig,
+ onRemoveConfigChange,
+ onNewConfigSecretChange,
+ onSelectedSecretChange,
}
}
diff --git a/charts/opskubedbcom-pgpoolopsrequest-editor/ui/create-ui.yaml b/charts/opskubedbcom-pgpoolopsrequest-editor/ui/create-ui.yaml
index 663aba85af..9caeba2ed6 100644
--- a/charts/opskubedbcom-pgpoolopsrequest-editor/ui/create-ui.yaml
+++ b/charts/opskubedbcom-pgpoolopsrequest-editor/ui/create-ui.yaml
@@ -221,112 +221,176 @@ step:
if:
name: ifRequestTypeEqualsTo|Reconfigure
type: function
+ loader:
+ name: fetchConfigSecrets
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
elements:
- type: block-layout
label: Configuration
elements:
- - type: radio
- label: Reconfigure Type
- subtitle: Choose the action you'd like to perform. Select a new configuration secret, apply a custom configuration , or remove an existing setup.
- validation:
- type: required
+ - type: label-element
+ label: ''
+ subtitle: Select a new configuration secret, apply a custom configuration, or remove an existing setup to update your database settings
+ - type: tab-layout
+ label: New Config Secret
+ schema: temp/properties/reconfigurationType
options:
- - text: New Config Secret
+ - text: NEW CONFIG SECRET
value: selectNewConfigSecret
- - text: Apply Config
+ - text: APPLY CONFIG
value: applyConfig
- - text: Remove
+ - text: REMOVE
value: remove
- schema: temp/properties/reconfigurationType
- watcher:
- func: onReconfigurationTypeChange
- paths:
- - temp/properties/reconfigurationType
- - type: block-layout
- label: Config Secret
- showLabels: true
- if:
- name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret
- type: function
elements:
- - type: label-element
- label: Select a configuration secret from the available list to update your database settings
- customClass: mb-10
- - type: select
- addNewButton:
- label: Create a new Secret
- target: _blank
- url:
- function: createSecretUrl
- schema: schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- validation:
- type: required
- refresh: true
- label: Config Secret
- loader:
- name: getConfigSecrets
- watchPaths:
- - schema/properties/metadata/properties/namespace
- init:
- type: func
- value: setValueFromDbDetails|/spec/configSecret/name
- - type: label-element
- label: Selected Config Secret
- loader: getSelectedConfigSecret
- watcher:
- func: getSelectedConfigSecret
- paths:
- - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- - type: editor
- label: Value
- readonly: true
- hasCopy: false
- if:
- name: isConfigSelected
- type: function
- watcher:
- func: getSelectedConfigSecretValue
- paths:
- - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- validation:
- type: required
- schema: temp/properties/configArray/items/properties/value
- - type: array-object-form
- label: ApplyConfig
- buttonClass: is-light is-outlined
- schema: temp/properties/applyConfig
- validation:
- type: required
- if:
- name: ifReconfigurationTypeEqualsTo|applyConfig
- type: function
- watcher:
- func: onApplyconfigChange
- paths:
- - temp/properties/applyConfig
- elements:
- - type: label-element
- label: New Apply Config
- subtitle: Define custom configurations for your database using key-value pairs. These parameters will overwrite existing settings. Enter the parameter you want to configure (e.g., max_connections).
- customClass: mb-10
- - type: input
- label: key
- validation:
- type: required
- schema: key
- - type: editor
- label: value
- hasCopy: false
- validation:
- type: required
- schema: value
- - type: switch
- schema: schema/properties/spec/properties/configuration/properties/removeCustomConfig
- label: Remove CustomConfig
- fullwidth: true
- if:
- name: returnFalse
- type: function
+ - type: block-layout
+ label: Config Secret
+ if:
+ name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret
+ type: function
+ elements:
+ - type: label-element
+ label: Config Secret
+ subtitle: Select a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ refresh: true
+ label: Config Secret
+ loader:
+ name: getConfigSecrets
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - temp/properties/createSecret/properties/status
+ init:
+ type: func
+ value: setValueFromDbDetails|/spec/configSecret/name
+ watcher:
+ func: onCreateSecretChange
+ paths:
+ - temp/properties/createSecret/properties/status
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|create
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ - type: block-layout
+ label: Create a New Config Secret
+ showLabels: true
+ if:
+ type: function
+ name: isCreateSecret
+ hasButton:
+ text: Save
+ hasCancel: cancelCreateSecret
+ action: createNewConfigSecret
+ elements:
+ - type: input
+ label: Secret Name
+ schema: temp/properties/createSecret/properties/name
+ validation:
+ type: required
+ - type: array-object-form
+ label: String Data
+ schema: temp/properties/createSecret/properties/data
+ buttonClass: is-light is-outlined
+ validation:
+ type: required
+ elements:
+ - type: select
+ label: Key
+ schema: key
+ loader:
+ name: onSelectedSecretChange
+ watchPaths:
+ - temp/properties/createSecret/properties/data
+ validation:
+ type: required
+ - type: textarea
+ label: Value
+ schema: value
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ if:
+ type: function
+ name: isNotCreateSecret
+ loader:
+ name: onNewConfigSecretChange
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ schema: temp/properties/newConfigSecret
+ - type: block-layout
+ label: ApplyConfig
+ if:
+ name: ifReconfigurationTypeEqualsTo|applyConfig
+ type: function
+ elements:
+ - type: label-element
+ label: New Apply Config
+ subtitle: Define custom configurations for your database using key-value pairs. These parameters will overwrite existing settings.
Enter the parameter you want to configure (e.g., max_connections).
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/selectedConfiguration
+ refresh: true
+ label: Configuration
+ loader: getConfigSecretsforAppyConfig
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|apply
+ watchPaths:
+ - temp/properties/selectedConfiguration
+ - type: multi-file-editor
+ editorHeight: 500px
+ schema: temp/properties/applyConfig
+ loader:
+ name: setApplyConfig
+ watchPaths:
+ - temp/properties/selectedConfiguration
+ watcher:
+ func: onApplyconfigChange
+ paths:
+ - temp/properties/applyConfig
+ - type: block-layout
+ label: Remove
+ if:
+ name: ifReconfigurationTypeEqualsTo|remove
+ type: function
+ elements:
+ - type: label-element
+ label: Remove
+ subtitle: Selected a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/selectedConfigurationRemove
+ refresh: true
+ label: Configuration
+ loader:
+ name: getConfigSecretsforAppyConfig
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|remove
+ watchPaths:
+ - temp/properties/selectedConfigurationRemove
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ init:
+ type: func
+ value: onRemoveConfigChange
+ watcher:
+ func: onRemoveConfigChange
+ paths:
+ - temp/properties/selectedConfigurationRemove
+ schema: temp/properties/removeConfig
# Reconfigure TLS
- type: block-layout
label: TLS
diff --git a/charts/opskubedbcom-pgpoolopsrequest-editor/ui/functions.js b/charts/opskubedbcom-pgpoolopsrequest-editor/ui/functions.js
index 378f894b80..94c6f3df62 100644
--- a/charts/opskubedbcom-pgpoolopsrequest-editor/ui/functions.js
+++ b/charts/opskubedbcom-pgpoolopsrequest-editor/ui/functions.js
@@ -1,4 +1,4 @@
-const { axios, useOperator, store } = window.vueHelpers || {}
+const { axios, useOperator, store, useToast } = window.vueHelpers || {}
const machines = {
'db.t.micro': {
resources: {
@@ -305,11 +305,13 @@ const machineList = [
'db.r.24xlarge',
]
+const configSecretKeys = ['*.conf']
let machinesFromPreset = []
let secretArray = []
export const useFunc = (model) => {
const route = store.state?.route
+ const toast = useToast()
const { getValue, storeGet, discriminator, setDiscriminatorValue, commit } = useOperator(
model,
@@ -844,29 +846,415 @@ export const useFunc = (model) => {
return machine === 'custom'
}
- // for config secret
- async function getConfigSecrets() {
+ // Fetch and store database Infos
+ // for secret configurations in reconfigure
+ let configSecrets = []
+ let secretConfigData = []
+
+ async function fetchConfigSecrets() {
const owner = storeGet('/route/params/user')
const cluster = storeGet('/route/params/cluster')
const namespace = getValue(model, '/metadata/namespace')
// watchDependency('model#/metadata/namespace')
- const resp = await axios.get(
- `/clusters/${owner}/${cluster}/proxy/core/v1/namespaces/${namespace}/secrets`,
+ const name = getValue(model, '/spec/databaseRef/name')
+ const dbGroup = getValue(model, '/route/params/group')
+ const dbKind = getValue(store.state, '/resource/definition/result/kind')
+ const dbResource = getValue(model, '/route/params/resource')
+ const dbVersion = getValue(model, '/route/params/version')
+
+ try {
+ const resp = await axios.post(
+ `/clusters/${owner}/${cluster}/proxy/ui.kubedb.com/v1alpha1/databaseinfos`,
+ {
+ apiVersion: 'ui.kubedb.com/v1alpha1',
+ kind: 'DatabaseInfo',
+ request: {
+ source: {
+ ref: {
+ name: name,
+ namespace: namespace,
+ },
+ resource: {
+ group: dbGroup,
+ kind: dbKind,
+ name: dbResource,
+ version: dbVersion,
+ },
+ },
+ keys: ['*.conf'],
+ },
+ },
+ )
+ configSecrets = resp?.data?.response?.availableSecrets || []
+ secretConfigData = resp?.data?.response?.configurations || []
+ } catch (e) {
+ console.log(e)
+ }
+ }
+
+ async function getConfigSecrets(type) {
+ type = type ? type + '/' : ''
+ const secretStatus = getValue(discriminator, `${type}createSecret/status`)
+ if (secretStatus === 'success') {
+ await fetchConfigSecrets()
+ }
+ const mappedSecrets = configSecrets.map((item) => {
+ return { text: item, value: item }
+ })
+ mappedSecrets.push({ text: '+ Create a new Secret', value: 'Create' })
+ return mappedSecrets
+ }
+
+ async function getConfigSecretsforAppyConfig() {
+ const secrets = secretConfigData.map((item) => {
+ return { text: item.componentName, value: item.componentName }
+ })
+ return secrets
+ }
+
+ function getSelectedConfigurationData(type) {
+ type = type ? type + '/' : ''
+ const path = `/${type}selectedConfiguration`
+ const selectedConfiguration = getValue(discriminator, path)
+
+ if (!selectedConfiguration) {
+ return []
+ }
+
+ const configuration = secretConfigData.find(
+ (item) => item.componentName === selectedConfiguration,
)
- const secrets = (resp && resp.data && resp.data.items) || []
- secretArray = secrets
+ if (!configuration) {
+ return []
+ }
- const filteredSecrets = secrets
+ const result = []
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(configuration.data).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedContent = atob(configuration.data[fileName])
+ result.push({
+ name: fileName,
+ content: decodedContent,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ result.push({
+ name: fileName,
+ content: configuration.data[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
- filteredSecrets.map((item) => {
- const name = (item.metadata && item.metadata.name) || ''
- item.text = name
- item.value = name
- return true
+ // Set the value to the model
+ commit('wizard/model$update', {
+ path: `/temp/${type}applyConfig`,
+ value: result,
+ force: true,
})
- return filteredSecrets
+
+ return result
+ }
+
+ function getSelectedConfigurationName(configType, type) {
+ type = type ? type + '/' : ''
+ let path = ''
+ if (configType === 'create') path = `/spec/configuration/${type}/configSecret/name`
+ else if (configType === 'apply') path = `/${type}selectedConfiguration`
+ else if (configType === 'remove') path = `/${type}selectedConfigurationRemove`
+
+ const selectedConfiguration =
+ configType === 'create' ? getValue(model, path) : getValue(discriminator, path)
+
+ if (selectedConfiguration)
+ return { subtitle: ` You have selected ${selectedConfiguration} secret` }
+ else return { subtitle: 'No secret selected' }
+ }
+
+ function getSelectedConfigurationValueForRemove(type) {
+ type = type ? type + '/' : ''
+ const path = `/${type}selectedConfigurationRemove`
+ const selectedConfiguration = getValue(discriminator, path)
+
+ if (!selectedConfiguration) {
+ return ''
+ }
+
+ const configuration = secretConfigData.find(
+ (item) => item.componentName === selectedConfiguration,
+ )
+
+ if (!configuration) {
+ return ''
+ }
+
+ let data = {}
+ // Decode base64 and parse YAML for each key in the secret data
+ Object.keys(configuration.data).forEach((item) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(configuration.data[item])
+ // Parse YAML string to object
+ const parsedYaml = yaml.load(decodedString)
+ // Store the parsed object with the filename as key
+ data[item] = parsedYaml
+ } catch (e) {
+ console.error(`Error parsing ${item}:`, e)
+ data[item] = atob(configuration.data[item]) // Fallback to decoded string
+ }
+ })
+
+ // Convert data object back to YAML string
+ return yaml.dump(data)
+ }
+
+ async function createNewConfigSecret(type) {
+ type = type ? type + '/' : ''
+ const { user, cluster } = route.params
+ const url = `/clusters/${user}/${cluster}/resources`
+ const namespace = storeGet('/route/query/namespace') || getValue(model, '/metadata/namespace')
+ const secretName = getValue(discriminator, `${type}createSecret/name`)
+ const secretData = getValue(discriminator, `${type}createSecret/data`)
+ const secretDataObj = Object.fromEntries(secretData.map((item) => [item.key, item.value]))
+
+ try {
+ const res = await axios.post(url, {
+ apiVersion: 'v1',
+ stringData: secretDataObj,
+ kind: 'Secret',
+ metadata: {
+ name: secretName,
+ namespace: namespace,
+ },
+ type: 'Opaque',
+ })
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'success',
+ })
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/lastCreatedSecret`,
+ value: secretName,
+ })
+ toast.success('Secret created successfully')
+ } catch (error) {
+ const errMsg = decodeError(error, 'Failed to create secret')
+ toast.error(errMsg, { timeout: 5000 })
+ cancelCreateSecret()
+ }
+ }
+
+ function decodeError(msg, defaultMsg) {
+ if (typeof msg === 'string') {
+ return msg || defaultMsg
+ }
+ return (
+ (msg.response && msg.response.data && msg.response.data.message) ||
+ (msg.response && msg.response.data) ||
+ (msg.status && msg.status.status) ||
+ defaultMsg
+ )
+ }
+
+ function isCreateSecret(type) {
+ type = type ? type + '/' : ''
+ const selectedSecret = getValue(model, `spec/configuration/${type}configSecret/name`)
+ const res = selectedSecret === 'Create'
+
+ if (res === true) {
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'pending',
+ })
+ }
+ return res
+ }
+
+ function isNotCreateSecret(type) {
+ return !isCreateSecret(type)
+ }
+
+ function onCreateSecretChange(type) {
+ type = type ? type + '/' : ''
+ const secretStatus = getValue(discriminator, `${type}createSecret/status`)
+ if (secretStatus === 'cancelled') return ''
+ else if (secretStatus === 'success') {
+ const name = getValue(discriminator, `${type}createSecret/lastCreatedSecret`)
+
+ const configFound = configSecrets.find((item) => item === name)
+ return configFound ? { text: name, value: name } : ''
+ }
+ }
+
+ function cancelCreateSecret(type) {
+ type = type ? type + '/' : ''
+ commit('wizard/temp$delete', `${type}createSecret/name`)
+ commit('wizard/temp$delete', `${type}createSecret/data`)
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'cancelled',
+ })
+ }
+
+ async function onApplyconfigChange(type) {
+ type = type ? type + '/' : ''
+ const configValue = getValue(discriminator, `${type}applyConfig`)
+
+ if (!configValue) {
+ commit('wizard/model$delete', `/spec/configuration/${type}applyConfig`)
+ return
+ }
+ const tempConfigObj = {}
+ configValue.forEach((item) => {
+ if (item.name) {
+ tempConfigObj[item.name] = item.content
+ }
+ })
+ if (Object.keys(tempConfigObj).length === 0) {
+ commit('wizard/model$delete', `/spec/configuration/${type}applyConfig`)
+ return
+ }
+ commit('wizard/model$update', {
+ path: `/spec/configuration/${type}applyConfig`,
+ value: tempConfigObj,
+ })
+ }
+
+ function setApplyConfig(type) {
+ type = type ? type + '/' : ''
+ const configPath = `/${type}selectedConfiguration`
+ const selectedConfig = getValue(discriminator, configPath)
+ if (!selectedConfig) {
+ return [{ name: '', content: '' }]
+ }
+ const applyconfigData = secretConfigData.find((item) => {
+ if (item.componentName === selectedConfig) {
+ return item
+ }
+ })
+ const { applyConfig } = applyconfigData
+ const configObj = []
+
+ if (applyConfig) {
+ Object.keys(applyConfig).forEach((fileName) => {
+ configObj.push({
+ name: fileName,
+ content: applyConfig[fileName],
+ })
+ })
+ } else {
+ configObj.push({ name: '', content: '' })
+ }
+ return configObj
+ }
+
+ function onRemoveConfigChange(type) {
+ type = type ? type + '/' : ''
+ const configPath = `/${type}selectedConfigurationRemove`
+ const selectedConfig = getValue(discriminator, configPath)
+
+ if (!selectedConfig) {
+ commit('wizard/model$delete', `/spec/configuration/${type}removeCustomConfig`)
+ return [{ name: '', content: '' }]
+ }
+ commit('wizard/model$update', {
+ path: `/spec/configuration/${type}removeCustomConfig`,
+ value: true,
+ })
+
+ const configuration = secretConfigData.find((item) => item.componentName === selectedConfig)
+
+ if (!configuration.data) {
+ return [{ name: '', content: '' }]
+ }
+
+ const configObj = []
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(configuration.data).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(configuration.data[fileName])
+ configObj.push({
+ name: fileName,
+ content: decodedString,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ configObj.push({
+ name: fileName,
+ content: configuration.data[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
+ return configObj
+ }
+
+ async function onNewConfigSecretChange(type) {
+ type = type ? type + '/' : ''
+ const path = `/spec/configuration/${type}configSecret/name`
+ const selectedSecret = getValue(model, path)
+
+ if (!selectedSecret) {
+ commit('wizard/model$delete', `/spec/configuration/${type}configSecret`)
+ return [{ name: '', content: '' }]
+ }
+ if (selectedSecret === 'Create') return [{ name: '', content: '' }]
+
+ const owner = storeGet('/route/params/user')
+ const cluster = storeGet('/route/params/cluster')
+ const namespace = storeGet('/route/query/namespace') || getValue(model, '/metadata/namespace')
+
+ try {
+ // Fetch the secret data from API
+ const secretResp = await axios.get(
+ `/clusters/${owner}/${cluster}/proxy/core/v1/namespaces/${namespace}/secrets/${selectedSecret}`,
+ )
+
+ const secretData = secretResp.data?.data || {}
+ const configObj = []
+
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(secretData).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(secretData[fileName])
+ configObj.push({
+ name: fileName,
+ content: decodedString,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ configObj.push({
+ name: fileName,
+ content: secretData[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
+
+ return configObj
+ } catch (e) {
+ console.error('Error fetching secret:', e)
+ return [{ name: '', content: '' }]
+ }
+ }
+
+ function onSelectedSecretChange(index) {
+ const secretData = getValue(discriminator, 'createSecret/data') || []
+ const selfSecrets = secretData.map((item) => item.key)
+
+ const remainingSecrets = configSecretKeys.filter((item) => !selfSecrets.includes(item))
+
+ const selfKey = getValue(discriminator, `createSecret/data/${index}/key`)
+ if (selfKey) {
+ remainingSecrets.push(selfKey)
+ }
+ const resSecret = remainingSecrets.map((item) => {
+ return { text: item, value: item }
+ })
+ return resSecret
}
function isConfigSelected() {
@@ -1050,24 +1438,6 @@ export const useFunc = (model) => {
return reconfigurationType === value
}
- function onApplyconfigChange() {
- const applyconfig = getValue(discriminator, '/applyConfig')
-
- const configObj = {}
- if (applyconfig) {
- applyconfig.forEach((item) => {
- const { key, value } = item
- configObj[key] = value
- })
- }
-
- commit('wizard/model$update', {
- path: '/spec/configuration/applyConfig',
- value: configObj,
- force: true,
- })
- }
-
function onReconfigurationTypeChange() {
const reconfigurationType = getValue(discriminator, '/reconfigurationType')
setDiscriminatorValue('/applyConfig', [])
@@ -1382,5 +1752,20 @@ export const useFunc = (model) => {
onMachineChange,
isMachineCustom,
objectToYaml,
+ fetchConfigSecrets,
+ getConfigSecretsforAppyConfig,
+ getSelectedConfigurationData,
+ getSelectedConfigurationName,
+ getSelectedConfigurationValueForRemove,
+ createNewConfigSecret,
+ decodeError,
+ isCreateSecret,
+ isNotCreateSecret,
+ onCreateSecretChange,
+ cancelCreateSecret,
+ setApplyConfig,
+ onRemoveConfigChange,
+ onNewConfigSecretChange,
+ onSelectedSecretChange,
}
}
diff --git a/charts/opskubedbcom-postgresopsrequest-editor/ui/create-ui.yaml b/charts/opskubedbcom-postgresopsrequest-editor/ui/create-ui.yaml
index d97b7ee8ac..33776d0d0a 100644
--- a/charts/opskubedbcom-postgresopsrequest-editor/ui/create-ui.yaml
+++ b/charts/opskubedbcom-postgresopsrequest-editor/ui/create-ui.yaml
@@ -269,111 +269,177 @@ step:
if:
name: ifRequestTypeEqualsTo|Reconfigure
type: function
+ loader:
+ name: fetchConfigSecrets
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
elements:
- type: block-layout
label: Configuration
- showLabels: false
elements:
- - type: radio
- label: Reconfigure Type
+ - type: label-element
+ label: ''
+ subtitle: Select a new configuration secret, apply a custom configuration, or remove an existing setup to update your database settings
+ - type: tab-layout
+ label: New Config Secret
+ schema: temp/properties/reconfigurationType
options:
- - text: Select New Config Secret
+ - text: NEW CONFIG SECRET
value: selectNewConfigSecret
- - text: Apply Config
+ - text: APPLY CONFIG
value: applyConfig
- - text: Remove
+ - text: REMOVE
value: remove
- watcher:
- func: onReconfigurationTypeChange
- paths:
- - temp/properties/reconfigurationType
- validation:
- type: required
- schema: temp/properties/reconfigurationType
- - type: block-layout
- label: Config Secret
- showLabels: true
- if:
- name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret
- type: function
elements:
- - type: label-element
- label: Select a configuration secret from the available list to update your database settings
- customClass: mb-10
- - type: select
- addNewButton:
- label: Create a new Secret
- target: _blank
- url:
- function: createSecretUrl
- schema: schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- validation:
- type: required
- refresh: true
- label: Config Secret
- loader:
- name: getConfigSecrets
- watchPaths:
- - schema/properties/metadata/properties/namespace
- init:
- type: func
- value: setValueFromDbDetails|/spec/configSecret/name
- - type: label-element
- label: Selected Config Secret
- loader: getSelectedConfigSecret
- if:
- name: isConfigSelected
- type: function
- watcher:
- func: getSelectedConfigSecret
- paths:
- - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- - type: editor
- label: Value
- readonly: true
- hasCopy: false
- if:
- name: isConfigSelected
- type: function
- loader:
- name: getSelectedConfigSecretValue
- watchPaths:
- - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- validation:
- type: required
- schema: temp/properties/configArray/items/properties/value
- - type: array-object-form
- label: Apply Config
- buttonClass: is-light is-outlined
- if:
- name: ifReconfigurationTypeEqualsTo|applyConfig
- type: function
- validation:
- type: required
- watcher:
- func: onApplyconfigChange
- paths:
- - temp/properties/applyConfig
- schema: temp/properties/applyConfig
- elements:
- - type: input
- label: key
- validation:
- type: required
- schema: key
- - type: editor
- hasCopy: false
- label: value
- validation:
- type: required
- schema: value
- - type: switch
- schema: schema/properties/spec/properties/configuration/properties/removeCustomConfig
- label: Remove CustomConfig
- fullwidth: true
- if:
- name: returnFalse
- type: function
+ - type: block-layout
+ label: Config Secret
+ if:
+ name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret
+ type: function
+ elements:
+ - type: label-element
+ label: Config Secret
+ subtitle: Select a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ refresh: true
+ label: Config Secret
+ loader:
+ name: getConfigSecrets
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - temp/properties/createSecret/properties/status
+ init:
+ type: func
+ value: setValueFromDbDetails|/spec/configSecret/name
+ watcher:
+ func: onCreateSecretChange
+ paths:
+ - temp/properties/createSecret/properties/status
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|create
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ - type: block-layout
+ label: Create a New Config Secret
+ showLabels: true
+ if:
+ type: function
+ name: isCreateSecret
+ hasButton:
+ text: Save
+ hasCancel: cancelCreateSecret
+ action: createNewConfigSecret
+ elements:
+ - type: input
+ label: Secret Name
+ schema: temp/properties/createSecret/properties/name
+ validation:
+ type: required
+ - type: array-object-form
+ label: String Data
+ schema: temp/properties/createSecret/properties/data
+ buttonClass: is-light is-outlined
+ validation:
+ type: required
+ elements:
+ - type: select
+ label: Key
+ schema: key
+ loader:
+ name: onSelectedSecretChange
+ watchPaths:
+ - temp/properties/createSecret/properties/data
+ validation:
+ type: required
+ - type: textarea
+ label: Value
+ schema: value
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ if:
+ type: function
+ name: isNotCreateSecret
+ loader:
+ name: onNewConfigSecretChange
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ schema: temp/properties/newConfigSecret
+ - type: block-layout
+ label: ApplyConfig
+ if:
+ name: ifReconfigurationTypeEqualsTo|applyConfig
+ type: function
+ elements:
+ - type: label-element
+ label: New Apply Config
+ subtitle: Define custom configurations for your database using key-value pairs. These parameters will overwrite existing settings.
Enter the parameter you want to configure (e.g., max_connections).
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/selectedConfiguration
+ refresh: true
+ label: Configuration
+ loader: getConfigSecretsforAppyConfig
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|apply
+ watchPaths:
+ - temp/properties/selectedConfiguration
+ - type: multi-file-editor
+ editorHeight: 500px
+ schema: temp/properties/applyConfig
+ loader:
+ name: setApplyConfig
+ watchPaths:
+ - temp/properties/selectedConfiguration
+ watcher:
+ func: onApplyconfigChange
+ paths:
+ - temp/properties/applyConfig
+ - type: block-layout
+ label: Remove
+ if:
+ name: ifReconfigurationTypeEqualsTo|remove
+ type: function
+ elements:
+ - type: label-element
+ label: Remove
+ subtitle: Selected a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/selectedConfigurationRemove
+ refresh: true
+ label: Configuration
+ loader:
+ name: getConfigSecretsforAppyConfig
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|remove
+ watchPaths:
+ - temp/properties/selectedConfigurationRemove
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ init:
+ type: func
+ value: onRemoveConfigChange
+ watcher:
+ func: onRemoveConfigChange
+ paths:
+ - temp/properties/selectedConfigurationRemove
+ schema: temp/properties/removeConfig
+
# Reconfigure TLS
- type: block-layout
label: TLS
diff --git a/charts/opskubedbcom-postgresopsrequest-editor/ui/functions.js b/charts/opskubedbcom-postgresopsrequest-editor/ui/functions.js
index f8a1d8f938..709b82118b 100644
--- a/charts/opskubedbcom-postgresopsrequest-editor/ui/functions.js
+++ b/charts/opskubedbcom-postgresopsrequest-editor/ui/functions.js
@@ -1,4 +1,4 @@
-const { axios, useOperator, store } = window.vueHelpers || {}
+const { axios, useOperator, store, useToast } = window.vueHelpers || {}
const machines = {
'db.t.micro': {
resources: {
@@ -304,11 +304,12 @@ const machineList = [
'db.r.16xlarge',
'db.r.24xlarge',
]
-
+const configSecretKeys = ['user.conf']
let machinesFromPreset = []
export const useFunc = (model) => {
const route = store.state?.route
+ const toast = useToast()
const { getValue, storeGet, discriminator, setDiscriminatorValue, commit } = useOperator(
model,
@@ -834,32 +835,419 @@ export const useFunc = (model) => {
return machine === 'custom'
}
- // for config secret
- let secretArray = []
- async function getConfigSecrets() {
+ // Fetch and store database Infos
+ // for secret configurations in reconfigure
+ let configSecrets = []
+ let secretConfigData = []
+
+ async function fetchConfigSecrets() {
const owner = storeGet('/route/params/user')
const cluster = storeGet('/route/params/cluster')
const namespace = getValue(model, '/metadata/namespace')
// watchDependency('model#/metadata/namespace')
- const resp = await axios.get(
- `/clusters/${owner}/${cluster}/proxy/core/v1/namespaces/${namespace}/secrets`,
+ const name = getValue(model, '/spec/databaseRef/name')
+ const dbGroup = getValue(model, '/route/params/group')
+ const dbKind = getValue(store.state, '/resource/definition/result/kind')
+ const dbResource = getValue(model, '/route/params/resource')
+ const dbVersion = getValue(model, '/route/params/version')
+
+ try {
+ const resp = await axios.post(
+ `/clusters/${owner}/${cluster}/proxy/ui.kubedb.com/v1alpha1/databaseinfos`,
+ {
+ apiVersion: 'ui.kubedb.com/v1alpha1',
+ kind: 'DatabaseInfo',
+ request: {
+ source: {
+ ref: {
+ name: name,
+ namespace: namespace,
+ },
+ resource: {
+ group: dbGroup,
+ kind: dbKind,
+ name: dbResource,
+ version: dbVersion,
+ },
+ },
+ keys: ['user.conf'],
+ },
+ },
+ )
+ configSecrets = resp?.data?.response?.availableSecrets || []
+ secretConfigData = resp?.data?.response?.configurations || []
+ } catch (e) {
+ console.log(e)
+ }
+ }
+
+ async function getConfigSecrets(type) {
+ type = type ? type + '/' : ''
+ const secretStatus = getValue(discriminator, `${type}createSecret/status`)
+ if (secretStatus === 'success') {
+ await fetchConfigSecrets()
+ }
+ const mappedSecrets = configSecrets.map((item) => {
+ return { text: item, value: item }
+ })
+ mappedSecrets.push({ text: '+ Create a new Secret', value: 'Create' })
+ return mappedSecrets
+ }
+
+ async function getConfigSecretsforAppyConfig() {
+ const secrets = secretConfigData.map((item) => {
+ return { text: item.componentName, value: item.componentName }
+ })
+ return secrets
+ }
+
+ function getSelectedConfigurationData(type) {
+ type = type ? type + '/' : ''
+ const path = `/${type}selectedConfiguration`
+ const selectedConfiguration = getValue(discriminator, path)
+
+ if (!selectedConfiguration) {
+ return []
+ }
+
+ const configuration = secretConfigData.find(
+ (item) => item.componentName === selectedConfiguration,
)
- const secrets = (resp && resp.data && resp.data.items) || []
- secretArray = secrets
+ if (!configuration) {
+ return []
+ }
- const filteredSecrets = secrets
+ const result = []
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(configuration.data).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedContent = atob(configuration.data[fileName])
+ result.push({
+ name: fileName,
+ content: decodedContent,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ result.push({
+ name: fileName,
+ content: configuration.data[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
- filteredSecrets.map((item) => {
- const name = (item.metadata && item.metadata.name) || ''
- item.text = name
- item.value = name
- return true
+ // Set the value to the model
+ commit('wizard/model$update', {
+ path: `/temp/${type}applyConfig`,
+ value: result,
+ force: true,
+ })
+
+ return result
+ }
+
+ function getSelectedConfigurationName(configType, type) {
+ type = type ? type + '/' : ''
+ let path = ''
+ if (configType === 'create') path = `/spec/configuration/${type}/configSecret/name`
+ else if (configType === 'apply') path = `/${type}selectedConfiguration`
+ else if (configType === 'remove') path = `/${type}selectedConfigurationRemove`
+
+ const selectedConfiguration =
+ configType === 'create' ? getValue(model, path) : getValue(discriminator, path)
+
+ if (selectedConfiguration)
+ return { subtitle: ` You have selected ${selectedConfiguration} secret` }
+ else return { subtitle: 'No secret selected' }
+ }
+
+ function getSelectedConfigurationValueForRemove(type) {
+ type = type ? type + '/' : ''
+ const path = `/${type}selectedConfigurationRemove`
+ const selectedConfiguration = getValue(discriminator, path)
+
+ if (!selectedConfiguration) {
+ return ''
+ }
+
+ const configuration = secretConfigData.find(
+ (item) => item.componentName === selectedConfiguration,
+ )
+
+ if (!configuration) {
+ return ''
+ }
+
+ let data = {}
+ // Decode base64 and parse YAML for each key in the secret data
+ Object.keys(configuration.data).forEach((item) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(configuration.data[item])
+ // Parse YAML string to object
+ const parsedYaml = yaml.load(decodedString)
+ // Store the parsed object with the filename as key
+ data[item] = parsedYaml
+ } catch (e) {
+ console.error(`Error parsing ${item}:`, e)
+ data[item] = atob(configuration.data[item]) // Fallback to decoded string
+ }
+ })
+
+ // Convert data object back to YAML string
+ return yaml.dump(data)
+ }
+
+ async function createNewConfigSecret(type) {
+ type = type ? type + '/' : ''
+ const { user, cluster } = route.params
+ const url = `/clusters/${user}/${cluster}/resources`
+ const namespace = storeGet('/route/query/namespace') || getValue(model, '/metadata/namespace')
+ const secretName = getValue(discriminator, `${type}createSecret/name`)
+ const secretData = getValue(discriminator, `${type}createSecret/data`)
+ const secretDataObj = Object.fromEntries(secretData.map((item) => [item.key, item.value]))
+
+ try {
+ const res = await axios.post(url, {
+ apiVersion: 'v1',
+ stringData: secretDataObj,
+ kind: 'Secret',
+ metadata: {
+ name: secretName,
+ namespace: namespace,
+ },
+ type: 'Opaque',
+ })
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'success',
+ })
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/lastCreatedSecret`,
+ value: secretName,
+ })
+ toast.success('Secret created successfully')
+ } catch (error) {
+ const errMsg = decodeError(error, 'Failed to create secret')
+ toast.error(errMsg, { timeout: 5000 })
+ cancelCreateSecret()
+ }
+ }
+
+ function decodeError(msg, defaultMsg) {
+ if (typeof msg === 'string') {
+ return msg || defaultMsg
+ }
+ return (
+ (msg.response && msg.response.data && msg.response.data.message) ||
+ (msg.response && msg.response.data) ||
+ (msg.status && msg.status.status) ||
+ defaultMsg
+ )
+ }
+
+ function isCreateSecret(type) {
+ type = type ? type + '/' : ''
+ const selectedSecret = getValue(model, `spec/configuration/${type}configSecret/name`)
+ const res = selectedSecret === 'Create'
+
+ if (res === true) {
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'pending',
+ })
+ }
+ return res
+ }
+
+ function isNotCreateSecret(type) {
+ return !isCreateSecret(type)
+ }
+
+ function onCreateSecretChange(type) {
+ type = type ? type + '/' : ''
+ const secretStatus = getValue(discriminator, `${type}createSecret/status`)
+ if (secretStatus === 'cancelled') return ''
+ else if (secretStatus === 'success') {
+ const name = getValue(discriminator, `${type}createSecret/lastCreatedSecret`)
+
+ const configFound = configSecrets.find((item) => item === name)
+ return configFound ? { text: name, value: name } : ''
+ }
+ }
+
+ function cancelCreateSecret(type) {
+ type = type ? type + '/' : ''
+ commit('wizard/temp$delete', `${type}createSecret/name`)
+ commit('wizard/temp$delete', `${type}createSecret/data`)
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'cancelled',
+ })
+ }
+
+ async function onApplyconfigChange(type) {
+ type = type ? type + '/' : ''
+ const configValue = getValue(discriminator, `${type}applyConfig`)
+
+ if (!configValue) {
+ commit('wizard/model$delete', `/spec/configuration/${type}applyConfig`)
+ return
+ }
+ const tempConfigObj = {}
+ configValue.forEach((item) => {
+ if (item.name) {
+ tempConfigObj[item.name] = item.content
+ }
+ })
+ if (Object.keys(tempConfigObj).length === 0) {
+ commit('wizard/model$delete', `/spec/configuration/${type}applyConfig`)
+ return
+ }
+ commit('wizard/model$update', {
+ path: `/spec/configuration/${type}applyConfig`,
+ value: tempConfigObj,
+ })
+ }
+
+ function setApplyConfig(type) {
+ type = type ? type + '/' : ''
+ const configPath = `/${type}selectedConfiguration`
+ const selectedConfig = getValue(discriminator, configPath)
+ if (!selectedConfig) {
+ return [{ name: '', content: '' }]
+ }
+ const applyconfigData = secretConfigData.find((item) => {
+ if (item.componentName === selectedConfig) {
+ return item
+ }
+ })
+ const { applyConfig } = applyconfigData
+ const configObj = []
+
+ if (applyConfig) {
+ Object.keys(applyConfig).forEach((fileName) => {
+ configObj.push({
+ name: fileName,
+ content: applyConfig[fileName],
+ })
+ })
+ } else {
+ configObj.push({ name: '', content: '' })
+ }
+ return configObj
+ }
+
+ function onRemoveConfigChange(type) {
+ type = type ? type + '/' : ''
+ const configPath = `/${type}selectedConfigurationRemove`
+ const selectedConfig = getValue(discriminator, configPath)
+
+ if (!selectedConfig) {
+ commit('wizard/model$delete', `/spec/configuration/${type}removeCustomConfig`)
+ return [{ name: '', content: '' }]
+ }
+ commit('wizard/model$update', {
+ path: `/spec/configuration/${type}removeCustomConfig`,
+ value: true,
})
- return filteredSecrets
+
+ const configuration = secretConfigData.find((item) => item.componentName === selectedConfig)
+
+ if (!configuration.data) {
+ return [{ name: '', content: '' }]
+ }
+
+ const configObj = []
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(configuration.data).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(configuration.data[fileName])
+ configObj.push({
+ name: fileName,
+ content: decodedString,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ configObj.push({
+ name: fileName,
+ content: configuration.data[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
+ return configObj
}
+ async function onNewConfigSecretChange(type) {
+ type = type ? type + '/' : ''
+ const path = `/spec/configuration/${type}configSecret/name`
+ const selectedSecret = getValue(model, path)
+
+ if (!selectedSecret) {
+ commit('wizard/model$delete', `/spec/configuration/${type}configSecret`)
+ return [{ name: '', content: '' }]
+ }
+ if (selectedSecret === 'Create') return [{ name: '', content: '' }]
+
+ const owner = storeGet('/route/params/user')
+ const cluster = storeGet('/route/params/cluster')
+ const namespace = storeGet('/route/query/namespace') || getValue(model, '/metadata/namespace')
+
+ try {
+ // Fetch the secret data from API
+ const secretResp = await axios.get(
+ `/clusters/${owner}/${cluster}/proxy/core/v1/namespaces/${namespace}/secrets/${selectedSecret}`,
+ )
+
+ const secretData = secretResp.data?.data || {}
+ const configObj = []
+
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(secretData).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(secretData[fileName])
+ configObj.push({
+ name: fileName,
+ content: decodedString,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ configObj.push({
+ name: fileName,
+ content: secretData[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
+
+ return configObj
+ } catch (e) {
+ console.error('Error fetching secret:', e)
+ return [{ name: '', content: '' }]
+ }
+ }
+
+ function onSelectedSecretChange(index) {
+ const secretData = getValue(discriminator, 'createSecret/data') || []
+ const selfSecrets = secretData.map((item) => item.key)
+
+ const remainingSecrets = configSecretKeys.filter((item) => !selfSecrets.includes(item))
+
+ const selfKey = getValue(discriminator, `createSecret/data/${index}/key`)
+ if (selfKey) {
+ remainingSecrets.push(selfKey)
+ }
+ const resSecret = remainingSecrets.map((item) => {
+ return { text: item, value: item }
+ })
+ return resSecret
+ }
+
+ let secretArray = []
+
function createSecretUrl() {
const user = storeGet('/route/params/user')
const cluster = storeGet('/route/params/cluster')
@@ -985,25 +1373,6 @@ export const useFunc = (model) => {
return reconfigurationType === value
}
- function onApplyconfigChange() {
- const applyconfig = getValue(discriminator, '/applyConfig')
-
- const configObj = {}
-
- if (applyconfig) {
- applyconfig.forEach((item) => {
- const { key, value } = item
- configObj[key] = value
- })
- }
-
- commit('wizard/model$update', {
- path: '/spec/configuration/applyConfig',
- value: configObj,
- force: true,
- })
- }
-
function onReconfigurationTypeChange() {
const reconfigurationType = getValue(discriminator, '/reconfigurationType')
setDiscriminatorValue('/applyConfig', [])
@@ -1316,13 +1685,6 @@ export const useFunc = (model) => {
return !!(model && model.alias)
}
- function getSelectedConfigSecret() {
- const path = `/spec/configuration/configSecret/name`
- const selectedSecret = getValue(model, path)
- // watchDependency(`model#${path}`)
- return `You have selected ${selectedSecret} secret` || 'No secret selected'
- }
-
function objectToYaml(obj, indent = 0) {
if (obj === null || obj === undefined) return 'null'
if (typeof obj !== 'object') return JSON.stringify(obj)
@@ -1500,7 +1862,21 @@ export const useFunc = (model) => {
onMachineChange,
isMachineCustom,
checkVolume,
- getSelectedConfigSecret,
getSelectedConfigSecretValue,
+ fetchConfigSecrets,
+ getConfigSecretsforAppyConfig,
+ getSelectedConfigurationData,
+ getSelectedConfigurationName,
+ getSelectedConfigurationValueForRemove,
+ createNewConfigSecret,
+ decodeError,
+ isCreateSecret,
+ isNotCreateSecret,
+ onCreateSecretChange,
+ cancelCreateSecret,
+ setApplyConfig,
+ onRemoveConfigChange,
+ onNewConfigSecretChange,
+ onSelectedSecretChange,
}
}
diff --git a/charts/opskubedbcom-rabbitmqopsrequest-editor/ui/create-ui.yaml b/charts/opskubedbcom-rabbitmqopsrequest-editor/ui/create-ui.yaml
index 656b3d080b..9a36e8c736 100644
--- a/charts/opskubedbcom-rabbitmqopsrequest-editor/ui/create-ui.yaml
+++ b/charts/opskubedbcom-rabbitmqopsrequest-editor/ui/create-ui.yaml
@@ -273,114 +273,176 @@ step:
if:
name: ifRequestTypeEqualsTo|Reconfigure
type: function
+ loader:
+ name: fetchConfigSecrets
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
elements:
- type: block-layout
label: Configuration
- showLabels: false
elements:
- - type: radio
- label: Reconfigure Type
+ - type: label-element
+ label: ''
+ subtitle: Select a new configuration secret, apply a custom configuration, or remove an existing setup to update your database settings
+ - type: tab-layout
+ label: New Config Secret
+ schema: temp/properties/reconfigurationType
options:
- - text: New Config Secret
+ - text: NEW CONFIG SECRET
value: selectNewConfigSecret
- - text: Apply Config
+ - text: APPLY CONFIG
value: applyConfig
- - text: Remove
+ - text: REMOVE
value: remove
- watcher:
- func: onReconfigurationTypeChange
- paths:
- - temp/properties/reconfigurationType
- validation:
- type: required
- schema: temp/properties/reconfigurationType
- - type: block-layout
- label: Config Secret
- showLabels: true
- if:
- name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret
- type: function
- elements:
- - type: label-element
- label: Select a configuration secret from the available list to update your database settings
- customClass: mb-10
- - type: select
- addNewButton:
- label: Create a new Secret
- target: _blank
- url:
- function: createSecretUrl
- label: Config Secret
- if:
- name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret
- type: function
- loader:
- name: getConfigSecrets
- watchPaths:
- - schema/properties/metadata/properties/namespace
- init:
- type: func
- value: setValueFromDbDetails|/spec/configSecret/name
- refresh: true
- validation:
- type: required
- schema: schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- - type: label-element
- label: Selected Config Secret
- loader: getSelectedConfigSecret
- if:
- name: isConfigSelected
- type: function
- watcher:
- func: getSelectedConfigSecret
- paths:
- - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- - type: editor
- label: Value
- readonly: true
- hasCopy: false
- if:
- name: isConfigSelected
- type: function
- loader:
- name: getSelectedConfigSecretValue
- watchPaths:
- - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- validation:
- type: required
- schema: temp/properties/configArray/items/properties/value
- - type: array-object-form
- label: Apply Config
- buttonClass: is-light is-outlined
- if:
- name: ifReconfigurationTypeEqualsTo|applyConfig
- type: function
- validation:
- type: required
- watcher:
- func: onApplyconfigChange
- paths:
- - temp/properties/applyConfig
- schema: temp/properties/applyConfig
elements:
- - type: input
- label: key
- validation:
- type: required
- schema: key
- - type: editor
- hasCopy: false
- label: value
- validation:
- type: required
- schema: value
- - type: switch
- schema: schema/properties/spec/properties/configuration/properties/removeCustomConfig
- label: Remove CustomConfig
- fullwidth: true
- if:
- name: returnFalse
- type: function
+ - type: block-layout
+ label: Config Secret
+ if:
+ name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret
+ type: function
+ elements:
+ - type: label-element
+ label: Config Secret
+ subtitle: Select a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ refresh: true
+ label: Config Secret
+ loader:
+ name: getConfigSecrets
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - temp/properties/createSecret/properties/status
+ init:
+ type: func
+ value: setValueFromDbDetails|/spec/configSecret/name
+ watcher:
+ func: onCreateSecretChange
+ paths:
+ - temp/properties/createSecret/properties/status
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|create
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ - type: block-layout
+ label: Create a New Config Secret
+ showLabels: true
+ if:
+ type: function
+ name: isCreateSecret
+ hasButton:
+ text: Save
+ hasCancel: cancelCreateSecret
+ action: createNewConfigSecret
+ elements:
+ - type: input
+ label: Secret Name
+ schema: temp/properties/createSecret/properties/name
+ validation:
+ type: required
+ - type: array-object-form
+ label: String Data
+ schema: temp/properties/createSecret/properties/data
+ buttonClass: is-light is-outlined
+ validation:
+ type: required
+ elements:
+ - type: select
+ label: Key
+ schema: key
+ loader:
+ name: onSelectedSecretChange
+ watchPaths:
+ - temp/properties/createSecret/properties/data
+ validation:
+ type: required
+ - type: textarea
+ label: Value
+ schema: value
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ if:
+ type: function
+ name: isNotCreateSecret
+ loader:
+ name: onNewConfigSecretChange
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ schema: temp/properties/newConfigSecret
+ - type: block-layout
+ label: ApplyConfig
+ if:
+ name: ifReconfigurationTypeEqualsTo|applyConfig
+ type: function
+ elements:
+ - type: label-element
+ label: New Apply Config
+ subtitle: Define custom configurations for your database using key-value pairs. These parameters will overwrite existing settings.
Enter the parameter you want to configure (e.g., max_connections).
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/selectedConfiguration
+ refresh: true
+ label: Configuration
+ loader: getConfigSecretsforAppyConfig
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|apply
+ watchPaths:
+ - temp/properties/selectedConfiguration
+ - type: multi-file-editor
+ editorHeight: 500px
+ schema: temp/properties/applyConfig
+ loader:
+ name: setApplyConfig
+ watchPaths:
+ - temp/properties/selectedConfiguration
+ watcher:
+ func: onApplyconfigChange
+ paths:
+ - temp/properties/applyConfig
+ - type: block-layout
+ label: Remove
+ if:
+ name: ifReconfigurationTypeEqualsTo|remove
+ type: function
+ elements:
+ - type: label-element
+ label: Remove
+ subtitle: Selected a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/selectedConfigurationRemove
+ refresh: true
+ label: Configuration
+ loader:
+ name: getConfigSecretsforAppyConfig
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|remove
+ watchPaths:
+ - temp/properties/selectedConfigurationRemove
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ init:
+ type: func
+ value: onRemoveConfigChange
+ watcher:
+ func: onRemoveConfigChange
+ paths:
+ - temp/properties/selectedConfigurationRemove
+ schema: temp/properties/removeConfig
# Reconfigure TLS
- type: block-layout
label: TLS
diff --git a/charts/opskubedbcom-rabbitmqopsrequest-editor/ui/functions.js b/charts/opskubedbcom-rabbitmqopsrequest-editor/ui/functions.js
index 890c16c645..badf868eea 100644
--- a/charts/opskubedbcom-rabbitmqopsrequest-editor/ui/functions.js
+++ b/charts/opskubedbcom-rabbitmqopsrequest-editor/ui/functions.js
@@ -1,4 +1,4 @@
-const { axios, useOperator, store } = window.vueHelpers || {}
+const { axios, useOperator, store, useToast } = window.vueHelpers || {}
const machines = {
'db.t.micro': {
resources: {
@@ -305,10 +305,12 @@ const machineList = [
'db.r.24xlarge',
]
+const configSecretKeys = ['rabbitmq.conf']
let machinesFromPreset = []
export const useFunc = (model) => {
const route = store.state?.route
+ const toast = useToast()
const { getValue, storeGet, discriminator, setDiscriminatorValue, commit } = useOperator(
model,
@@ -821,31 +823,419 @@ export const useFunc = (model) => {
return machine === 'custom'
}
- let secretArray = []
- async function getConfigSecrets() {
+ // Fetch and store database Infos
+ // for secret configurations in reconfigure
+ let configSecrets = []
+ let secretConfigData = []
+
+ async function fetchConfigSecrets() {
const owner = storeGet('/route/params/user')
const cluster = storeGet('/route/params/cluster')
const namespace = getValue(model, '/metadata/namespace')
// watchDependency('model#/metadata/namespace')
- const resp = await axios.get(
- `/clusters/${owner}/${cluster}/proxy/core/v1/namespaces/${namespace}/secrets`,
+ const name = getValue(model, '/spec/databaseRef/name')
+ const dbGroup = getValue(model, '/route/params/group')
+ const dbKind = getValue(store.state, '/resource/definition/result/kind')
+ const dbResource = getValue(model, '/route/params/resource')
+ const dbVersion = getValue(model, '/route/params/version')
+
+ try {
+ const resp = await axios.post(
+ `/clusters/${owner}/${cluster}/proxy/ui.kubedb.com/v1alpha1/databaseinfos`,
+ {
+ apiVersion: 'ui.kubedb.com/v1alpha1',
+ kind: 'DatabaseInfo',
+ request: {
+ source: {
+ ref: {
+ name: name,
+ namespace: namespace,
+ },
+ resource: {
+ group: dbGroup,
+ kind: dbKind,
+ name: dbResource,
+ version: dbVersion,
+ },
+ },
+ keys: ['rabbitmq.conf'],
+ },
+ },
+ )
+ configSecrets = resp?.data?.response?.availableSecrets || []
+ secretConfigData = resp?.data?.response?.configurations || []
+ } catch (e) {
+ console.log(e)
+ }
+ }
+
+ async function getConfigSecrets(type) {
+ type = type ? type + '/' : ''
+ const secretStatus = getValue(discriminator, `${type}createSecret/status`)
+ if (secretStatus === 'success') {
+ await fetchConfigSecrets()
+ }
+ const mappedSecrets = configSecrets.map((item) => {
+ return { text: item, value: item }
+ })
+ mappedSecrets.push({ text: '+ Create a new Secret', value: 'Create' })
+ return mappedSecrets
+ }
+
+ async function getConfigSecretsforAppyConfig() {
+ const secrets = secretConfigData.map((item) => {
+ return { text: item.componentName, value: item.componentName }
+ })
+ return secrets
+ }
+
+ function getSelectedConfigurationData(type) {
+ type = type ? type + '/' : ''
+ const path = `/${type}selectedConfiguration`
+ const selectedConfiguration = getValue(discriminator, path)
+
+ if (!selectedConfiguration) {
+ return []
+ }
+
+ const configuration = secretConfigData.find(
+ (item) => item.componentName === selectedConfiguration,
)
- const secrets = (resp && resp.data && resp.data.items) || []
+ if (!configuration) {
+ return []
+ }
- const filteredSecrets = secrets
+ const result = []
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(configuration.data).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedContent = atob(configuration.data[fileName])
+ result.push({
+ name: fileName,
+ content: decodedContent,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ result.push({
+ name: fileName,
+ content: configuration.data[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
- filteredSecrets.map((item) => {
- const name = (item.metadata && item.metadata.name) || ''
- item.text = name
- item.value = name
- return true
+ // Set the value to the model
+ commit('wizard/model$update', {
+ path: `/temp/${type}applyConfig`,
+ value: result,
+ force: true,
+ })
+
+ return result
+ }
+
+ function getSelectedConfigurationName(configType, type) {
+ type = type ? type + '/' : ''
+ let path = ''
+ if (configType === 'create') path = `/spec/configuration/${type}/configSecret/name`
+ else if (configType === 'apply') path = `/${type}selectedConfiguration`
+ else if (configType === 'remove') path = `/${type}selectedConfigurationRemove`
+
+ const selectedConfiguration =
+ configType === 'create' ? getValue(model, path) : getValue(discriminator, path)
+
+ if (selectedConfiguration)
+ return { subtitle: ` You have selected ${selectedConfiguration} secret` }
+ else return { subtitle: 'No secret selected' }
+ }
+
+ function getSelectedConfigurationValueForRemove(type) {
+ type = type ? type + '/' : ''
+ const path = `/${type}selectedConfigurationRemove`
+ const selectedConfiguration = getValue(discriminator, path)
+
+ if (!selectedConfiguration) {
+ return ''
+ }
+
+ const configuration = secretConfigData.find(
+ (item) => item.componentName === selectedConfiguration,
+ )
+
+ if (!configuration) {
+ return ''
+ }
+
+ let data = {}
+ // Decode base64 and parse YAML for each key in the secret data
+ Object.keys(configuration.data).forEach((item) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(configuration.data[item])
+ // Parse YAML string to object
+ const parsedYaml = yaml.load(decodedString)
+ // Store the parsed object with the filename as key
+ data[item] = parsedYaml
+ } catch (e) {
+ console.error(`Error parsing ${item}:`, e)
+ data[item] = atob(configuration.data[item]) // Fallback to decoded string
+ }
})
- secretArray = secrets
- return filteredSecrets
+
+ // Convert data object back to YAML string
+ return yaml.dump(data)
}
+ async function createNewConfigSecret(type) {
+ type = type ? type + '/' : ''
+ const { user, cluster } = route.params
+ const url = `/clusters/${user}/${cluster}/resources`
+ const namespace = storeGet('/route/query/namespace') || getValue(model, '/metadata/namespace')
+ const secretName = getValue(discriminator, `${type}createSecret/name`)
+ const secretData = getValue(discriminator, `${type}createSecret/data`)
+ const secretDataObj = Object.fromEntries(secretData.map((item) => [item.key, item.value]))
+
+ try {
+ const res = await axios.post(url, {
+ apiVersion: 'v1',
+ stringData: secretDataObj,
+ kind: 'Secret',
+ metadata: {
+ name: secretName,
+ namespace: namespace,
+ },
+ type: 'Opaque',
+ })
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'success',
+ })
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/lastCreatedSecret`,
+ value: secretName,
+ })
+ toast.success('Secret created successfully')
+ } catch (error) {
+ const errMsg = decodeError(error, 'Failed to create secret')
+ toast.error(errMsg, { timeout: 5000 })
+ cancelCreateSecret()
+ }
+ }
+
+ function decodeError(msg, defaultMsg) {
+ if (typeof msg === 'string') {
+ return msg || defaultMsg
+ }
+ return (
+ (msg.response && msg.response.data && msg.response.data.message) ||
+ (msg.response && msg.response.data) ||
+ (msg.status && msg.status.status) ||
+ defaultMsg
+ )
+ }
+
+ function isCreateSecret(type) {
+ type = type ? type + '/' : ''
+ const selectedSecret = getValue(model, `spec/configuration/${type}configSecret/name`)
+ const res = selectedSecret === 'Create'
+
+ if (res === true) {
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'pending',
+ })
+ }
+ return res
+ }
+
+ function isNotCreateSecret(type) {
+ return !isCreateSecret(type)
+ }
+
+ function onCreateSecretChange(type) {
+ type = type ? type + '/' : ''
+ const secretStatus = getValue(discriminator, `${type}createSecret/status`)
+ if (secretStatus === 'cancelled') return ''
+ else if (secretStatus === 'success') {
+ const name = getValue(discriminator, `${type}createSecret/lastCreatedSecret`)
+
+ const configFound = configSecrets.find((item) => item === name)
+ return configFound ? { text: name, value: name } : ''
+ }
+ }
+
+ function cancelCreateSecret(type) {
+ type = type ? type + '/' : ''
+ commit('wizard/temp$delete', `${type}createSecret/name`)
+ commit('wizard/temp$delete', `${type}createSecret/data`)
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'cancelled',
+ })
+ }
+
+ async function onApplyconfigChange(type) {
+ type = type ? type + '/' : ''
+ const configValue = getValue(discriminator, `${type}applyConfig`)
+
+ if (!configValue) {
+ commit('wizard/model$delete', `/spec/configuration/${type}applyConfig`)
+ return
+ }
+ const tempConfigObj = {}
+ configValue.forEach((item) => {
+ if (item.name) {
+ tempConfigObj[item.name] = item.content
+ }
+ })
+ if (Object.keys(tempConfigObj).length === 0) {
+ commit('wizard/model$delete', `/spec/configuration/${type}applyConfig`)
+ return
+ }
+ commit('wizard/model$update', {
+ path: `/spec/configuration/${type}applyConfig`,
+ value: tempConfigObj,
+ })
+ }
+
+ function setApplyConfig(type) {
+ type = type ? type + '/' : ''
+ const configPath = `/${type}selectedConfiguration`
+ const selectedConfig = getValue(discriminator, configPath)
+ if (!selectedConfig) {
+ return [{ name: '', content: '' }]
+ }
+ const applyconfigData = secretConfigData.find((item) => {
+ if (item.componentName === selectedConfig) {
+ return item
+ }
+ })
+ const { applyConfig } = applyconfigData
+ const configObj = []
+
+ if (applyConfig) {
+ Object.keys(applyConfig).forEach((fileName) => {
+ configObj.push({
+ name: fileName,
+ content: applyConfig[fileName],
+ })
+ })
+ } else {
+ configObj.push({ name: '', content: '' })
+ }
+ return configObj
+ }
+
+ function onRemoveConfigChange(type) {
+ type = type ? type + '/' : ''
+ const configPath = `/${type}selectedConfigurationRemove`
+ const selectedConfig = getValue(discriminator, configPath)
+
+ if (!selectedConfig) {
+ commit('wizard/model$delete', `/spec/configuration/${type}removeCustomConfig`)
+ return [{ name: '', content: '' }]
+ }
+ commit('wizard/model$update', {
+ path: `/spec/configuration/${type}removeCustomConfig`,
+ value: true,
+ })
+
+ const configuration = secretConfigData.find((item) => item.componentName === selectedConfig)
+
+ if (!configuration.data) {
+ return [{ name: '', content: '' }]
+ }
+
+ const configObj = []
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(configuration.data).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(configuration.data[fileName])
+ configObj.push({
+ name: fileName,
+ content: decodedString,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ configObj.push({
+ name: fileName,
+ content: configuration.data[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
+ return configObj
+ }
+
+ async function onNewConfigSecretChange(type) {
+ type = type ? type + '/' : ''
+ const path = `/spec/configuration/${type}configSecret/name`
+ const selectedSecret = getValue(model, path)
+
+ if (!selectedSecret) {
+ commit('wizard/model$delete', `/spec/configuration/${type}configSecret`)
+ return [{ name: '', content: '' }]
+ }
+ if (selectedSecret === 'Create') return [{ name: '', content: '' }]
+
+ const owner = storeGet('/route/params/user')
+ const cluster = storeGet('/route/params/cluster')
+ const namespace = storeGet('/route/query/namespace') || getValue(model, '/metadata/namespace')
+
+ try {
+ // Fetch the secret data from API
+ const secretResp = await axios.get(
+ `/clusters/${owner}/${cluster}/proxy/core/v1/namespaces/${namespace}/secrets/${selectedSecret}`,
+ )
+
+ const secretData = secretResp.data?.data || {}
+ const configObj = []
+
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(secretData).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(secretData[fileName])
+ configObj.push({
+ name: fileName,
+ content: decodedString,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ configObj.push({
+ name: fileName,
+ content: secretData[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
+
+ return configObj
+ } catch (e) {
+ console.error('Error fetching secret:', e)
+ return [{ name: '', content: '' }]
+ }
+ }
+
+ function onSelectedSecretChange(index) {
+ const secretData = getValue(discriminator, 'createSecret/data') || []
+ const selfSecrets = secretData.map((item) => item.key)
+
+ const remainingSecrets = configSecretKeys.filter((item) => !selfSecrets.includes(item))
+
+ const selfKey = getValue(discriminator, `createSecret/data/${index}/key`)
+ if (selfKey) {
+ remainingSecrets.push(selfKey)
+ }
+ const resSecret = remainingSecrets.map((item) => {
+ return { text: item, value: item }
+ })
+ return resSecret
+ }
+
+ let secretArray = []
+
function createSecretUrl() {
const user = storeGet('/route/params/user')
const cluster = storeGet('/route/params/cluster')
@@ -968,24 +1358,6 @@ export const useFunc = (model) => {
return reconfigurationType === value
}
- function onApplyconfigChange() {
- const applyconfig = getValue(discriminator, '/applyConfig')
-
- const configObj = {}
- if (applyconfig) {
- applyconfig.forEach((item) => {
- const { key, value } = item
- configObj[key] = value
- })
- }
-
- commit('wizard/model$update', {
- path: '/spec/configuration/applyConfig',
- value: configObj,
- force: true,
- })
- }
-
function onReconfigurationTypeChange() {
const reconfigurationType = getValue(discriminator, '/reconfigurationType')
setDiscriminatorValue('/applyConfig', [])
@@ -1409,5 +1781,20 @@ export const useFunc = (model) => {
isConfigSelected,
getSelectedConfigSecret,
getSelectedConfigSecretValue,
+ fetchConfigSecrets,
+ getConfigSecretsforAppyConfig,
+ getSelectedConfigurationData,
+ getSelectedConfigurationName,
+ getSelectedConfigurationValueForRemove,
+ createNewConfigSecret,
+ decodeError,
+ isCreateSecret,
+ isNotCreateSecret,
+ onCreateSecretChange,
+ cancelCreateSecret,
+ setApplyConfig,
+ onRemoveConfigChange,
+ onNewConfigSecretChange,
+ onSelectedSecretChange,
}
}
diff --git a/charts/opskubedbcom-redisopsrequest-editor/ui/create-ui.yaml b/charts/opskubedbcom-redisopsrequest-editor/ui/create-ui.yaml
index 2d2c0d732f..9a0c7aadd6 100644
--- a/charts/opskubedbcom-redisopsrequest-editor/ui/create-ui.yaml
+++ b/charts/opskubedbcom-redisopsrequest-editor/ui/create-ui.yaml
@@ -276,115 +276,176 @@ step:
if:
name: ifRequestTypeEqualsTo|Reconfigure
type: function
+ loader:
+ name: fetchConfigSecrets
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
elements:
- type: block-layout
label: Configuration
- showLabels: false
elements:
- - type: radio
- label: Reconfigure Type
+ - type: label-element
+ label: ''
+ subtitle: Select a new configuration secret, apply a custom configuration, or remove an existing setup to update your database settings
+ - type: tab-layout
+ label: New Config Secret
+ schema: temp/properties/reconfigurationType
options:
- - text: New Config Secret
+ - text: NEW CONFIG SECRET
value: selectNewConfigSecret
- - text: Apply Config
+ - text: APPLY CONFIG
value: applyConfig
- - text: Remove
+ - text: REMOVE
value: remove
- watcher:
- func: onReconfigurationTypeChange
- paths:
- - temp/properties/reconfigurationType
- validation:
- type: required
- schema: temp/properties/reconfigurationType
- - type: block-layout
- label: Config Secret
- showLabels: true
- if:
- name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret
- type: function
- elements:
- - type: label-element
- label: Select a configuration secret from the available list to update your database settings
- customClass: mb-10
- - type: select
- addNewButton:
- label: Create a new Secret
- target: _blank
- url:
- function: createSecretUrl
- schema: schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- validation:
- type: required
- refresh: true
- label: Config Secret
- loader:
- name: getConfigSecrets
- watchPaths:
- - schema/properties/metadata/properties/namespace
- init:
- type: func
- value: setValueFromDbDetails|/spec/configSecret/name
- - type: label-element
- label: Selected Config Secret
- loader: getSelectedConfigSecret
- if:
- name: isConfigSelected
- type: function
- watcher:
- func: getSelectedConfigSecret
- paths:
- - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- - type: editor
- label: Value
- readonly: true
- hasCopy: false
- if:
- name: isConfigSelected
- type: function
- loader:
- name: getSelectedConfigSecretValue
- watchPaths:
- - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- validation:
- type: required
- schema: temp/properties/configArray/items/properties/value
- - type: array-object-form
- label: ApplyConfig
- buttonClass: is-light is-outlined
- schema: temp/properties/applyConfig
- validation:
- type: required
- if:
- name: ifReconfigurationTypeEqualsTo|applyConfig
- type: function
- watcher:
- func: onApplyconfigChange
- paths:
- - temp/properties/applyConfig
elements:
- - type: label-element
- label: New Apply Config
- subtitle: Define custom configurations for your database using key-value pairs. Each entry allows you to specify configuration parameters.
- customClass: mb-10
- - type: input
- label: key
- validation:
- type: required
- schema: key
- - type: editor
- label: value
- hasCopy: false
- validation:
- type: required
- schema: value
- - type: switch
- schema: schema/properties/spec/properties/configuration/properties/removeCustomConfig
- label: Remove CustomConfig
- fullwidth: true
- if:
- name: returnFalse
- type: function
+ - type: block-layout
+ label: Config Secret
+ if:
+ name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret
+ type: function
+ elements:
+ - type: label-element
+ label: Config Secret
+ subtitle: Select a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ refresh: true
+ label: Config Secret
+ loader:
+ name: getConfigSecrets
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - temp/properties/createSecret/properties/status
+ init:
+ type: func
+ value: setValueFromDbDetails|/spec/configSecret/name
+ watcher:
+ func: onCreateSecretChange
+ paths:
+ - temp/properties/createSecret/properties/status
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|create
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ - type: block-layout
+ label: Create a New Config Secret
+ showLabels: true
+ if:
+ type: function
+ name: isCreateSecret
+ hasButton:
+ text: Save
+ hasCancel: cancelCreateSecret
+ action: createNewConfigSecret
+ elements:
+ - type: input
+ label: Secret Name
+ schema: temp/properties/createSecret/properties/name
+ validation:
+ type: required
+ - type: array-object-form
+ label: String Data
+ schema: temp/properties/createSecret/properties/data
+ buttonClass: is-light is-outlined
+ validation:
+ type: required
+ elements:
+ - type: select
+ label: Key
+ schema: key
+ loader:
+ name: onSelectedSecretChange
+ watchPaths:
+ - temp/properties/createSecret/properties/data
+ validation:
+ type: required
+ - type: textarea
+ label: Value
+ schema: value
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ if:
+ type: function
+ name: isNotCreateSecret
+ loader:
+ name: onNewConfigSecretChange
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ schema: temp/properties/newConfigSecret
+ - type: block-layout
+ label: ApplyConfig
+ if:
+ name: ifReconfigurationTypeEqualsTo|applyConfig
+ type: function
+ elements:
+ - type: label-element
+ label: New Apply Config
+ subtitle: Define custom configurations for your database using key-value pairs. These parameters will overwrite existing settings.
Enter the parameter you want to configure (e.g., max_connections).
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/selectedConfiguration
+ refresh: true
+ label: Configuration
+ loader: getConfigSecretsforAppyConfig
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|apply
+ watchPaths:
+ - temp/properties/selectedConfiguration
+ - type: multi-file-editor
+ editorHeight: 500px
+ schema: temp/properties/applyConfig
+ loader:
+ name: setApplyConfig
+ watchPaths:
+ - temp/properties/selectedConfiguration
+ watcher:
+ func: onApplyconfigChange
+ paths:
+ - temp/properties/applyConfig
+ - type: block-layout
+ label: Remove
+ if:
+ name: ifReconfigurationTypeEqualsTo|remove
+ type: function
+ elements:
+ - type: label-element
+ label: Remove
+ subtitle: Selected a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/selectedConfigurationRemove
+ refresh: true
+ label: Configuration
+ loader:
+ name: getConfigSecretsforAppyConfig
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|remove
+ watchPaths:
+ - temp/properties/selectedConfigurationRemove
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ init:
+ type: func
+ value: onRemoveConfigChange
+ watcher:
+ func: onRemoveConfigChange
+ paths:
+ - temp/properties/selectedConfigurationRemove
+ schema: temp/properties/removeConfig
# Reconfigure TLS
- type: block-layout
label: TLS
diff --git a/charts/opskubedbcom-redisopsrequest-editor/ui/functions.js b/charts/opskubedbcom-redisopsrequest-editor/ui/functions.js
index 4b536dc52e..044238d55e 100644
--- a/charts/opskubedbcom-redisopsrequest-editor/ui/functions.js
+++ b/charts/opskubedbcom-redisopsrequest-editor/ui/functions.js
@@ -1,4 +1,4 @@
-const { axios, useOperator, store } = window.vueHelpers || {}
+const { axios, useOperator, store, useToast } = window.vueHelpers || {}
const machines = {
'db.t.micro': {
resources: {
@@ -306,9 +306,11 @@ const machineList = [
]
let machinesFromPreset = []
+const configSecretKeys = ['kubedb-user.conf']
export const useFunc = (model) => {
const route = store.state?.route
+ const toast = useToast()
const { getValue, storeGet, discriminator, setDiscriminatorValue, commit } = useOperator(
model,
@@ -826,28 +828,416 @@ export const useFunc = (model) => {
// for config secret
let secretArray = []
- async function getConfigSecrets() {
+
+ // Fetch and store database Infos
+ // for secret configurations in reconfigure
+ let configSecrets = []
+ let secretConfigData = []
+
+ async function fetchConfigSecrets() {
const owner = storeGet('/route/params/user')
const cluster = storeGet('/route/params/cluster')
const namespace = getValue(model, '/metadata/namespace')
// watchDependency('model#/metadata/namespace')
- const resp = await axios.get(
- `/clusters/${owner}/${cluster}/proxy/core/v1/namespaces/${namespace}/secrets`,
+ const name = getValue(model, '/spec/databaseRef/name')
+ const dbGroup = getValue(model, '/route/params/group')
+ const dbKind = getValue(store.state, '/resource/definition/result/kind')
+ const dbResource = getValue(model, '/route/params/resource')
+ const dbVersion = getValue(model, '/route/params/version')
+
+ try {
+ const resp = await axios.post(
+ `/clusters/${owner}/${cluster}/proxy/ui.kubedb.com/v1alpha1/databaseinfos`,
+ {
+ apiVersion: 'ui.kubedb.com/v1alpha1',
+ kind: 'DatabaseInfo',
+ request: {
+ source: {
+ ref: {
+ name: name,
+ namespace: namespace,
+ },
+ resource: {
+ group: dbGroup,
+ kind: dbKind,
+ name: dbResource,
+ version: dbVersion,
+ },
+ },
+ keys: ['kubedb-user.conf'],
+ },
+ },
+ )
+ configSecrets = resp?.data?.response?.availableSecrets || []
+ secretConfigData = resp?.data?.response?.configurations || []
+ } catch (e) {
+ console.log(e)
+ }
+ }
+
+ async function getConfigSecrets(type) {
+ type = type ? type + '/' : ''
+ const secretStatus = getValue(discriminator, `${type}createSecret/status`)
+ if (secretStatus === 'success') {
+ await fetchConfigSecrets()
+ }
+ const mappedSecrets = configSecrets.map((item) => {
+ return { text: item, value: item }
+ })
+ mappedSecrets.push({ text: '+ Create a new Secret', value: 'Create' })
+ return mappedSecrets
+ }
+
+ async function getConfigSecretsforAppyConfig() {
+ const secrets = secretConfigData.map((item) => {
+ return { text: item.componentName, value: item.componentName }
+ })
+ return secrets
+ }
+
+ function getSelectedConfigurationData(type) {
+ type = type ? type + '/' : ''
+ const path = `/${type}selectedConfiguration`
+ const selectedConfiguration = getValue(discriminator, path)
+
+ if (!selectedConfiguration) {
+ return []
+ }
+
+ const configuration = secretConfigData.find(
+ (item) => item.componentName === selectedConfiguration,
)
- const secrets = (resp && resp.data && resp.data.items) || []
- secretArray = secrets
+ if (!configuration) {
+ return []
+ }
- const filteredSecrets = secrets
+ const result = []
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(configuration.data).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedContent = atob(configuration.data[fileName])
+ result.push({
+ name: fileName,
+ content: decodedContent,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ result.push({
+ name: fileName,
+ content: configuration.data[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
- filteredSecrets.map((item) => {
- const name = (item.metadata && item.metadata.name) || ''
- item.text = name
- item.value = name
- return true
+ // Set the value to the model
+ commit('wizard/model$update', {
+ path: `/temp/${type}applyConfig`,
+ value: result,
+ force: true,
+ })
+
+ return result
+ }
+
+ function getSelectedConfigurationName(configType, type) {
+ type = type ? type + '/' : ''
+ let path = ''
+ if (configType === 'create') path = `/spec/configuration/${type}/configSecret/name`
+ else if (configType === 'apply') path = `/${type}selectedConfiguration`
+ else if (configType === 'remove') path = `/${type}selectedConfigurationRemove`
+
+ const selectedConfiguration =
+ configType === 'create' ? getValue(model, path) : getValue(discriminator, path)
+
+ if (selectedConfiguration)
+ return { subtitle: ` You have selected ${selectedConfiguration} secret` }
+ else return { subtitle: 'No secret selected' }
+ }
+
+ function getSelectedConfigurationValueForRemove(type) {
+ type = type ? type + '/' : ''
+ const path = `/${type}selectedConfigurationRemove`
+ const selectedConfiguration = getValue(discriminator, path)
+
+ if (!selectedConfiguration) {
+ return ''
+ }
+
+ const configuration = secretConfigData.find(
+ (item) => item.componentName === selectedConfiguration,
+ )
+
+ if (!configuration) {
+ return ''
+ }
+
+ let data = {}
+ // Decode base64 and parse YAML for each key in the secret data
+ Object.keys(configuration.data).forEach((item) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(configuration.data[item])
+ // Parse YAML string to object
+ const parsedYaml = yaml.load(decodedString)
+ // Store the parsed object with the filename as key
+ data[item] = parsedYaml
+ } catch (e) {
+ console.error(`Error parsing ${item}:`, e)
+ data[item] = atob(configuration.data[item]) // Fallback to decoded string
+ }
+ })
+
+ // Convert data object back to YAML string
+ return yaml.dump(data)
+ }
+
+ async function createNewConfigSecret(type) {
+ type = type ? type + '/' : ''
+ const { user, cluster } = route.params
+ const url = `/clusters/${user}/${cluster}/resources`
+ const namespace = storeGet('/route/query/namespace') || getValue(model, '/metadata/namespace')
+ const secretName = getValue(discriminator, `${type}createSecret/name`)
+ const secretData = getValue(discriminator, `${type}createSecret/data`)
+ const secretDataObj = Object.fromEntries(secretData.map((item) => [item.key, item.value]))
+
+ try {
+ const res = await axios.post(url, {
+ apiVersion: 'v1',
+ stringData: secretDataObj,
+ kind: 'Secret',
+ metadata: {
+ name: secretName,
+ namespace: namespace,
+ },
+ type: 'Opaque',
+ })
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'success',
+ })
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/lastCreatedSecret`,
+ value: secretName,
+ })
+ toast.success('Secret created successfully')
+ } catch (error) {
+ const errMsg = decodeError(error, 'Failed to create secret')
+ toast.error(errMsg, { timeout: 5000 })
+ cancelCreateSecret()
+ }
+ }
+
+ function decodeError(msg, defaultMsg) {
+ if (typeof msg === 'string') {
+ return msg || defaultMsg
+ }
+ return (
+ (msg.response && msg.response.data && msg.response.data.message) ||
+ (msg.response && msg.response.data) ||
+ (msg.status && msg.status.status) ||
+ defaultMsg
+ )
+ }
+
+ function isCreateSecret(type) {
+ type = type ? type + '/' : ''
+ const selectedSecret = getValue(model, `spec/configuration/${type}configSecret/name`)
+ const res = selectedSecret === 'Create'
+
+ if (res === true) {
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'pending',
+ })
+ }
+ return res
+ }
+
+ function isNotCreateSecret(type) {
+ return !isCreateSecret(type)
+ }
+
+ function onCreateSecretChange(type) {
+ type = type ? type + '/' : ''
+ const secretStatus = getValue(discriminator, `${type}createSecret/status`)
+ if (secretStatus === 'cancelled') return ''
+ else if (secretStatus === 'success') {
+ const name = getValue(discriminator, `${type}createSecret/lastCreatedSecret`)
+
+ const configFound = configSecrets.find((item) => item === name)
+ return configFound ? { text: name, value: name } : ''
+ }
+ }
+
+ function cancelCreateSecret(type) {
+ type = type ? type + '/' : ''
+ commit('wizard/temp$delete', `${type}createSecret/name`)
+ commit('wizard/temp$delete', `${type}createSecret/data`)
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'cancelled',
+ })
+ }
+
+ async function onApplyconfigChange(type) {
+ type = type ? type + '/' : ''
+ const configValue = getValue(discriminator, `${type}applyConfig`)
+
+ if (!configValue) {
+ commit('wizard/model$delete', `/spec/configuration/${type}applyConfig`)
+ return
+ }
+ const tempConfigObj = {}
+ configValue.forEach((item) => {
+ if (item.name) {
+ tempConfigObj[item.name] = item.content
+ }
+ })
+ if (Object.keys(tempConfigObj).length === 0) {
+ commit('wizard/model$delete', `/spec/configuration/${type}applyConfig`)
+ return
+ }
+ commit('wizard/model$update', {
+ path: `/spec/configuration/${type}applyConfig`,
+ value: tempConfigObj,
+ })
+ }
+
+ function setApplyConfig(type) {
+ type = type ? type + '/' : ''
+ const configPath = `/${type}selectedConfiguration`
+ const selectedConfig = getValue(discriminator, configPath)
+ if (!selectedConfig) {
+ return [{ name: '', content: '' }]
+ }
+ const applyconfigData = secretConfigData.find((item) => {
+ if (item.componentName === selectedConfig) {
+ return item
+ }
})
- return filteredSecrets
+ const { applyConfig } = applyconfigData
+ const configObj = []
+
+ if (applyConfig) {
+ Object.keys(applyConfig).forEach((fileName) => {
+ configObj.push({
+ name: fileName,
+ content: applyConfig[fileName],
+ })
+ })
+ } else {
+ configObj.push({ name: '', content: '' })
+ }
+ return configObj
+ }
+
+ function onRemoveConfigChange(type) {
+ type = type ? type + '/' : ''
+ const configPath = `/${type}selectedConfigurationRemove`
+ const selectedConfig = getValue(discriminator, configPath)
+
+ if (!selectedConfig) {
+ commit('wizard/model$delete', `/spec/configuration/${type}removeCustomConfig`)
+ return [{ name: '', content: '' }]
+ }
+ commit('wizard/model$update', {
+ path: `/spec/configuration/${type}removeCustomConfig`,
+ value: true,
+ })
+
+ const configuration = secretConfigData.find((item) => item.componentName === selectedConfig)
+
+ if (!configuration.data) {
+ return [{ name: '', content: '' }]
+ }
+
+ const configObj = []
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(configuration.data).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(configuration.data[fileName])
+ configObj.push({
+ name: fileName,
+ content: decodedString,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ configObj.push({
+ name: fileName,
+ content: configuration.data[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
+ return configObj
+ }
+
+ async function onNewConfigSecretChange(type) {
+ type = type ? type + '/' : ''
+ const path = `/spec/configuration/${type}configSecret/name`
+ const selectedSecret = getValue(model, path)
+
+ if (!selectedSecret) {
+ commit('wizard/model$delete', `/spec/configuration/${type}configSecret`)
+ return [{ name: '', content: '' }]
+ }
+ if (selectedSecret === 'Create') return [{ name: '', content: '' }]
+
+ const owner = storeGet('/route/params/user')
+ const cluster = storeGet('/route/params/cluster')
+ const namespace = storeGet('/route/query/namespace') || getValue(model, '/metadata/namespace')
+
+ try {
+ // Fetch the secret data from API
+ const secretResp = await axios.get(
+ `/clusters/${owner}/${cluster}/proxy/core/v1/namespaces/${namespace}/secrets/${selectedSecret}`,
+ )
+
+ const secretData = secretResp.data?.data || {}
+ const configObj = []
+
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(secretData).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(secretData[fileName])
+ configObj.push({
+ name: fileName,
+ content: decodedString,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ configObj.push({
+ name: fileName,
+ content: secretData[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
+
+ return configObj
+ } catch (e) {
+ console.error('Error fetching secret:', e)
+ return [{ name: '', content: '' }]
+ }
+ }
+
+ function onSelectedSecretChange(index) {
+ const secretData = getValue(discriminator, 'createSecret/data') || []
+ const selfSecrets = secretData.map((item) => item.key)
+
+ const remainingSecrets = configSecretKeys.filter((item) => !selfSecrets.includes(item))
+
+ const selfKey = getValue(discriminator, `createSecret/data/${index}/key`)
+ if (selfKey) {
+ remainingSecrets.push(selfKey)
+ }
+ const resSecret = remainingSecrets.map((item) => {
+ return { text: item, value: item }
+ })
+ return resSecret
}
function createSecretUrl() {
@@ -1029,24 +1419,6 @@ export const useFunc = (model) => {
return reconfigurationType === value
}
- function onApplyconfigChange() {
- const applyconfig = getValue(discriminator, '/applyConfig')
-
- const configObj = {}
- if (applyconfig) {
- applyconfig.forEach((item) => {
- const { key, value } = item
- configObj[key] = value
- })
- }
-
- commit('wizard/model$update', {
- path: '/spec/configuration/applyConfig',
- value: configObj,
- force: true,
- })
- }
-
function onReconfigurationTypeChange() {
const reconfigurationType = getValue(discriminator, '/reconfigurationType')
setDiscriminatorValue('/applyConfig', [])
@@ -1405,5 +1777,20 @@ export const useFunc = (model) => {
isMachineCustom,
checkVolume,
setReplicas,
+ fetchConfigSecrets,
+ getConfigSecretsforAppyConfig,
+ getSelectedConfigurationData,
+ getSelectedConfigurationName,
+ getSelectedConfigurationValueForRemove,
+ createNewConfigSecret,
+ decodeError,
+ isCreateSecret,
+ isNotCreateSecret,
+ onCreateSecretChange,
+ cancelCreateSecret,
+ setApplyConfig,
+ onRemoveConfigChange,
+ onNewConfigSecretChange,
+ onSelectedSecretChange,
}
}
diff --git a/charts/opskubedbcom-singlestoreopsrequest-editor/ui/create-ui.yaml b/charts/opskubedbcom-singlestoreopsrequest-editor/ui/create-ui.yaml
index bbf4336ac9..f7d2c87e47 100644
--- a/charts/opskubedbcom-singlestoreopsrequest-editor/ui/create-ui.yaml
+++ b/charts/opskubedbcom-singlestoreopsrequest-editor/ui/create-ui.yaml
@@ -466,109 +466,177 @@ step:
if:
name: ifRequestTypeEqualsTo|Reconfigure
type: function
+ loader:
+ name: fetchConfigSecrets
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
elements:
- - type: radio
- label: Reconfigure Type
- subtitle: Choose the action you'd like to perform. Select a new configuration secret, apply a custom configuration, or remove an existing setup.
- options:
- - text: New Config Secret
- value: selectNewConfigSecret
- - text: Apply Config
- value: applyConfig
- - text: Remove
- value: remove
- watcher:
- func: onReconfigurationTypeChange
- paths:
- - temp/properties/reconfigurationType
- validation:
- type: required
- schema: temp/properties/reconfigurationType
- type: block-layout
- label: Config Secret
- showLabels: true
- if:
- name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret
- type: function
- elements:
- - type: label-element
- label: Select a configuration secret from the available list to update your database settings
- customClass: mb-10
- - type: select
- addNewButton:
- label: Create a new Secret
- target: _blank
- url:
- function: createSecretUrl
- label: Config Secret
- loader:
- name: getConfigSecrets
- watchPaths:
- - schema/properties/metadata/properties/namespace
- init:
- type: func
- value: setValueFromDbDetails|/spec/configSecret/name
- refresh: true
- validation:
- type: required
- schema: schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- - type: label-element
- label: Selected Config Secret
- loader: getSelectedConfigSecret
- watcher:
- func: getSelectedConfigSecret
- paths:
- - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- - type: editor
- label: Value
- readonly: true
- hasCopy: false
- if:
- name: isConfigSelected
- type: function
- watcher:
- func: getSelectedConfigSecretValue
- paths:
- - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- validation:
- type: required
- schema: temp/properties/configArray/items/properties/value
- - type: array-object-form
- label: ApplyConfig
- buttonClass: is-light is-outlined
- if:
- name: ifReconfigurationTypeEqualsTo|applyConfig
- type: function
- validation:
- type: required
- watcher:
- func: onApplyconfigChange
- paths:
- - temp/properties/applyConfig
- schema: temp/properties/applyConfig
+ label: Configuration
elements:
- type: label-element
- label: New Apply Config
- subtitle: Define custom configurations for your database using key-value pairs. These parameters will overwrite existing settings. Enter the parameter you want to configure (e.g., max_connections).
- customClass: mb-10
- - type: input
- label: key
- validation:
- type: required
- schema: key
- - type: editor
- hasCopy: false
- label: value
- validation:
- type: required
- schema: value
- - type: switch
- schema: schema/properties/spec/properties/configuration/properties/removeCustomConfig
- label: Remove CustomConfig
- fullwidth: true
- if:
- name: returnFalse
- type: function
+ label: ''
+ subtitle: Select a new configuration secret, apply a custom configuration, or remove an existing setup to update your database settings
+ - type: tab-layout
+ label: New Config Secret
+ schema: temp/properties/reconfigurationType
+ options:
+ - text: NEW CONFIG SECRET
+ value: selectNewConfigSecret
+ - text: APPLY CONFIG
+ value: applyConfig
+ - text: REMOVE
+ value: remove
+ elements:
+ - type: block-layout
+ label: Config Secret
+ if:
+ name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret
+ type: function
+ elements:
+ - type: label-element
+ label: Config Secret
+ subtitle: Select a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ refresh: true
+ label: Config Secret
+ loader:
+ name: getConfigSecrets
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - temp/properties/createSecret/properties/status
+ init:
+ type: func
+ value: setValueFromDbDetails|/spec/configSecret/name
+ watcher:
+ func: onCreateSecretChange
+ paths:
+ - temp/properties/createSecret/properties/status
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|create
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ - type: block-layout
+ label: Create a New Config Secret
+ showLabels: true
+ if:
+ type: function
+ name: isCreateSecret
+ hasButton:
+ text: Save
+ hasCancel: cancelCreateSecret
+ action: createNewConfigSecret
+ elements:
+ - type: input
+ label: Secret Name
+ schema: temp/properties/createSecret/properties/name
+ validation:
+ type: required
+ - type: array-object-form
+ label: String Data
+ schema: temp/properties/createSecret/properties/data
+ buttonClass: is-light is-outlined
+ validation:
+ type: required
+ elements:
+ - type: select
+ label: Key
+ schema: key
+ loader:
+ name: onSelectedSecretChange
+ watchPaths:
+ - temp/properties/createSecret/properties/data
+ validation:
+ type: required
+ - type: textarea
+ label: Value
+ schema: value
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ if:
+ type: function
+ name: isNotCreateSecret
+ loader:
+ name: onNewConfigSecretChange
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ schema: temp/properties/newConfigSecret
+ - type: block-layout
+ label: ApplyConfig
+ if:
+ name: ifReconfigurationTypeEqualsTo|applyConfig
+ type: function
+ elements:
+ - type: label-element
+ label: New Apply Config
+ subtitle: Define custom configurations for your database using key-value pairs. These parameters will overwrite existing settings.
Enter the parameter you want to configure (e.g., max_connections).
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/selectedConfiguration
+ refresh: true
+ label: Configuration
+ loader: getConfigSecretsforAppyConfig
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|apply
+ watchPaths:
+ - temp/properties/selectedConfiguration
+ - type: multi-file-editor
+ editorHeight: 500px
+ schema: temp/properties/applyConfig
+ loader:
+ name: setApplyConfig
+ watchPaths:
+ - temp/properties/selectedConfiguration
+ watcher:
+ func: onApplyconfigChange
+ paths:
+ - temp/properties/applyConfig
+ - type: block-layout
+ label: Remove
+ if:
+ name: ifReconfigurationTypeEqualsTo|remove
+ type: function
+ elements:
+ - type: label-element
+ label: Remove
+ subtitle: Selected a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/selectedConfigurationRemove
+ refresh: true
+ label: Configuration
+ loader:
+ name: getConfigSecretsforAppyConfig
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|remove
+ watchPaths:
+ - temp/properties/selectedConfigurationRemove
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ init:
+ type: func
+ value: onRemoveConfigChange
+ watcher:
+ func: onRemoveConfigChange
+ paths:
+ - temp/properties/selectedConfigurationRemove
+ schema: temp/properties/removeConfig
+
# Reconfigure TLS
- type: block-layout
label: TLS
diff --git a/charts/opskubedbcom-singlestoreopsrequest-editor/ui/functions.js b/charts/opskubedbcom-singlestoreopsrequest-editor/ui/functions.js
index ac233ded5a..05e0e26547 100644
--- a/charts/opskubedbcom-singlestoreopsrequest-editor/ui/functions.js
+++ b/charts/opskubedbcom-singlestoreopsrequest-editor/ui/functions.js
@@ -1,4 +1,4 @@
-const { axios, useOperator, store } = window.vueHelpers || {}
+const { axios, useOperator, store, useToast } = window.vueHelpers || {}
const machines = {
'db.t.micro': {
resources: {
@@ -306,9 +306,11 @@ const machineList = [
]
let machinesFromPreset = []
+const configSecretKeys = ['kubedb-user.cnf']
export const useFunc = (model) => {
const route = store.state?.route
+ const toast = useToast()
const { getValue, storeGet, discriminator, setDiscriminatorValue, commit } = useOperator(
model,
@@ -878,32 +880,419 @@ export const useFunc = (model) => {
return resource[0]?.resources
}
- // for config secret
- let secretArray = []
- async function getConfigSecrets() {
+ // Fetch and store database Infos
+ // for secret configurations in reconfigure
+ let configSecrets = []
+ let secretConfigData = []
+
+ async function fetchConfigSecrets() {
const owner = storeGet('/route/params/user')
const cluster = storeGet('/route/params/cluster')
const namespace = getValue(model, '/metadata/namespace')
// watchDependency('model#/metadata/namespace')
- const resp = await axios.get(
- `/clusters/${owner}/${cluster}/proxy/core/v1/namespaces/${namespace}/secrets`,
+ const name = getValue(model, '/spec/databaseRef/name')
+ const dbGroup = getValue(model, '/route/params/group')
+ const dbKind = getValue(store.state, '/resource/definition/result/kind')
+ const dbResource = getValue(model, '/route/params/resource')
+ const dbVersion = getValue(model, '/route/params/version')
+
+ try {
+ const resp = await axios.post(
+ `/clusters/${owner}/${cluster}/proxy/ui.kubedb.com/v1alpha1/databaseinfos`,
+ {
+ apiVersion: 'ui.kubedb.com/v1alpha1',
+ kind: 'DatabaseInfo',
+ request: {
+ source: {
+ ref: {
+ name: name,
+ namespace: namespace,
+ },
+ resource: {
+ group: dbGroup,
+ kind: dbKind,
+ name: dbResource,
+ version: dbVersion,
+ },
+ },
+ keys: ['kubedb-user.cnf'],
+ },
+ },
+ )
+ configSecrets = resp?.data?.response?.availableSecrets || []
+ secretConfigData = resp?.data?.response?.configurations || []
+ } catch (e) {
+ console.log(e)
+ }
+ }
+
+ async function getConfigSecrets(type) {
+ type = type ? type + '/' : ''
+ const secretStatus = getValue(discriminator, `${type}createSecret/status`)
+ if (secretStatus === 'success') {
+ await fetchConfigSecrets()
+ }
+ const mappedSecrets = configSecrets.map((item) => {
+ return { text: item, value: item }
+ })
+ mappedSecrets.push({ text: '+ Create a new Secret', value: 'Create' })
+ return mappedSecrets
+ }
+
+ async function getConfigSecretsforAppyConfig() {
+ const secrets = secretConfigData.map((item) => {
+ return { text: item.componentName, value: item.componentName }
+ })
+ return secrets
+ }
+
+ function getSelectedConfigurationData(type) {
+ type = type ? type + '/' : ''
+ const path = `/${type}selectedConfiguration`
+ const selectedConfiguration = getValue(discriminator, path)
+
+ if (!selectedConfiguration) {
+ return []
+ }
+
+ const configuration = secretConfigData.find(
+ (item) => item.componentName === selectedConfiguration,
)
- const secrets = (resp && resp.data && resp.data.items) || []
- secretArray = secrets
+ if (!configuration) {
+ return []
+ }
- const filteredSecrets = secrets
+ const result = []
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(configuration.data).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedContent = atob(configuration.data[fileName])
+ result.push({
+ name: fileName,
+ content: decodedContent,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ result.push({
+ name: fileName,
+ content: configuration.data[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
- filteredSecrets.map((item) => {
- const name = (item.metadata && item.metadata.name) || ''
- item.text = name
- item.value = name
- return true
+ // Set the value to the model
+ commit('wizard/model$update', {
+ path: `/temp/${type}applyConfig`,
+ value: result,
+ force: true,
})
- return filteredSecrets
+
+ return result
+ }
+
+ function getSelectedConfigurationName(configType, type) {
+ type = type ? type + '/' : ''
+ let path = ''
+ if (configType === 'create') path = `/spec/configuration/${type}/configSecret/name`
+ else if (configType === 'apply') path = `/${type}selectedConfiguration`
+ else if (configType === 'remove') path = `/${type}selectedConfigurationRemove`
+
+ const selectedConfiguration =
+ configType === 'create' ? getValue(model, path) : getValue(discriminator, path)
+
+ if (selectedConfiguration)
+ return { subtitle: ` You have selected ${selectedConfiguration} secret` }
+ else return { subtitle: 'No secret selected' }
+ }
+
+ function getSelectedConfigurationValueForRemove(type) {
+ type = type ? type + '/' : ''
+ const path = `/${type}selectedConfigurationRemove`
+ const selectedConfiguration = getValue(discriminator, path)
+
+ if (!selectedConfiguration) {
+ return ''
+ }
+
+ const configuration = secretConfigData.find(
+ (item) => item.componentName === selectedConfiguration,
+ )
+
+ if (!configuration) {
+ return ''
+ }
+
+ let data = {}
+ // Decode base64 and parse YAML for each key in the secret data
+ Object.keys(configuration.data).forEach((item) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(configuration.data[item])
+ // Parse YAML string to object
+ const parsedYaml = yaml.load(decodedString)
+ // Store the parsed object with the filename as key
+ data[item] = parsedYaml
+ } catch (e) {
+ console.error(`Error parsing ${item}:`, e)
+ data[item] = atob(configuration.data[item]) // Fallback to decoded string
+ }
+ })
+
+ // Convert data object back to YAML string
+ return yaml.dump(data)
+ }
+
+ async function createNewConfigSecret(type) {
+ type = type ? type + '/' : ''
+ const { user, cluster } = route.params
+ const url = `/clusters/${user}/${cluster}/resources`
+ const namespace = storeGet('/route/query/namespace') || getValue(model, '/metadata/namespace')
+ const secretName = getValue(discriminator, `${type}createSecret/name`)
+ const secretData = getValue(discriminator, `${type}createSecret/data`)
+ const secretDataObj = Object.fromEntries(secretData.map((item) => [item.key, item.value]))
+
+ try {
+ const res = await axios.post(url, {
+ apiVersion: 'v1',
+ stringData: secretDataObj,
+ kind: 'Secret',
+ metadata: {
+ name: secretName,
+ namespace: namespace,
+ },
+ type: 'Opaque',
+ })
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'success',
+ })
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/lastCreatedSecret`,
+ value: secretName,
+ })
+ toast.success('Secret created successfully')
+ } catch (error) {
+ const errMsg = decodeError(error, 'Failed to create secret')
+ toast.error(errMsg, { timeout: 5000 })
+ cancelCreateSecret()
+ }
+ }
+
+ function decodeError(msg, defaultMsg) {
+ if (typeof msg === 'string') {
+ return msg || defaultMsg
+ }
+ return (
+ (msg.response && msg.response.data && msg.response.data.message) ||
+ (msg.response && msg.response.data) ||
+ (msg.status && msg.status.status) ||
+ defaultMsg
+ )
+ }
+
+ function isCreateSecret(type) {
+ type = type ? type + '/' : ''
+ const selectedSecret = getValue(model, `spec/configuration/${type}configSecret/name`)
+ const res = selectedSecret === 'Create'
+
+ if (res === true) {
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'pending',
+ })
+ }
+ return res
+ }
+
+ function isNotCreateSecret(type) {
+ return !isCreateSecret(type)
}
+ function onCreateSecretChange(type) {
+ type = type ? type + '/' : ''
+ const secretStatus = getValue(discriminator, `${type}createSecret/status`)
+ if (secretStatus === 'cancelled') return ''
+ else if (secretStatus === 'success') {
+ const name = getValue(discriminator, `${type}createSecret/lastCreatedSecret`)
+
+ const configFound = configSecrets.find((item) => item === name)
+ return configFound ? { text: name, value: name } : ''
+ }
+ }
+
+ function cancelCreateSecret(type) {
+ type = type ? type + '/' : ''
+ commit('wizard/temp$delete', `${type}createSecret/name`)
+ commit('wizard/temp$delete', `${type}createSecret/data`)
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'cancelled',
+ })
+ }
+
+ async function onApplyconfigChange(type) {
+ type = type ? type + '/' : ''
+ const configValue = getValue(discriminator, `${type}applyConfig`)
+
+ if (!configValue) {
+ commit('wizard/model$delete', `/spec/configuration/${type}applyConfig`)
+ return
+ }
+ const tempConfigObj = {}
+ configValue.forEach((item) => {
+ if (item.name) {
+ tempConfigObj[item.name] = item.content
+ }
+ })
+ if (Object.keys(tempConfigObj).length === 0) {
+ commit('wizard/model$delete', `/spec/configuration/${type}applyConfig`)
+ return
+ }
+ commit('wizard/model$update', {
+ path: `/spec/configuration/${type}applyConfig`,
+ value: tempConfigObj,
+ })
+ }
+
+ function setApplyConfig(type) {
+ type = type ? type + '/' : ''
+ const configPath = `/${type}selectedConfiguration`
+ const selectedConfig = getValue(discriminator, configPath)
+ if (!selectedConfig) {
+ return [{ name: '', content: '' }]
+ }
+ const applyconfigData = secretConfigData.find((item) => {
+ if (item.componentName === selectedConfig) {
+ return item
+ }
+ })
+ const { applyConfig } = applyconfigData
+ const configObj = []
+
+ if (applyConfig) {
+ Object.keys(applyConfig).forEach((fileName) => {
+ configObj.push({
+ name: fileName,
+ content: applyConfig[fileName],
+ })
+ })
+ } else {
+ configObj.push({ name: '', content: '' })
+ }
+ return configObj
+ }
+
+ function onRemoveConfigChange(type) {
+ type = type ? type + '/' : ''
+ const configPath = `/${type}selectedConfigurationRemove`
+ const selectedConfig = getValue(discriminator, configPath)
+
+ if (!selectedConfig) {
+ commit('wizard/model$delete', `/spec/configuration/${type}removeCustomConfig`)
+ return [{ name: '', content: '' }]
+ }
+ commit('wizard/model$update', {
+ path: `/spec/configuration/${type}removeCustomConfig`,
+ value: true,
+ })
+
+ const configuration = secretConfigData.find((item) => item.componentName === selectedConfig)
+
+ if (!configuration.data) {
+ return [{ name: '', content: '' }]
+ }
+
+ const configObj = []
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(configuration.data).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(configuration.data[fileName])
+ configObj.push({
+ name: fileName,
+ content: decodedString,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ configObj.push({
+ name: fileName,
+ content: configuration.data[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
+ return configObj
+ }
+
+ async function onNewConfigSecretChange(type) {
+ type = type ? type + '/' : ''
+ const path = `/spec/configuration/${type}configSecret/name`
+ const selectedSecret = getValue(model, path)
+
+ if (!selectedSecret) {
+ commit('wizard/model$delete', `/spec/configuration/${type}configSecret`)
+ return [{ name: '', content: '' }]
+ }
+ if (selectedSecret === 'Create') return [{ name: '', content: '' }]
+
+ const owner = storeGet('/route/params/user')
+ const cluster = storeGet('/route/params/cluster')
+ const namespace = storeGet('/route/query/namespace') || getValue(model, '/metadata/namespace')
+
+ try {
+ // Fetch the secret data from API
+ const secretResp = await axios.get(
+ `/clusters/${owner}/${cluster}/proxy/core/v1/namespaces/${namespace}/secrets/${selectedSecret}`,
+ )
+
+ const secretData = secretResp.data?.data || {}
+ const configObj = []
+
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(secretData).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(secretData[fileName])
+ configObj.push({
+ name: fileName,
+ content: decodedString,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ configObj.push({
+ name: fileName,
+ content: secretData[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
+
+ return configObj
+ } catch (e) {
+ console.error('Error fetching secret:', e)
+ return [{ name: '', content: '' }]
+ }
+ }
+
+ function onSelectedSecretChange(index) {
+ const secretData = getValue(discriminator, 'createSecret/data') || []
+ const selfSecrets = secretData.map((item) => item.key)
+
+ const remainingSecrets = configSecretKeys.filter((item) => !selfSecrets.includes(item))
+
+ const selfKey = getValue(discriminator, `createSecret/data/${index}/key`)
+ if (selfKey) {
+ remainingSecrets.push(selfKey)
+ }
+ const resSecret = remainingSecrets.map((item) => {
+ return { text: item, value: item }
+ })
+ return resSecret
+ }
+
+ let secretArray = []
+
function isConfigSelected() {
const path = `/spec/configuration/configSecret/name`
const selectedSecret = getValue(model, path)
@@ -1085,25 +1474,6 @@ export const useFunc = (model) => {
return reconfigurationType === value
}
- function onApplyconfigChange() {
- const applyconfig = getValue(discriminator, '/applyConfig')
-
- const configObj = {}
-
- if (applyconfig) {
- applyconfig.forEach((item) => {
- const { key, value } = item
- configObj[key] = value
- })
- }
-
- commit('wizard/model$update', {
- path: `/spec/configuration/applyConfig`,
- value: configObj,
- force: true,
- })
- }
-
function onReconfigurationTypeChange() {
setDiscriminatorValue('/applyConfig', [])
const reconfigurationType = getValue(discriminator, '/reconfigurationType')
@@ -1456,5 +1826,20 @@ export const useFunc = (model) => {
checkVolume,
setResource,
isVerticalScaleTopologyRequired,
+ fetchConfigSecrets,
+ getConfigSecretsforAppyConfig,
+ getSelectedConfigurationData,
+ getSelectedConfigurationName,
+ getSelectedConfigurationValueForRemove,
+ createNewConfigSecret,
+ decodeError,
+ isCreateSecret,
+ isNotCreateSecret,
+ onCreateSecretChange,
+ cancelCreateSecret,
+ setApplyConfig,
+ onRemoveConfigChange,
+ onNewConfigSecretChange,
+ onSelectedSecretChange,
}
}
diff --git a/charts/opskubedbcom-solropsrequest-editor/ui/create-ui.yaml b/charts/opskubedbcom-solropsrequest-editor/ui/create-ui.yaml
index 18acb745de..1b48af9120 100644
--- a/charts/opskubedbcom-solropsrequest-editor/ui/create-ui.yaml
+++ b/charts/opskubedbcom-solropsrequest-editor/ui/create-ui.yaml
@@ -570,428 +570,670 @@ step:
if:
name: ifRequestTypeEqualsTo|Reconfigure
type: function
+ loader:
+ name: fetchConfigSecrets
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
elements:
+ - type: label-element
+ label: ''
+ subtitle: Select a new configuration secret, apply a custom configuration, or remove an existing setup to update your database settings
- type: block-layout
label: Combined
if:
name: ifDbTypeEqualsTo|Combined|configuration
type: function
elements:
- - type: radio
- label: Reconfigure Type
- subtitle: Choose the action you'd like to perform. Select a new configuration secret, apply a custom configuration , or remove an existing setup.
- validation:
- type: required
+ - type: tab-layout
+ label: New Config Secret
+ schema: temp/properties/reconfigurationType
options:
- - text: New Config Secret
+ - text: NEW CONFIG SECRET
value: selectNewConfigSecret
- - text: Apply Config
+ - text: APPLY CONFIG
value: applyConfig
- - text: Remove
+ - text: REMOVE
value: remove
- schema: temp/properties/reconfigurationType
- watcher:
- func: onReconfigurationTypeChange|node
- paths:
- - temp/properties/reconfigurationType
- - type: block-layout
- label: Config Secret
- showLabels: true
- if:
- name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret
- type: function
- elements:
- - type: label-element
- label: Select a configuration secret from the available list to update your database settings
- customClass: mb-10
- - type: select
- addNewButton:
- label: Create Secret
- target: _blank
- url:
- function: createSecretUrl
- schema: schema/properties/spec/properties/configuration/properties/node/properties/configSecret/properties/name
- validation:
- type: required
- refresh: true
- label: Config Secret
- loader:
- name: getConfigSecrets
- watchPaths:
- - schema/properties/metadata/properties/namespace
- init:
- type: func
- value: setValueFromDbDetails|/spec/configSecret/name
- - type: label-element
- label: Selected Config Secret
- loader: getSelectedConfigSecret|node
- watcher:
- func: getSelectedConfigSecret|node
- paths:
- - schema/properties/spec/properties/configuration/properties/node/properties/configSecret/properties/name
- - type: editor
- label: Value
- readonly: true
- hasCopy: false
- watcher:
- func: getSelectedConfigSecretValue|node
- paths:
- - schema/properties/spec/properties/configuration/properties/node/properties/configSecret/properties/name
- validation:
- type: required
- schema: temp/properties/configArray/items/properties/value
- - type: array-object-form
- label: ApplyConfig
- buttonClass: is-light is-outlined
- schema: temp/properties/node/applyConfig
- validation:
- type: required
- if:
- name: ifReconfigurationTypeEqualsTo|applyConfig
- type: function
- watcher:
- func: onApplyconfigChange|node
- paths:
- - temp/properties/node/applyConfig
elements:
- - type: label-element
- label: New Apply Config
- subtitle: Define custom configurations for your database using key-value pairs. These parameters will overwrite existing settings. Enter the parameter you want to configure (e.g., max_connections).
- customClass: mb-10
- - type: input
- label: key
- validation:
- type: required
- schema: key
- - type: editor
- label: value
- hasCopy: false
- validation:
- type: required
- schema: value
- - type: switch
- schema: schema/properties/spec/properties/configuration/properties/node/properties/removeCustomConfig
- label: Remove CustomConfig
- fullwidth: true
- if:
- name: returnFalse
- type: function
- - type: block-layout
- elements:
- - type: block-layout
- label: Coordinator
- showLabels: true
- elements:
- - type: radio
- label: Reconfigure Type
- subtitle: Choose the action you'd like to perform. Select a new configuration secret, apply a custom configuration , or remove an existing setup.
- validation:
- type: required
- schema: temp/properties/reconfigurationType-coordinator
- options:
- - text: New Config Secret
- value: selectNewConfigSecret
- - text: Apply Config
- value: applyConfig
- - text: Remove
- value: remove
- watcher:
- func: onReconfigurationTypeChange|coordinator|true
- paths:
- - temp/properties/reconfigurationType-coordinator
- type: block-layout
label: Config Secret
- showLabels: true
if:
- name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret|coordinator|true
+ name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret
type: function
elements:
- type: label-element
- label: Select a configuration secret from the available list to update your database settings
+ label: Config Secret
+ subtitle: Select a configuration secret from the available list to update your database settings
customClass: mb-10
- type: select
- addNewButton:
- label: Create a new Secret
- target: _blank
- url:
- function: createSecretUrl
- schema: schema/properties/spec/properties/configuration/properties/coordinator/properties/configSecret/properties/name
- validation:
- type: required
+ customClass: mb-2
+ schema: schema/properties/spec/properties/configuration/properties/node/properties/configSecret/properties/name
refresh: true
label: Config Secret
loader:
- name: getConfigSecrets
+ name: getConfigSecrets|node
watchPaths:
- schema/properties/metadata/properties/namespace
+ - temp/properties/node/properties/createSecret/properties/status
init:
type: func
- value: setValueFromDbDetails|/spec/topology/coordinator/configSecret/name
- - type: label-element
- label: Selected Config Secret
- loader: getSelectedConfigSecret|coordinator
+ value: setValueFromDbDetails|/spec/configSecret/name
watcher:
- func: getSelectedConfigSecret|coordinator
+ func: onCreateSecretChange|node
paths:
- - schema/properties/spec/properties/configuration/properties/coordinator/properties/configSecret/properties/name
- - type: editor
- label: Value
+ - temp/properties/node/properties/createSecret/properties/status
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|create|node
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/node/properties/configSecret/properties/name
+ - type: block-layout
+ label: Create a New Config Secret
+ showLabels: true
+ if:
+ type: function
+ name: isCreateSecret|node
+ hasButton:
+ text: Save
+ hasCancel: cancelCreateSecret|node
+ action: createNewConfigSecret|node
+ elements:
+ - type: input
+ label: Secret Name
+ schema: temp/properties/node/properties/createSecret/properties/name
+ validation:
+ type: required
+ - type: array-object-form
+ label: String Data
+ schema: temp/properties/node/properties/createSecret/properties/data
+ buttonClass: is-light is-outlined
+ validation:
+ type: required
+ elements:
+ - type: select
+ label: Key
+ schema: key
+ loader:
+ name: onSelectedSecretChange|node
+ watchPaths:
+ - temp/properties/node/properties/createSecret/properties/data
+ validation:
+ type: required
+ - type: textarea
+ label: Value
+ schema: value
+ - type: multi-file-editor
+ editorHeight: 500px
readonly: true
- hasCopy: false
- watcher:
- func: getSelectedConfigSecretValue|coordinator
- paths:
- - schema/properties/spec/properties/configuration/properties/coordinator/properties/configSecret/properties/name
- validation:
- type: required
- schema: temp/properties/configArray/items/properties/value
- - type: array-object-form
+ if:
+ type: function
+ name: isNotCreateSecret|node
+ loader:
+ name: onNewConfigSecretChange|node
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/node/properties/configSecret/properties/name
+ schema: temp/properties/node/newConfigSecret
+ - type: block-layout
label: ApplyConfig
- schema: temp/properties/coordinator/applyConfig
- buttonClass: is-light is-outlined
- validation:
- type: required
if:
- name: ifReconfigurationTypeEqualsTo|applyConfig|coordinator|true
+ name: ifReconfigurationTypeEqualsTo|applyConfig
type: function
- watcher:
- func: onApplyconfigChange|coordinator
- paths:
- - temp/properties/coordinator/applyConfig
elements:
- type: label-element
label: New Apply Config
- subtitle: Define custom configurations for your database using key-value pairs. These parameters will overwrite existing settings. Enter the parameter you want to configure (e.g., max_connections).
+ subtitle: Define custom configurations for your database using key-value pairs. These parameters will overwrite existing settings.
Enter the parameter you want to configure (e.g., max_connections).
customClass: mb-10
- - type: input
- label: key
- validation:
- type: required
- schema: key
- - type: editor
- label: value
- hasCopy: false
- validation:
- type: required
- schema: value
-
- - type: switch
- label: Remove CustomConfig
- fullwidth: true
- schema: schema/properties/spec/properties/configuration/properties/coordinator/properties/removeCustomConfig
- if:
- name: returnFalse
- type: function
- - type: block-layout
- label: Data
- showLabels: true
- elements:
- - type: radio
- label: Reconfigure Type
- subtitle: Choose the action you'd like to perform. Select a new configuration secret, apply a custom configuration , or remove an existing setup.
- validation:
- type: required
- schema: temp/properties/reconfigurationType-data
- options:
- - text: New Config Secret
- value: selectNewConfigSecret
- - text: Apply Config
- value: applyConfig
- - text: Remove
- value: remove
- watcher:
- func: onReconfigurationTypeChange|data|true
- paths:
- - temp/properties/reconfigurationType-data
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/node/selectedConfiguration
+ refresh: true
+ label: Configuration
+ loader: getConfigSecretsforAppyConfig
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|apply|node
+ watchPaths:
+ - temp/properties/node/selectedConfiguration
+ - type: multi-file-editor
+ editorHeight: 500px
+ schema: temp/properties/node/applyConfig
+ loader:
+ name: setApplyConfig|node
+ watchPaths:
+ - temp/properties/node/selectedConfiguration
+ watcher:
+ func: onApplyconfigChange|node
+ paths:
+ - temp/properties/node/applyConfig
- type: block-layout
- label: Config Secret
- showLabels: true
+ label: Remove
if:
- name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret|data|true
+ name: ifReconfigurationTypeEqualsTo|remove
type: function
elements:
- - type: label-element
- label: Select a configuration secret from the available list to update your database settings
+ - type: label-element
+ label: Remove
+ subtitle: Selected a configuration secret from the available list to update your database settings
customClass: mb-10
- type: select
- addNewButton:
- label: Create Secret
- target: _blank
- url:
- function: createSecretUrl
- schema: schema/properties/spec/properties/configuration/properties/data/properties/configSecret/properties/name
- validation:
- type: required
+ customClass: mb-2
+ schema: temp/properties/node/selectedConfigurationRemove
refresh: true
- label: Config Secret
+ label: Configuration
loader:
- name: getConfigSecrets
+ name: getConfigSecretsforAppyConfig
watchPaths:
- schema/properties/metadata/properties/namespace
- init:
- type: func
- value: setValueFromDbDetails|/spec/topology/data/configSecret/name
- type: label-element
- label: Selected Config Secret
- loader: getSelectedConfigSecret|data
- watcher:
- func: getSelectedConfigSecret|data
- paths:
- - schema/properties/spec/properties/configuration/properties/data/properties/configSecret/properties/name
- - type: editor
- label: Value
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|remove|node
+ watchPaths:
+ - temp/properties/node/selectedConfigurationRemove
+ - type: multi-file-editor
+ editorHeight: 500px
readonly: true
- hasCopy: false
+ init:
+ type: func
+ value: onRemoveConfigChange|node
watcher:
- func: getSelectedConfigSecretValue|data
+ func: onRemoveConfigChange|node
paths:
- - schema/properties/spec/properties/configuration/properties/data/properties/configSecret/properties/name
- validation:
- type: required
- schema: temp/properties/configArray/items/properties/value
- - type: array-object-form
- label: ApplyConfig
- schema: temp/properties/data/applyConfig
- buttonClass: is-light is-outlined
- validation:
- type: required
- if:
- name: ifReconfigurationTypeEqualsTo|applyConfig|data|true
- type: function
- watcher:
- func: onApplyconfigChange|data
- paths:
- - temp/properties/data/applyConfig
+ - temp/properties/node/selectedConfigurationRemove
+ schema: temp/properties/node/removeConfig
+ - type: block-layout
+ elements:
+ - type: block-layout
+ label: Coordinator
+ showLabels: true
+ elements:
+ - type: tab-layout
+ label: New Config Secret
+ schema: temp/properties/reconfigurationType-coordinator
+ options:
+ - text: NEW CONFIG SECRET
+ value: selectNewConfigSecret
+ - text: APPLY CONFIG
+ value: applyConfig
+ - text: REMOVE
+ value: remove
elements:
- - type: label-element
- label: New Apply Config
- subtitle: Define custom configurations for your database using key-value pairs. These parameters will overwrite existing settings. Enter the parameter you want to configure (e.g., max_connections).
- customClass: mb-10
- - type: input
- label: key
- validation:
- type: required
- schema: key
- - type: editor
- label: value
- hasCopy: false
- validation:
- type: required
- schema: value
- - type: switch
- label: Remove CustomConfig
- fullwidth: true
- schema: schema/properties/spec/properties/configuration/properties/data/properties/removeCustomConfig
- if:
- name: returnFalse
- type: function
+ - type: block-layout
+ label: Config Secret
+ if:
+ name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret|coordinator|true
+ type: function
+ elements:
+ - type: label-element
+ label: Config Secret
+ subtitle: Select a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: schema/properties/spec/properties/configuration/properties/coordinator/properties/configSecret/properties/name
+ refresh: true
+ label: Config Secret
+ loader:
+ name: getConfigSecrets|coordinator
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - temp/properties/coordinator/properties/createSecret/properties/status
+ init:
+ type: func
+ value: setValueFromDbDetails|/spec/topology/coordinator/configSecret/name
+ watcher:
+ func: onCreateSecretChange|coordinator
+ paths:
+ - temp/properties/coordinator/properties/createSecret/properties/status
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|create|coordinator
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/coordinator/properties/configSecret/properties/name
+ - type: block-layout
+ label: Create a New Config Secret
+ showLabels: true
+ if:
+ type: function
+ name: isCreateSecret|coordinator
+ hasButton:
+ text: Save
+ hasCancel: cancelCreateSecret|coordinator
+ action: createNewConfigSecret|coordinator
+ elements:
+ - type: input
+ label: Secret Name
+ schema: temp/properties/coordinator/properties/createSecret/properties/name
+ validation:
+ type: required
+ - type: array-object-form
+ label: String Data
+ schema: temp/properties/coordinator/properties/createSecret/properties/data
+ buttonClass: is-light is-outlined
+ validation:
+ type: required
+ elements:
+ - type: select
+ label: Key
+ schema: key
+ loader:
+ name: onSelectedSecretChange|coordinator
+ watchPaths:
+ - temp/properties/coordinator/properties/createSecret/properties/data
+ validation:
+ type: required
+ - type: textarea
+ label: Value
+ schema: value
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ if:
+ type: function
+ name: isNotCreateSecret|coordinator
+ loader:
+ name: onNewConfigSecretChange|coordinator
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/coordinator/properties/configSecret/properties/name
+ schema: temp/properties/coordinator/newConfigSecret
+ - type: block-layout
+ label: ApplyConfig
+ if:
+ name: ifReconfigurationTypeEqualsTo|applyConfig|coordinator|true
+ type: function
+ elements:
+ - type: label-element
+ label: New Apply Config
+ subtitle: Define custom configurations for your database using key-value pairs. These parameters will overwrite existing settings.
Enter the parameter you want to configure (e.g., max_connections).
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/coordinator/selectedConfiguration
+ refresh: true
+ label: Configuration
+ loader: getConfigSecretsforAppyConfig
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|apply|coordinator
+ watchPaths:
+ - temp/properties/coordinator/selectedConfiguration
+ - type: multi-file-editor
+ editorHeight: 500px
+ schema: temp/properties/coordinator/applyConfig
+ loader:
+ name: setApplyConfig|coordinator
+ watchPaths:
+ - temp/properties/coordinator/selectedConfiguration
+ watcher:
+ func: onApplyconfigChange|coordinator
+ paths:
+ - temp/properties/coordinator/applyConfig
+ - type: block-layout
+ label: Remove
+ if:
+ name: ifReconfigurationTypeEqualsTo|remove|coordinator|true
+ type: function
+ elements:
+ - type: label-element
+ label: Remove
+ subtitle: Selected a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/coordinator/selectedConfigurationRemove
+ refresh: true
+ label: Configuration
+ loader:
+ name: getConfigSecretsforAppyConfig
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|remove|coordinator
+ watchPaths:
+ - temp/properties/coordinator/selectedConfigurationRemove
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ init:
+ type: func
+ value: onRemoveConfigChange|coordinator
+ watcher:
+ func: onRemoveConfigChange|coordinator
+ paths:
+ - temp/properties/coordinator/selectedConfigurationRemove
+ schema: temp/properties/coordinator/removeConfig
+ - type: block-layout
+ label: Data
+ showLabels: true
+ elements:
+ - type: tab-layout
+ label: New Config Secret
+ schema: temp/properties/reconfigurationType-data
+ options:
+ - text: NEW CONFIG SECRET
+ value: selectNewConfigSecret
+ - text: APPLY CONFIG
+ value: applyConfig
+ - text: REMOVE
+ value: remove
+ elements:
+ - type: block-layout
+ label: Config Secret
+ if:
+ name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret|data|true
+ type: function
+ elements:
+ - type: label-element
+ label: Config Secret
+ subtitle: Select a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: schema/properties/spec/properties/configuration/properties/data/properties/configSecret/properties/name
+ refresh: true
+ label: Config Secret
+ loader:
+ name: getConfigSecrets|data
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - temp/properties/data/properties/createSecret/properties/status
+ init:
+ type: func
+ value: setValueFromDbDetails|/spec/topology/data/configSecret/name
+ watcher:
+ func: onCreateSecretChange|data
+ paths:
+ - temp/properties/data/properties/createSecret/properties/status
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|create|data
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/data/properties/configSecret/properties/name
+ - type: block-layout
+ label: Create a New Config Secret
+ showLabels: true
+ if:
+ type: function
+ name: isCreateSecret|data
+ hasButton:
+ text: Save
+ hasCancel: cancelCreateSecret|data
+ action: createNewConfigSecret|data
+ elements:
+ - type: input
+ label: Secret Name
+ schema: temp/properties/data/properties/createSecret/properties/name
+ validation:
+ type: required
+ - type: array-object-form
+ label: String Data
+ schema: temp/properties/data/properties/createSecret/properties/data
+ buttonClass: is-light is-outlined
+ validation:
+ type: required
+ elements:
+ - type: select
+ label: Key
+ schema: key
+ loader:
+ name: onSelectedSecretChange|data
+ watchPaths:
+ - temp/properties/data/properties/createSecret/properties/data
+ validation:
+ type: required
+ - type: textarea
+ label: Value
+ schema: value
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ if:
+ type: function
+ name: isNotCreateSecret|data
+ loader:
+ name: onNewConfigSecretChange|data
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/data/properties/configSecret/properties/name
+ schema: temp/properties/data/newConfigSecret
+ - type: block-layout
+ label: ApplyConfig
+ if:
+ name: ifReconfigurationTypeEqualsTo|applyConfig|data|true
+ type: function
+ elements:
+ - type: label-element
+ label: New Apply Config
+ subtitle: Define custom configurations for your database using key-value pairs. These parameters will overwrite existing settings.
Enter the parameter you want to configure (e.g., max_connections).
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/data/selectedConfiguration
+ refresh: true
+ label: Configuration
+ loader: getConfigSecretsforAppyConfig
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|apply|data
+ watchPaths:
+ - temp/properties/data/selectedConfiguration
+ - type: multi-file-editor
+ editorHeight: 500px
+ schema: temp/properties/data/applyConfig
+ loader:
+ name: setApplyConfig|data
+ watchPaths:
+ - temp/properties/data/selectedConfiguration
+ watcher:
+ func: onApplyconfigChange|data
+ paths:
+ - temp/properties/data/applyConfig
+ - type: block-layout
+ label: Remove
+ if:
+ name: ifReconfigurationTypeEqualsTo|remove|data|true
+ type: function
+ elements:
+ - type: label-element
+ label: Remove
+ subtitle: Selected a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/data/selectedConfigurationRemove
+ refresh: true
+ label: Configuration
+ loader:
+ name: getConfigSecretsforAppyConfig
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|remove|data
+ watchPaths:
+ - temp/properties/data/selectedConfigurationRemove
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ init:
+ type: func
+ value: onRemoveConfigChange|data
+ watcher:
+ func: onRemoveConfigChange|data
+ paths:
+ - temp/properties/data/selectedConfigurationRemove
+ schema: temp/properties/data/removeConfig
- type: block-layout
label: Overseer
showLabels: true
elements:
- - type: radio
- label: Reconfigure Type
- subtitle: Choose the action you'd like to perform. Select a new configuration secret, apply a custom configuration , or remove an existing setup.
- validation:
- type: required
+ - type: tab-layout
+ label: New Config Secret
schema: temp/properties/reconfigurationType-overseer
options:
- - text: New Config Secret
- value: selectNewConfigSecret
- - text: Apply Config
- value: applyConfig
- - text: Remove
- value: remove
- watcher:
- func: onReconfigurationTypeChange|overseer|true
- paths:
- - temp/properties/reconfigurationType-overseer
-
- - type: block-layout
- label: Config Secret
- showLabels: true
- if:
- name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret|overseer|true
- type: function
- elements:
- - type: label-element
- label: Select a configuration secret from the available list to update your database settings
- customClass: mb-10
- - type: select
- addNewButton:
- label: Create Secret
- target: _blank
- url:
- function: createSecretUrl
- schema: schema/properties/spec/properties/configuration/properties/overseer/properties/configSecret/properties/name
- validation:
- type: required
- refresh: true
- label: Config Secret
- loader:
- name: getConfigSecrets
- watchPaths:
- - schema/properties/metadata/properties/namespace
- init:
- type: func
- value: setValueFromDbDetails|/spec/topology/overseer/configSecret/name
- - type: label-element
- label: Selected Config Secret
- loader: getSelectedConfigSecret|overseer
- watcher:
- func: getSelectedConfigSecret|overseer
- paths:
- - schema/properties/spec/properties/configuration/properties/overseer/properties/configSecret/properties/name
- - type: editor
- label: Value
- readonly: true
- hasCopy: false
- if:
- name: isConfigSelected
- type: function
- watcher:
- func: getSelectedConfigSecretValue|overseer
- paths:
- - schema/properties/spec/properties/configuration/properties/overseer/properties/configSecret/properties/name
- validation:
- type: required
- schema: temp/properties/configArray/items/properties/value
- - type: array-object-form
- label: ApplyConfig
- schema: temp/properties/overseer/applyConfig
- buttonClass: is-light is-outlined
- validation:
- type: required
- if:
- name: ifReconfigurationTypeEqualsTo|applyConfig|overseer|true
- type: function
- watcher:
- func: onApplyconfigChange|overseer
- paths:
- - temp/properties/overseer/applyConfig
+ - text: NEW CONFIG SECRET
+ value: selectNewConfigSecret
+ - text: APPLY CONFIG
+ value: applyConfig
+ - text: REMOVE
+ value: remove
elements:
- - type: label-element
- label: New Apply Config
- subtitle: Define custom configurations for your database using key-value pairs. These parameters will overwrite existing settings. Enter the parameter you want to configure (e.g., max_connections).
- customClass: mb-10
- - type: input
- label: key
- validation:
- type: required
- schema: key
- - type: editor
- label: value
- hasCopy: false
- validation:
- type: required
- schema: value
- - type: switch
- label: Remove CustomConfig
- fullwidth: true
- schema: schema/properties/spec/properties/configuration/properties/overseer/properties/removeCustomConfig
- if:
- name: returnFalse
- type: function
+ - type: block-layout
+ label: Config Secret
+ if:
+ name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret|overseer|true
+ type: function
+ elements:
+ - type: label-element
+ label: Config Secret
+ subtitle: Select a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: schema/properties/spec/properties/configuration/properties/overseer/properties/configSecret/properties/name
+ refresh: true
+ label: Config Secret
+ loader:
+ name: getConfigSecrets|overseer
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - temp/properties/overseer/properties/createSecret/properties/status
+ init:
+ type: func
+ value: setValueFromDbDetails|/spec/topology/overseer/configSecret/name
+ watcher:
+ func: onCreateSecretChange|overseer
+ paths:
+ - temp/properties/overseer/properties/createSecret/properties/status
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|create|overseer
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/overseer/properties/configSecret/properties/name
+ - type: block-layout
+ label: Create a New Config Secret
+ showLabels: true
+ if:
+ type: function
+ name: isCreateSecret|overseer
+ hasButton:
+ text: Save
+ hasCancel: cancelCreateSecret|overseer
+ action: createNewConfigSecret|overseer
+ elements:
+ - type: input
+ label: Secret Name
+ schema: temp/properties/overseer/properties/createSecret/properties/name
+ validation:
+ type: required
+ - type: array-object-form
+ label: String Data
+ schema: temp/properties/overseer/properties/createSecret/properties/data
+ buttonClass: is-light is-outlined
+ validation:
+ type: required
+ elements:
+ - type: select
+ label: Key
+ schema: key
+ loader:
+ name: onSelectedSecretChange|overseer
+ watchPaths:
+ - temp/properties/overseer/properties/createSecret/properties/data
+ validation:
+ type: required
+ - type: textarea
+ label: Value
+ schema: value
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ if:
+ type: function
+ name: isNotCreateSecret|overseer
+ loader:
+ name: onNewConfigSecretChange|overseer
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/overseer/properties/configSecret/properties/name
+ schema: temp/properties/overseer/newConfigSecret
+ - type: block-layout
+ label: ApplyConfig
+ if:
+ name: ifReconfigurationTypeEqualsTo|applyConfig|overseer|true
+ type: function
+ elements:
+ - type: label-element
+ label: New Apply Config
+ subtitle: Define custom configurations for your database using key-value pairs. These parameters will overwrite existing settings.
Enter the parameter you want to configure (e.g., max_connections).
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/overseer/selectedConfiguration
+ refresh: true
+ label: Configuration
+ loader: getConfigSecretsforAppyConfig
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|apply|overseer
+ watchPaths:
+ - temp/properties/overseer/selectedConfiguration
+ - type: multi-file-editor
+ editorHeight: 500px
+ schema: temp/properties/overseer/applyConfig
+ loader:
+ name: setApplyConfig|overseer
+ watchPaths:
+ - temp/properties/overseer/selectedConfiguration
+ watcher:
+ func: onApplyconfigChange|overseer
+ paths:
+ - temp/properties/overseer/applyConfig
+ - type: block-layout
+ label: Remove
+ if:
+ name: ifReconfigurationTypeEqualsTo|remove|overseer|true
+ type: function
+ elements:
+ - type: label-element
+ label: Remove
+ subtitle: Selected a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/overseer/selectedConfigurationRemove
+ refresh: true
+ label: Configuration
+ loader:
+ name: getConfigSecretsforAppyConfig
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|remove|overseer
+ watchPaths:
+ - temp/properties/overseer/selectedConfigurationRemove
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ init:
+ type: func
+ value: onRemoveConfigChange|overseer
+ watcher:
+ func: onRemoveConfigChange|overseer
+ paths:
+ - temp/properties/overseer/selectedConfigurationRemove
+ schema: temp/properties/overseer/removeConfig
label: Topology Reconfigure form
if:
name: ifDbTypeEqualsTo|Topology|configuration
diff --git a/charts/opskubedbcom-solropsrequest-editor/ui/functions.js b/charts/opskubedbcom-solropsrequest-editor/ui/functions.js
index 0fb0a2a6ae..150b1928cb 100644
--- a/charts/opskubedbcom-solropsrequest-editor/ui/functions.js
+++ b/charts/opskubedbcom-solropsrequest-editor/ui/functions.js
@@ -1,4 +1,4 @@
-const { axios, useOperator, store } = window.vueHelpers || {}
+const { axios, useOperator, store, useToast } = window.vueHelpers || {}
const machines = {
'db.t.micro': {
resources: {
@@ -306,9 +306,11 @@ const machineList = [
]
let machinesFromPreset = []
+const configSecretKeys = ['solr.xml']
export const useFunc = (model) => {
const route = store.state?.route
+ const toast = useToast()
const { getValue, storeGet, discriminator, setDiscriminatorValue, commit } = useOperator(
model,
@@ -874,29 +876,416 @@ export const useFunc = (model) => {
return machine === 'custom'
}
- // for config secret
- async function getConfigSecrets() {
+ // Fetch and store database Infos
+ // for secret configurations in reconfigure
+ let configSecrets = []
+ let secretConfigData = []
+
+ async function fetchConfigSecrets() {
const owner = storeGet('/route/params/user')
const cluster = storeGet('/route/params/cluster')
const namespace = getValue(model, '/metadata/namespace')
// watchDependency('model#/metadata/namespace')
- const resp = await axios.get(
- `/clusters/${owner}/${cluster}/proxy/core/v1/namespaces/${namespace}/secrets`,
+ const name = getValue(model, '/spec/databaseRef/name')
+ const dbGroup = getValue(model, '/route/params/group')
+ const dbKind = getValue(store.state, '/resource/definition/result/kind')
+ const dbResource = getValue(model, '/route/params/resource')
+ const dbVersion = getValue(model, '/route/params/version')
+
+ try {
+ const resp = await axios.post(
+ `/clusters/${owner}/${cluster}/proxy/ui.kubedb.com/v1alpha1/databaseinfos`,
+ {
+ apiVersion: 'ui.kubedb.com/v1alpha1',
+ kind: 'DatabaseInfo',
+ request: {
+ source: {
+ ref: {
+ name: name,
+ namespace: namespace,
+ },
+ resource: {
+ group: dbGroup,
+ kind: dbKind,
+ name: dbResource,
+ version: dbVersion,
+ },
+ },
+ keys: ['solr.xml'],
+ },
+ },
+ )
+ configSecrets = resp?.data?.response?.availableSecrets || []
+ secretConfigData = resp?.data?.response?.configurations || []
+ } catch (e) {
+ console.log(e)
+ }
+ }
+
+ async function getConfigSecrets(type) {
+ type = type ? type + '/' : ''
+ const secretStatus = getValue(discriminator, `${type}createSecret/status`)
+ if (secretStatus === 'success') {
+ await fetchConfigSecrets()
+ }
+ const mappedSecrets = configSecrets.map((item) => {
+ return { text: item, value: item }
+ })
+ mappedSecrets.push({ text: '+ Create a new Secret', value: 'Create' })
+ return mappedSecrets
+ }
+
+ async function getConfigSecretsforAppyConfig() {
+ const secrets = secretConfigData.map((item) => {
+ return { text: item.componentName, value: item.componentName }
+ })
+ return secrets
+ }
+
+ function getSelectedConfigurationData(type) {
+ type = type ? type + '/' : ''
+ const path = `/${type}selectedConfiguration`
+ const selectedConfiguration = getValue(discriminator, path)
+
+ if (!selectedConfiguration) {
+ return []
+ }
+
+ const configuration = secretConfigData.find(
+ (item) => item.componentName === selectedConfiguration,
)
- const secrets = (resp && resp.data && resp.data.items) || []
- secretArray = secrets
+ if (!configuration) {
+ return []
+ }
- const filteredSecrets = secrets
+ const result = []
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(configuration.data).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedContent = atob(configuration.data[fileName])
+ result.push({
+ name: fileName,
+ content: decodedContent,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ result.push({
+ name: fileName,
+ content: configuration.data[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
- filteredSecrets.map((item) => {
- const name = (item.metadata && item.metadata.name) || ''
- item.text = name
- item.value = name
- return true
+ // Set the value to the model
+ commit('wizard/model$update', {
+ path: `/temp/${type}applyConfig`,
+ value: result,
+ force: true,
+ })
+
+ return result
+ }
+
+ function getSelectedConfigurationName(configType, type) {
+ type = type ? type + '/' : ''
+ let path = ''
+ if (configType === 'create') path = `/spec/configuration/${type}/configSecret/name`
+ else if (configType === 'apply') path = `/${type}selectedConfiguration`
+ else if (configType === 'remove') path = `/${type}selectedConfigurationRemove`
+
+ const selectedConfiguration =
+ configType === 'create' ? getValue(model, path) : getValue(discriminator, path)
+
+ if (selectedConfiguration)
+ return { subtitle: ` You have selected ${selectedConfiguration} secret` }
+ else return { subtitle: 'No secret selected' }
+ }
+
+ function getSelectedConfigurationValueForRemove(type) {
+ type = type ? type + '/' : ''
+ const path = `/${type}selectedConfigurationRemove`
+ const selectedConfiguration = getValue(discriminator, path)
+
+ if (!selectedConfiguration) {
+ return ''
+ }
+
+ const configuration = secretConfigData.find(
+ (item) => item.componentName === selectedConfiguration,
+ )
+
+ if (!configuration) {
+ return ''
+ }
+
+ let data = {}
+ // Decode base64 and parse YAML for each key in the secret data
+ Object.keys(configuration.data).forEach((item) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(configuration.data[item])
+ // Parse YAML string to object
+ const parsedYaml = yaml.load(decodedString)
+ // Store the parsed object with the filename as key
+ data[item] = parsedYaml
+ } catch (e) {
+ console.error(`Error parsing ${item}:`, e)
+ data[item] = atob(configuration.data[item]) // Fallback to decoded string
+ }
})
- return filteredSecrets
+
+ // Convert data object back to YAML string
+ return yaml.dump(data)
+ }
+
+ async function createNewConfigSecret(type) {
+ type = type ? type + '/' : ''
+ const { user, cluster } = route.params
+ const url = `/clusters/${user}/${cluster}/resources`
+ const namespace = storeGet('/route/query/namespace') || getValue(model, '/metadata/namespace')
+ const secretName = getValue(discriminator, `${type}createSecret/name`)
+ const secretData = getValue(discriminator, `${type}createSecret/data`)
+ const secretDataObj = Object.fromEntries(secretData.map((item) => [item.key, item.value]))
+
+ try {
+ const res = await axios.post(url, {
+ apiVersion: 'v1',
+ stringData: secretDataObj,
+ kind: 'Secret',
+ metadata: {
+ name: secretName,
+ namespace: namespace,
+ },
+ type: 'Opaque',
+ })
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'success',
+ })
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/lastCreatedSecret`,
+ value: secretName,
+ })
+ toast.success('Secret created successfully')
+ } catch (error) {
+ const errMsg = decodeError(error, 'Failed to create secret')
+ toast.error(errMsg, { timeout: 5000 })
+ cancelCreateSecret()
+ }
+ }
+
+ function decodeError(msg, defaultMsg) {
+ if (typeof msg === 'string') {
+ return msg || defaultMsg
+ }
+ return (
+ (msg.response && msg.response.data && msg.response.data.message) ||
+ (msg.response && msg.response.data) ||
+ (msg.status && msg.status.status) ||
+ defaultMsg
+ )
+ }
+
+ function isCreateSecret(type) {
+ type = type ? type + '/' : ''
+ const selectedSecret = getValue(model, `spec/configuration/${type}configSecret/name`)
+ const res = selectedSecret === 'Create'
+
+ if (res === true) {
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'pending',
+ })
+ }
+ return res
+ }
+
+ function isNotCreateSecret(type) {
+ return !isCreateSecret(type)
+ }
+
+ function onCreateSecretChange(type) {
+ type = type ? type + '/' : ''
+ const secretStatus = getValue(discriminator, `${type}createSecret/status`)
+ if (secretStatus === 'cancelled') return ''
+ else if (secretStatus === 'success') {
+ const name = getValue(discriminator, `${type}createSecret/lastCreatedSecret`)
+
+ const configFound = configSecrets.find((item) => item === name)
+ return configFound ? { text: name, value: name } : ''
+ }
+ }
+
+ function cancelCreateSecret(type) {
+ type = type ? type + '/' : ''
+ commit('wizard/temp$delete', `${type}createSecret/name`)
+ commit('wizard/temp$delete', `${type}createSecret/data`)
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'cancelled',
+ })
+ }
+
+ async function onApplyconfigChange(type) {
+ type = type ? type + '/' : ''
+ const configValue = getValue(discriminator, `${type}applyConfig`)
+
+ if (!configValue) {
+ commit('wizard/model$delete', `/spec/configuration/${type}applyConfig`)
+ return
+ }
+ const tempConfigObj = {}
+ configValue.forEach((item) => {
+ if (item.name) {
+ tempConfigObj[item.name] = item.content
+ }
+ })
+ if (Object.keys(tempConfigObj).length === 0) {
+ commit('wizard/model$delete', `/spec/configuration/${type}applyConfig`)
+ return
+ }
+ commit('wizard/model$update', {
+ path: `/spec/configuration/${type}applyConfig`,
+ value: tempConfigObj,
+ })
+ }
+
+ function setApplyConfig(type) {
+ type = type ? type + '/' : ''
+ const configPath = `/${type}selectedConfiguration`
+ const selectedConfig = getValue(discriminator, configPath)
+ if (!selectedConfig) {
+ return [{ name: '', content: '' }]
+ }
+ const applyconfigData = secretConfigData.find((item) => {
+ if (item.componentName === selectedConfig) {
+ return item
+ }
+ })
+ const { applyConfig } = applyconfigData
+ const configObj = []
+
+ if (applyConfig) {
+ Object.keys(applyConfig).forEach((fileName) => {
+ configObj.push({
+ name: fileName,
+ content: applyConfig[fileName],
+ })
+ })
+ } else {
+ configObj.push({ name: '', content: '' })
+ }
+ return configObj
+ }
+
+ function onRemoveConfigChange(type) {
+ type = type ? type + '/' : ''
+ const configPath = `/${type}selectedConfigurationRemove`
+ const selectedConfig = getValue(discriminator, configPath)
+
+ if (!selectedConfig) {
+ commit('wizard/model$delete', `/spec/configuration/${type}removeCustomConfig`)
+ return [{ name: '', content: '' }]
+ }
+ commit('wizard/model$update', {
+ path: `/spec/configuration/${type}removeCustomConfig`,
+ value: true,
+ })
+
+ const configuration = secretConfigData.find((item) => item.componentName === selectedConfig)
+
+ if (!configuration.data) {
+ return [{ name: '', content: '' }]
+ }
+
+ const configObj = []
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(configuration.data).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(configuration.data[fileName])
+ configObj.push({
+ name: fileName,
+ content: decodedString,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ configObj.push({
+ name: fileName,
+ content: configuration.data[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
+ return configObj
+ }
+
+ async function onNewConfigSecretChange(type) {
+ type = type ? type + '/' : ''
+ const path = `/spec/configuration/${type}configSecret/name`
+ const selectedSecret = getValue(model, path)
+
+ if (!selectedSecret) {
+ commit('wizard/model$delete', `/spec/configuration/${type}configSecret`)
+ return [{ name: '', content: '' }]
+ }
+ if (selectedSecret === 'Create') return [{ name: '', content: '' }]
+
+ const owner = storeGet('/route/params/user')
+ const cluster = storeGet('/route/params/cluster')
+ const namespace = storeGet('/route/query/namespace') || getValue(model, '/metadata/namespace')
+
+ try {
+ // Fetch the secret data from API
+ const secretResp = await axios.get(
+ `/clusters/${owner}/${cluster}/proxy/core/v1/namespaces/${namespace}/secrets/${selectedSecret}`,
+ )
+
+ const secretData = secretResp.data?.data || {}
+ const configObj = []
+
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(secretData).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(secretData[fileName])
+ configObj.push({
+ name: fileName,
+ content: decodedString,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ configObj.push({
+ name: fileName,
+ content: secretData[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
+
+ return configObj
+ } catch (e) {
+ console.error('Error fetching secret:', e)
+ return [{ name: '', content: '' }]
+ }
+ }
+
+ function onSelectedSecretChange(type, index) {
+ type = type ? type + '/' : ''
+ const secretData = getValue(discriminator, `${type}createSecret/data`) || []
+ const selfSecrets = secretData.map((item) => item.key)
+
+ const remainingSecrets = configSecretKeys.filter((item) => !selfSecrets.includes(item))
+
+ const selfKey = getValue(discriminator, `${type}createSecret/data/${index}/key`)
+ if (selfKey) {
+ remainingSecrets.push(selfKey)
+ }
+ const resSecret = remainingSecrets.map((item) => {
+ return { text: item, value: item }
+ })
+ return resSecret
}
function createSecretUrl() {
@@ -1022,26 +1411,6 @@ export const useFunc = (model) => {
return reconfigurationType === value
}
- function onApplyconfigChange(type) {
- const configPath = `/${type}/applyConfig`
- const applyconfig = getValue(discriminator, configPath)
-
- const configObj = {}
-
- if (applyconfig) {
- applyconfig.forEach((item) => {
- const { key, value } = item
- configObj[key] = value
- })
- }
-
- commit('wizard/model$update', {
- path: `/spec/configuration/${type}/applyConfig`,
- value: configObj,
- force: true,
- })
- }
-
function onReconfigurationTypeChange(property, isShard) {
setDiscriminatorValue(`/${property}/applyConfig`, [])
let path = '/reconfigurationType'
@@ -1450,5 +1819,20 @@ export const useFunc = (model) => {
checkVolume,
getSelectedConfigSecretValue,
objectToYaml,
+ fetchConfigSecrets,
+ getConfigSecretsforAppyConfig,
+ getSelectedConfigurationData,
+ getSelectedConfigurationName,
+ getSelectedConfigurationValueForRemove,
+ createNewConfigSecret,
+ decodeError,
+ isCreateSecret,
+ isNotCreateSecret,
+ onCreateSecretChange,
+ cancelCreateSecret,
+ setApplyConfig,
+ onRemoveConfigChange,
+ onNewConfigSecretChange,
+ onSelectedSecretChange,
}
}
diff --git a/charts/opskubedbcom-zookeeperopsrequest-editor/ui/create-ui.yaml b/charts/opskubedbcom-zookeeperopsrequest-editor/ui/create-ui.yaml
index f2e4a49ea3..00e7239749 100644
--- a/charts/opskubedbcom-zookeeperopsrequest-editor/ui/create-ui.yaml
+++ b/charts/opskubedbcom-zookeeperopsrequest-editor/ui/create-ui.yaml
@@ -264,106 +264,177 @@ step:
if:
name: ifRequestTypeEqualsTo|Reconfigure
type: function
+ loader:
+ name: fetchConfigSecrets
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
elements:
- - type: radio
- label: Reconfigure Type
- subtitle: Choose the action you'd like to perform. Select a new configuration secret, apply a custom configuration, or remove an existing setup.
- validation:
- type: required
- options:
- - text: New Config Secret
- value: selectNewConfigSecret
- - text: Apply Config
- value: applyConfig
- - text: Remove
- value: remove
- schema: temp/properties/reconfigurationType
- watcher:
- func: onReconfigurationTypeChange
- paths:
- - temp/properties/reconfigurationType
- type: block-layout
- label: Config Secret
- showLabels: true
- if:
- name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret
- type: function
+ label: Configuration
elements:
- - type: label-element
- label: Select a configuration secret from the available list to update your database settings
- customClass: mb-10
- - type: select
- addNewButton:
- label: Create a new Secret
- target: _blank
- url:
- function: createSecretUrl
- schema: schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- validation:
- type: required
- refresh: true
- label: Config Secret
- loader:
- name: getConfigSecrets
- watchPaths:
- - schema/properties/metadata/properties/namespace
- init:
- type: func
- value: setValueFromDbDetails|/spec/configSecret/name
- type: label-element
- label: Selected Config Secret
- loader: getSelectedConfigSecret
- watcher:
- func: getSelectedConfigSecret
- paths:
- - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- - type: editor
- label: Value
- readonly: true
- hasCopy: false
- watcher:
- func: getSelectedConfigSecretValue
- paths:
- - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
- validation:
- type: required
- schema: temp/properties/configArray/items/properties/value
- - type: array-object-form
- label: ApplyConfig
- buttonClass: is-light is-outlined
- schema: temp/properties/applyConfig
- validation:
- type: required
- if:
- name: ifReconfigurationTypeEqualsTo|applyConfig
- type: function
- watcher:
- func: onApplyconfigChange
- paths:
- - temp/properties/applyConfig
- elements:
- - type: label-element
- label: New Apply Config
- subtitle: Define custom configurations for your database using key-value pairs. These parameters will overwrite existing settings. Enter the parameter you want to configure (e.g., tickTime, initLimit).
- customClass: mb-10
- - type: input
- label: key
- validation:
- type: required
- schema: key
- - type: editor
- label: value
- hasCopy: false
- validation:
- type: required
- schema: value
- - type: switch
- schema: schema/properties/spec/properties/configuration/properties/removeCustomConfig
- label: Remove CustomConfig
- fullwidth: true
- if:
- name: returnFalse
- type: function
+ label: ''
+ subtitle: Select a new configuration secret, apply a custom configuration, or remove an existing setup to update your database settings
+ - type: tab-layout
+ label: New Config Secret
+ schema: temp/properties/reconfigurationType
+ options:
+ - text: NEW CONFIG SECRET
+ value: selectNewConfigSecret
+ - text: APPLY CONFIG
+ value: applyConfig
+ - text: REMOVE
+ value: remove
+ elements:
+ - type: block-layout
+ label: Config Secret
+ if:
+ name: ifReconfigurationTypeEqualsTo|selectNewConfigSecret
+ type: function
+ elements:
+ - type: label-element
+ label: Config Secret
+ subtitle: Select a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ refresh: true
+ label: Config Secret
+ loader:
+ name: getConfigSecrets
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - temp/properties/createSecret/properties/status
+ init:
+ type: func
+ value: setValueFromDbDetails|/spec/configSecret/name
+ watcher:
+ func: onCreateSecretChange
+ paths:
+ - temp/properties/createSecret/properties/status
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|create
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ - type: block-layout
+ label: Create a New Config Secret
+ showLabels: true
+ if:
+ type: function
+ name: isCreateSecret
+ hasButton:
+ text: Save
+ hasCancel: cancelCreateSecret
+ action: createNewConfigSecret
+ elements:
+ - type: input
+ label: Secret Name
+ schema: temp/properties/createSecret/properties/name
+ validation:
+ type: required
+ - type: array-object-form
+ label: String Data
+ schema: temp/properties/createSecret/properties/data
+ buttonClass: is-light is-outlined
+ validation:
+ type: required
+ elements:
+ - type: select
+ label: Key
+ schema: key
+ loader:
+ name: onSelectedSecretChange
+ watchPaths:
+ - temp/properties/createSecret/properties/data
+ validation:
+ type: required
+ - type: textarea
+ label: Value
+ schema: value
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ if:
+ type: function
+ name: isNotCreateSecret
+ loader:
+ name: onNewConfigSecretChange
+ watchPaths:
+ - schema/properties/spec/properties/configuration/properties/configSecret/properties/name
+ schema: temp/properties/newConfigSecret
+ - type: block-layout
+ label: ApplyConfig
+ if:
+ name: ifReconfigurationTypeEqualsTo|applyConfig
+ type: function
+ elements:
+ - type: label-element
+ label: New Apply Config
+ subtitle: Define custom configurations for your database using key-value pairs. These parameters will overwrite existing settings.
Enter the parameter you want to configure (e.g., max_connections).
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/selectedConfiguration
+ refresh: true
+ label: Configuration
+ loader: getConfigSecretsforAppyConfig
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|apply
+ watchPaths:
+ - temp/properties/selectedConfiguration
+ - type: multi-file-editor
+ editorHeight: 500px
+ schema: temp/properties/applyConfig
+ loader:
+ name: setApplyConfig
+ watchPaths:
+ - temp/properties/selectedConfiguration
+ watcher:
+ func: onApplyconfigChange
+ paths:
+ - temp/properties/applyConfig
+ - type: block-layout
+ label: Remove
+ if:
+ name: ifReconfigurationTypeEqualsTo|remove
+ type: function
+ elements:
+ - type: label-element
+ label: Remove
+ subtitle: Selected a configuration secret from the available list to update your database settings
+ customClass: mb-10
+ - type: select
+ customClass: mb-2
+ schema: temp/properties/selectedConfigurationRemove
+ refresh: true
+ label: Configuration
+ loader:
+ name: getConfigSecretsforAppyConfig
+ watchPaths:
+ - schema/properties/metadata/properties/namespace
+ - type: label-element
+ label: ''
+ loader:
+ name: getSelectedConfigurationName|remove
+ watchPaths:
+ - temp/properties/selectedConfigurationRemove
+ - type: multi-file-editor
+ editorHeight: 500px
+ readonly: true
+ init:
+ type: func
+ value: onRemoveConfigChange
+ watcher:
+ func: onRemoveConfigChange
+ paths:
+ - temp/properties/selectedConfigurationRemove
+ schema: temp/properties/removeConfig
+
# common
- type: block-layout
label: OpsRequest Options
diff --git a/charts/opskubedbcom-zookeeperopsrequest-editor/ui/functions.js b/charts/opskubedbcom-zookeeperopsrequest-editor/ui/functions.js
index eb9a6a7d22..d89c6f517b 100644
--- a/charts/opskubedbcom-zookeeperopsrequest-editor/ui/functions.js
+++ b/charts/opskubedbcom-zookeeperopsrequest-editor/ui/functions.js
@@ -1,4 +1,4 @@
-const { axios, useOperator, store } = window.vueHelpers || {}
+const { axios, useOperator, store, useToast } = window.vueHelpers || {}
const machines = {
'db.t.micro': {
@@ -307,9 +307,11 @@ const machineList = [
]
let machinesFromPreset = []
+const configSecretKeys = ['zoo.cfg']
export const useFunc = (model) => {
const route = store.state?.route
+ const toast = useToast()
const { getValue, storeGet, discriminator, setDiscriminatorValue, commit } = useOperator(
model,
@@ -678,34 +680,419 @@ export const useFunc = (model) => {
return !ver
}
- // for config secret
- let secretArray = []
+ // Fetch and store database Infos
+ // for secret configurations in reconfigure
+ let configSecrets = []
+ let secretConfigData = []
- async function getConfigSecrets() {
+ async function fetchConfigSecrets() {
const owner = storeGet('/route/params/user')
const cluster = storeGet('/route/params/cluster')
const namespace = getValue(model, '/metadata/namespace')
- // watchDependency'model#/metadata/namespace')
+ // watchDependency('model#/metadata/namespace')
- const resp = await axios.get(
- `/clusters/${owner}/${cluster}/proxy/core/v1/namespaces/${namespace}/secrets`,
+ const name = getValue(model, '/spec/databaseRef/name')
+ const dbGroup = getValue(model, '/route/params/group')
+ const dbKind = getValue(store.state, '/resource/definition/result/kind')
+ const dbResource = getValue(model, '/route/params/resource')
+ const dbVersion = getValue(model, '/route/params/version')
+
+ try {
+ const resp = await axios.post(
+ `/clusters/${owner}/${cluster}/proxy/ui.kubedb.com/v1alpha1/databaseinfos`,
+ {
+ apiVersion: 'ui.kubedb.com/v1alpha1',
+ kind: 'DatabaseInfo',
+ request: {
+ source: {
+ ref: {
+ name: name,
+ namespace: namespace,
+ },
+ resource: {
+ group: dbGroup,
+ kind: dbKind,
+ name: dbResource,
+ version: dbVersion,
+ },
+ },
+ keys: ['zoo.cfg'],
+ },
+ },
+ )
+ configSecrets = resp?.data?.response?.availableSecrets || []
+ secretConfigData = resp?.data?.response?.configurations || []
+ } catch (e) {
+ console.log(e)
+ }
+ }
+
+ async function getConfigSecrets(type) {
+ type = type ? type + '/' : ''
+ const secretStatus = getValue(discriminator, `${type}createSecret/status`)
+ if (secretStatus === 'success') {
+ await fetchConfigSecrets()
+ }
+ const mappedSecrets = configSecrets.map((item) => {
+ return { text: item, value: item }
+ })
+ mappedSecrets.push({ text: '+ Create a new Secret', value: 'Create' })
+ return mappedSecrets
+ }
+
+ async function getConfigSecretsforAppyConfig() {
+ const secrets = secretConfigData.map((item) => {
+ return { text: item.componentName, value: item.componentName }
+ })
+ return secrets
+ }
+
+ function getSelectedConfigurationData(type) {
+ type = type ? type + '/' : ''
+ const path = `/${type}selectedConfiguration`
+ const selectedConfiguration = getValue(discriminator, path)
+
+ if (!selectedConfiguration) {
+ return []
+ }
+
+ const configuration = secretConfigData.find(
+ (item) => item.componentName === selectedConfiguration,
)
- const secrets = (resp && resp.data && resp.data.items) || []
+ if (!configuration) {
+ return []
+ }
- const filteredSecrets = secrets
+ const result = []
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(configuration.data).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedContent = atob(configuration.data[fileName])
+ result.push({
+ name: fileName,
+ content: decodedContent,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ result.push({
+ name: fileName,
+ content: configuration.data[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
- filteredSecrets.map((item) => {
- const name = (item.metadata && item.metadata.name) || ''
- item.text = name
- item.value = name
- return true
+ // Set the value to the model
+ commit('wizard/model$update', {
+ path: `/temp/${type}applyConfig`,
+ value: result,
+ force: true,
+ })
+
+ return result
+ }
+
+ function getSelectedConfigurationName(configType, type) {
+ type = type ? type + '/' : ''
+ let path = ''
+ if (configType === 'create') path = `/spec/configuration/${type}/configSecret/name`
+ else if (configType === 'apply') path = `/${type}selectedConfiguration`
+ else if (configType === 'remove') path = `/${type}selectedConfigurationRemove`
+
+ const selectedConfiguration =
+ configType === 'create' ? getValue(model, path) : getValue(discriminator, path)
+
+ if (selectedConfiguration)
+ return { subtitle: ` You have selected ${selectedConfiguration} secret` }
+ else return { subtitle: 'No secret selected' }
+ }
+
+ function getSelectedConfigurationValueForRemove(type) {
+ type = type ? type + '/' : ''
+ const path = `/${type}selectedConfigurationRemove`
+ const selectedConfiguration = getValue(discriminator, path)
+
+ if (!selectedConfiguration) {
+ return ''
+ }
+
+ const configuration = secretConfigData.find(
+ (item) => item.componentName === selectedConfiguration,
+ )
+
+ if (!configuration) {
+ return ''
+ }
+
+ let data = {}
+ // Decode base64 and parse YAML for each key in the secret data
+ Object.keys(configuration.data).forEach((item) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(configuration.data[item])
+ // Parse YAML string to object
+ const parsedYaml = yaml.load(decodedString)
+ // Store the parsed object with the filename as key
+ data[item] = parsedYaml
+ } catch (e) {
+ console.error(`Error parsing ${item}:`, e)
+ data[item] = atob(configuration.data[item]) // Fallback to decoded string
+ }
+ })
+
+ // Convert data object back to YAML string
+ return yaml.dump(data)
+ }
+
+ async function createNewConfigSecret(type) {
+ type = type ? type + '/' : ''
+ const { user, cluster } = route.params
+ const url = `/clusters/${user}/${cluster}/resources`
+ const namespace = storeGet('/route/query/namespace') || getValue(model, '/metadata/namespace')
+ const secretName = getValue(discriminator, `${type}createSecret/name`)
+ const secretData = getValue(discriminator, `${type}createSecret/data`)
+ const secretDataObj = Object.fromEntries(secretData.map((item) => [item.key, item.value]))
+
+ try {
+ const res = await axios.post(url, {
+ apiVersion: 'v1',
+ stringData: secretDataObj,
+ kind: 'Secret',
+ metadata: {
+ name: secretName,
+ namespace: namespace,
+ },
+ type: 'Opaque',
+ })
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'success',
+ })
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/lastCreatedSecret`,
+ value: secretName,
+ })
+ toast.success('Secret created successfully')
+ } catch (error) {
+ const errMsg = decodeError(error, 'Failed to create secret')
+ toast.error(errMsg, { timeout: 5000 })
+ cancelCreateSecret()
+ }
+ }
+
+ function decodeError(msg, defaultMsg) {
+ if (typeof msg === 'string') {
+ return msg || defaultMsg
+ }
+ return (
+ (msg.response && msg.response.data && msg.response.data.message) ||
+ (msg.response && msg.response.data) ||
+ (msg.status && msg.status.status) ||
+ defaultMsg
+ )
+ }
+
+ function isCreateSecret(type) {
+ type = type ? type + '/' : ''
+ const selectedSecret = getValue(model, `spec/configuration/${type}configSecret/name`)
+ const res = selectedSecret === 'Create'
+
+ if (res === true) {
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'pending',
+ })
+ }
+ return res
+ }
+
+ function isNotCreateSecret(type) {
+ return !isCreateSecret(type)
+ }
+
+ function onCreateSecretChange(type) {
+ type = type ? type + '/' : ''
+ const secretStatus = getValue(discriminator, `${type}createSecret/status`)
+ if (secretStatus === 'cancelled') return ''
+ else if (secretStatus === 'success') {
+ const name = getValue(discriminator, `${type}createSecret/lastCreatedSecret`)
+
+ const configFound = configSecrets.find((item) => item === name)
+ return configFound ? { text: name, value: name } : ''
+ }
+ }
+
+ function cancelCreateSecret(type) {
+ type = type ? type + '/' : ''
+ commit('wizard/temp$delete', `${type}createSecret/name`)
+ commit('wizard/temp$delete', `${type}createSecret/data`)
+ commit('wizard/temp$update', {
+ path: `${type}createSecret/status`,
+ value: 'cancelled',
+ })
+ }
+
+ async function onApplyconfigChange(type) {
+ type = type ? type + '/' : ''
+ const configValue = getValue(discriminator, `${type}applyConfig`)
+
+ if (!configValue) {
+ commit('wizard/model$delete', `/spec/configuration/${type}applyConfig`)
+ return
+ }
+ const tempConfigObj = {}
+ configValue.forEach((item) => {
+ if (item.name) {
+ tempConfigObj[item.name] = item.content
+ }
+ })
+ if (Object.keys(tempConfigObj).length === 0) {
+ commit('wizard/model$delete', `/spec/configuration/${type}applyConfig`)
+ return
+ }
+ commit('wizard/model$update', {
+ path: `/spec/configuration/${type}applyConfig`,
+ value: tempConfigObj,
+ })
+ }
+
+ function setApplyConfig(type) {
+ type = type ? type + '/' : ''
+ const configPath = `/${type}selectedConfiguration`
+ const selectedConfig = getValue(discriminator, configPath)
+ if (!selectedConfig) {
+ return [{ name: '', content: '' }]
+ }
+ const applyconfigData = secretConfigData.find((item) => {
+ if (item.componentName === selectedConfig) {
+ return item
+ }
+ })
+ const { applyConfig } = applyconfigData
+ const configObj = []
+
+ if (applyConfig) {
+ Object.keys(applyConfig).forEach((fileName) => {
+ configObj.push({
+ name: fileName,
+ content: applyConfig[fileName],
+ })
+ })
+ } else {
+ configObj.push({ name: '', content: '' })
+ }
+ return configObj
+ }
+
+ function onRemoveConfigChange(type) {
+ type = type ? type + '/' : ''
+ const configPath = `/${type}selectedConfigurationRemove`
+ const selectedConfig = getValue(discriminator, configPath)
+
+ if (!selectedConfig) {
+ commit('wizard/model$delete', `/spec/configuration/${type}removeCustomConfig`)
+ return [{ name: '', content: '' }]
+ }
+ commit('wizard/model$update', {
+ path: `/spec/configuration/${type}removeCustomConfig`,
+ value: true,
+ })
+
+ const configuration = secretConfigData.find((item) => item.componentName === selectedConfig)
+
+ if (!configuration.data) {
+ return [{ name: '', content: '' }]
+ }
+
+ const configObj = []
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(configuration.data).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(configuration.data[fileName])
+ configObj.push({
+ name: fileName,
+ content: decodedString,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ configObj.push({
+ name: fileName,
+ content: configuration.data[fileName], // Fallback to original if decode fails
+ })
+ }
})
+ return configObj
+ }
+
+ async function onNewConfigSecretChange(type) {
+ type = type ? type + '/' : ''
+ const path = `/spec/configuration/${type}configSecret/name`
+ const selectedSecret = getValue(model, path)
+
+ if (!selectedSecret) {
+ commit('wizard/model$delete', `/spec/configuration/${type}configSecret`)
+ return [{ name: '', content: '' }]
+ }
+ if (selectedSecret === 'Create') return [{ name: '', content: '' }]
+
+ const owner = storeGet('/route/params/user')
+ const cluster = storeGet('/route/params/cluster')
+ const namespace = storeGet('/route/query/namespace') || getValue(model, '/metadata/namespace')
+
+ try {
+ // Fetch the secret data from API
+ const secretResp = await axios.get(
+ `/clusters/${owner}/${cluster}/proxy/core/v1/namespaces/${namespace}/secrets/${selectedSecret}`,
+ )
+
+ const secretData = secretResp.data?.data || {}
+ const configObj = []
+
+ // Decode base64 and format as array of objects with name and content
+ Object.keys(secretData).forEach((fileName) => {
+ try {
+ // Decode base64 string
+ const decodedString = atob(secretData[fileName])
+ configObj.push({
+ name: fileName,
+ content: decodedString,
+ })
+ } catch (e) {
+ console.error(`Error decoding ${fileName}:`, e)
+ configObj.push({
+ name: fileName,
+ content: secretData[fileName], // Fallback to original if decode fails
+ })
+ }
+ })
- secretArray = filteredSecrets
- return filteredSecrets
+ return configObj
+ } catch (e) {
+ console.error('Error fetching secret:', e)
+ return [{ name: '', content: '' }]
+ }
+ }
+
+ function onSelectedSecretChange(index) {
+ const secretData = getValue(discriminator, 'createSecret/data') || []
+ const selfSecrets = secretData.map((item) => item.key)
+
+ const remainingSecrets = configSecretKeys.filter((item) => !selfSecrets.includes(item))
+
+ const selfKey = getValue(discriminator, `createSecret/data/${index}/key`)
+ if (selfKey) {
+ remainingSecrets.push(selfKey)
+ }
+ const resSecret = remainingSecrets.map((item) => {
+ return { text: item, value: item }
+ })
+ return resSecret
}
+ let secretArray = []
+
function objectToYaml(obj, indent = 0) {
if (obj === null || obj === undefined) return 'null'
if (typeof obj !== 'object') return JSON.stringify(obj)
@@ -887,24 +1274,6 @@ export const useFunc = (model) => {
return reconfigurationType === value
}
- function onApplyconfigChange() {
- const applyconfig = getValue(discriminator, '/applyConfig')
-
- const configObj = {}
- if (applyconfig) {
- applyconfig.forEach((item) => {
- const { key, value } = item
- configObj[key] = value
- })
- }
-
- commit('wizard/model$update', {
- path: '/spec/configuration/applyConfig',
- value: configObj,
- force: true,
- })
- }
-
function onReconfigurationTypeChange() {
const reconfigurationType = getValue(discriminator, '/reconfigurationType')
setDiscriminatorValue('/applyConfig', [])
@@ -1249,5 +1618,20 @@ export const useFunc = (model) => {
onMachineChange,
isMachineCustom,
checkVolume,
+ fetchConfigSecrets,
+ getConfigSecretsforAppyConfig,
+ getSelectedConfigurationData,
+ getSelectedConfigurationName,
+ getSelectedConfigurationValueForRemove,
+ createNewConfigSecret,
+ decodeError,
+ isCreateSecret,
+ isNotCreateSecret,
+ onCreateSecretChange,
+ cancelCreateSecret,
+ setApplyConfig,
+ onRemoveConfigChange,
+ onNewConfigSecretChange,
+ onSelectedSecretChange,
}
}
diff --git a/schemas/ui-schema.json b/schemas/ui-schema.json
index c5f02feb89..3176b085f8 100644
--- a/schemas/ui-schema.json
+++ b/schemas/ui-schema.json
@@ -500,7 +500,7 @@
"type": "string"
},
"loader": {
- "type": "string"
+ "$ref": "#/definitions/LoaderType"
},
"showLabels": {
"type": "boolean"
@@ -676,7 +676,7 @@
"type": "string"
},
"loader": {
- "type": "string"
+ "$ref": "#/definitions/LoaderType"
},
"showLabels": {
"type": "boolean"
@@ -1479,7 +1479,7 @@
"type": "string"
},
"loader": {
- "type": "string"
+ "$ref": "#/definitions/LoaderType"
},
"options": {
"$ref": "#/definitions/Options"