mirror of
https://github.com/OpenNebula/one.git
synced 2024-12-22 13:33:52 +03:00
F OpenNebula/one#6117: Marketplace tab (#2975)
Signed-off-by: David Carracedo <dcarracedo@opennebula.io>
This commit is contained in:
parent
868597a4b3
commit
45bcac8e1c
@ -2996,7 +2996,8 @@ FIREEDGE_SUNSTONE_ETC_VIEW_ADMIN="src/fireedge/etc/sunstone/admin/vm-tab.yaml \
|
||||
src/fireedge/etc/sunstone/admin/acl-tab.yaml \
|
||||
src/fireedge/etc/sunstone/admin/cluster-tab.yaml \
|
||||
src/fireedge/etc/sunstone/admin/support-tab.yaml \
|
||||
src/fireedge/etc/sunstone/admin/zone-tab.yaml"
|
||||
src/fireedge/etc/sunstone/admin/zone-tab.yaml \
|
||||
src/fireedge/etc/sunstone/admin/marketplace-tab.yaml"
|
||||
|
||||
FIREEDGE_SUNSTONE_ETC_VIEW_USER="src/fireedge/etc/sunstone/user/vm-tab.yaml \
|
||||
src/fireedge/etc/sunstone/user/vm-template-tab.yaml \
|
||||
|
@ -13,8 +13,8 @@ actions:
|
||||
chown: true
|
||||
chgrp: true
|
||||
delete: true
|
||||
lock: true
|
||||
unlock: true
|
||||
disable: true
|
||||
enable: true
|
||||
|
||||
# Filters - List of criteria to filter the resources
|
||||
|
||||
@ -47,5 +47,5 @@ info-tabs:
|
||||
edit: true
|
||||
delete: true
|
||||
|
||||
app:
|
||||
apps:
|
||||
enabled: true
|
||||
|
@ -174,9 +174,17 @@ const CreateDockerfile = loadable(
|
||||
ssr: false,
|
||||
}
|
||||
)
|
||||
|
||||
// Marketplace
|
||||
const Marketplaces = loadable(() => import('client/containers/Marketplaces'), {
|
||||
ssr: false,
|
||||
})
|
||||
const CreateMarketplace = loadable(
|
||||
() => import('client/containers/Marketplaces/Create'),
|
||||
{ ssr: false }
|
||||
)
|
||||
|
||||
// Marketplace app
|
||||
const MarketplaceApps = loadable(
|
||||
() => import('client/containers/MarketplaceApps'),
|
||||
{ ssr: false }
|
||||
@ -185,6 +193,12 @@ const CreateMarketplaceApp = loadable(
|
||||
() => import('client/containers/MarketplaceApps/Create'),
|
||||
{ ssr: false }
|
||||
)
|
||||
const MarketplaceAppDetail = loadable(
|
||||
() => import('client/containers/MarketplaceApps/Detail'),
|
||||
{
|
||||
ssr: false,
|
||||
}
|
||||
)
|
||||
|
||||
const VirtualNetworks = loadable(
|
||||
() => import('client/containers/VirtualNetworks'),
|
||||
@ -356,6 +370,7 @@ export const PATH = {
|
||||
MARKETPLACES: {
|
||||
LIST: `/${RESOURCE_NAMES.MARKETPLACE}`,
|
||||
DETAIL: `/${RESOURCE_NAMES.MARKETPLACE}/:id`,
|
||||
CREATE: `/${RESOURCE_NAMES.MARKETPLACE}/create`,
|
||||
},
|
||||
MARKETPLACE_APPS: {
|
||||
LIST: `/${RESOURCE_NAMES.APP}`,
|
||||
@ -625,6 +640,14 @@ const ENDPOINTS = [
|
||||
icon: MarketplaceIcon,
|
||||
Component: Marketplaces,
|
||||
},
|
||||
{
|
||||
title: (_, state) =>
|
||||
state?.ID !== undefined ? T.UpdateMarketplace : T.CreateMarketplace,
|
||||
description: (_, state) =>
|
||||
state?.ID !== undefined && `#${state.ID} ${state.NAME}`,
|
||||
path: PATH.STORAGE.MARKETPLACES.CREATE,
|
||||
Component: CreateMarketplace,
|
||||
},
|
||||
{
|
||||
title: T.Apps,
|
||||
path: PATH.STORAGE.MARKETPLACE_APPS.LIST,
|
||||
@ -637,6 +660,12 @@ const ENDPOINTS = [
|
||||
path: PATH.STORAGE.MARKETPLACE_APPS.CREATE,
|
||||
Component: CreateMarketplaceApp,
|
||||
},
|
||||
{
|
||||
title: T.App,
|
||||
description: (params) => `#${params?.id}`,
|
||||
path: PATH.STORAGE.MARKETPLACE_APPS.DETAIL,
|
||||
Component: MarketplaceAppDetail,
|
||||
},
|
||||
{
|
||||
title: T.CreateBackupJob,
|
||||
path: PATH.STORAGE.BACKUPJOBS.CREATE,
|
||||
|
@ -21,6 +21,7 @@ import {
|
||||
Group,
|
||||
Server,
|
||||
WarningCircledOutline as WarningIcon,
|
||||
MinusPinAlt as ZoneIcon,
|
||||
} from 'iconoir-react'
|
||||
import { Box, Typography, Tooltip } from '@mui/material'
|
||||
|
||||
@ -47,7 +48,8 @@ const MarketplaceCard = memo(
|
||||
({ market, rootProps, actions }) => {
|
||||
const classes = rowStyles()
|
||||
|
||||
const { ID, NAME, UNAME, GNAME, MARKET_MAD, MARKETPLACEAPPS } = market
|
||||
const { ID, NAME, UNAME, GNAME, MARKET_MAD, MARKETPLACEAPPS, ZONE_ID } =
|
||||
market
|
||||
|
||||
const { color: stateColor, name: stateName } = getState(market)
|
||||
const error = useMemo(() => getErrorMessage(market), [market])
|
||||
@ -96,6 +98,10 @@ const MarketplaceCard = memo(
|
||||
<Server />
|
||||
<span>{` ${apps}`}</span>
|
||||
</span>
|
||||
<span title={`${Tr(T.Zone)}: ${ZONE_ID}`}>
|
||||
<ZoneIcon />
|
||||
<span>{` ${ZONE_ID}`}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classes.secondary}>
|
||||
|
@ -111,10 +111,7 @@ const Steps = createSteps([General, Hosts, Vnets, Datastores], {
|
||||
)
|
||||
|
||||
// Check if the name has been changed
|
||||
const changeName =
|
||||
initialValues?.NAME === formData?.general?.NAME
|
||||
? undefined
|
||||
: formData?.general?.NAME
|
||||
const changeName = initialValues?.NAME === formData?.general?.NAME
|
||||
|
||||
return {
|
||||
...formData,
|
||||
|
@ -0,0 +1,91 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { INPUT_TYPES, T, MARKET_TYPES } from 'client/constants'
|
||||
import { string } from 'yup'
|
||||
import { Field } from 'client/utils'
|
||||
|
||||
/** @type {Field} BASE_URL field */
|
||||
const BASE_URL = {
|
||||
name: 'BASE_URL',
|
||||
label: (type) => {
|
||||
if (type === MARKET_TYPES.HTTP.value)
|
||||
return T['marketplace.form.configuration.http.url']
|
||||
else if (type === MARKET_TYPES.DOCKER_REGISTRY.value)
|
||||
return T['marketplace.form.configuration.dockerRegistry.url']
|
||||
},
|
||||
type: INPUT_TYPES.TEXT,
|
||||
dependOf: '$general.MARKET_MAD',
|
||||
htmlType: (type) =>
|
||||
type !== MARKET_TYPES.HTTP.value &&
|
||||
type !== MARKET_TYPES.DOCKER_REGISTRY.value &&
|
||||
INPUT_TYPES.HIDDEN,
|
||||
validation: string()
|
||||
.trim()
|
||||
.when('$general.MARKET_MAD', (type, schema) => {
|
||||
if (type)
|
||||
return type !== MARKET_TYPES.HTTP.value &&
|
||||
type !== MARKET_TYPES.DOCKER_REGISTRY.value
|
||||
? schema.strip()
|
||||
: schema.required()
|
||||
})
|
||||
.default(() => undefined),
|
||||
grid: { md: 12 },
|
||||
fieldProps: { placeholder: 'http://frontend.opennebula.org/' },
|
||||
}
|
||||
|
||||
/** @type {Field} ENDPOINT field */
|
||||
const ENDPOINT = {
|
||||
name: 'ENDPOINT',
|
||||
label: (type) => {
|
||||
if (type === MARKET_TYPES.S3.value)
|
||||
return T['marketplace.form.configuration.s3.endpoint']
|
||||
else if (type === MARKET_TYPES.OPENNEBULA.value)
|
||||
return T['marketplace.form.configuration.one.url']
|
||||
},
|
||||
tooltip: (type) => {
|
||||
if (type === MARKET_TYPES.S3.value)
|
||||
return T['marketplace.form.configuration.s3.endpoint.tooltip']
|
||||
},
|
||||
type: INPUT_TYPES.TEXT,
|
||||
dependOf: '$general.MARKET_MAD',
|
||||
htmlType: (type) =>
|
||||
type !== MARKET_TYPES.S3.value &&
|
||||
type !== MARKET_TYPES.OPENNEBULA.value &&
|
||||
INPUT_TYPES.HIDDEN,
|
||||
validation: string()
|
||||
.trim()
|
||||
.when(
|
||||
['$general.MARKET_MAD', '$configuration.AWS'],
|
||||
(type, aws, schema) => {
|
||||
if (type) {
|
||||
if (
|
||||
type === MARKET_TYPES.OPENNEBULA.value ||
|
||||
(type === MARKET_TYPES.S3.value && !aws)
|
||||
)
|
||||
return schema.required()
|
||||
else if (type === MARKET_TYPES.S3.value && aws)
|
||||
return schema.notRequired()
|
||||
else return schema.strip()
|
||||
}
|
||||
}
|
||||
)
|
||||
.default(() => undefined),
|
||||
grid: { md: 12 },
|
||||
}
|
||||
|
||||
const FIELDS = [BASE_URL, ENDPOINT]
|
||||
|
||||
export { FIELDS }
|
@ -0,0 +1,43 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { INPUT_TYPES, T, MARKET_TYPES } from 'client/constants'
|
||||
import { boolean } from 'yup'
|
||||
import { Field } from 'client/utils'
|
||||
|
||||
/** @type {Field} SSL field */
|
||||
const SSL = {
|
||||
name: 'SSL',
|
||||
label: T['marketplace.form.configuration.dockerRegistry.ssl'],
|
||||
tooltip: T['marketplace.form.configuration.dockerRegistry.ssl.tooltip'],
|
||||
type: INPUT_TYPES.SWITCH,
|
||||
dependOf: '$general.MARKET_MAD',
|
||||
htmlType: (type) =>
|
||||
type !== MARKET_TYPES.DOCKER_REGISTRY.value && INPUT_TYPES.HIDDEN,
|
||||
validation: boolean()
|
||||
.yesOrNo()
|
||||
.afterSubmit((value, { context }) => {
|
||||
if (context?.general?.MARKET_MAD === MARKET_TYPES.DOCKER_REGISTRY.value) {
|
||||
return value ? 'YES' : 'NO'
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
}),
|
||||
grid: { xs: 12, md: 6 },
|
||||
}
|
||||
|
||||
const FIELDS = [SSL]
|
||||
|
||||
export { FIELDS }
|
@ -0,0 +1,64 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { INPUT_TYPES, T, MARKET_TYPES } from 'client/constants'
|
||||
import { string, array } from 'yup'
|
||||
import { Field } from 'client/utils'
|
||||
|
||||
/** @type {Field} PUBLIC_DIR field */
|
||||
const PUBLIC_DIR = {
|
||||
name: 'PUBLIC_DIR',
|
||||
label: T['marketplace.form.configuration.http.path'],
|
||||
tooltip: T['marketplace.form.configuration.http.path.tooltip'],
|
||||
type: INPUT_TYPES.TEXT,
|
||||
dependOf: '$general.MARKET_MAD',
|
||||
htmlType: (type) => type !== MARKET_TYPES.HTTP.value && INPUT_TYPES.HIDDEN,
|
||||
validation: string()
|
||||
.trim()
|
||||
.when('$general.MARKET_MAD', (type, schema) => {
|
||||
if (type)
|
||||
return type !== MARKET_TYPES.HTTP.value
|
||||
? schema.strip()
|
||||
: schema.required()
|
||||
})
|
||||
.default(() => undefined),
|
||||
grid: { md: 12 },
|
||||
fieldProps: { placeholder: '/var/local/market-http' },
|
||||
}
|
||||
|
||||
/** @type {Field} BRIDGE_LIST field */
|
||||
const BRIDGE_LIST = {
|
||||
name: 'BRIDGE_LIST',
|
||||
label: T['marketplace.form.configuration.http.bridge'],
|
||||
tooltip: [T.PressKeysToAddAValue, ['ENTER']],
|
||||
type: INPUT_TYPES.AUTOCOMPLETE,
|
||||
multiple: true,
|
||||
dependOf: '$general.MARKET_MAD',
|
||||
htmlType: (type) => type !== MARKET_TYPES.HTTP.value && INPUT_TYPES.HIDDEN,
|
||||
validation: array(string().trim())
|
||||
.notRequired()
|
||||
.default(() => undefined)
|
||||
.afterSubmit((value, { context }) =>
|
||||
context?.general?.MARKET_MAD === MARKET_TYPES.HTTP.value && value
|
||||
? value.join(' ')
|
||||
: undefined
|
||||
),
|
||||
grid: { md: 12 },
|
||||
fieldProps: { freeSolo: true },
|
||||
}
|
||||
|
||||
const FIELDS = [PUBLIC_DIR, BRIDGE_LIST]
|
||||
|
||||
export { FIELDS }
|
@ -0,0 +1,207 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { INPUT_TYPES, T, MARKET_TYPES } from 'client/constants'
|
||||
import { string, boolean, number } from 'yup'
|
||||
import { Field } from 'client/utils'
|
||||
|
||||
/** @type {Field} AWS field */
|
||||
const AWS = {
|
||||
name: 'AWS',
|
||||
label: T['marketplace.form.configuration.s3.aws'],
|
||||
tooltip: T['marketplace.form.configuration.s3.aws.tooltip'],
|
||||
type: INPUT_TYPES.SWITCH,
|
||||
dependOf: '$general.MARKET_MAD',
|
||||
htmlType: (type) => type !== MARKET_TYPES.S3.value && INPUT_TYPES.HIDDEN,
|
||||
validation: boolean()
|
||||
.yesOrNo()
|
||||
.afterSubmit((value, { context }) => {
|
||||
if (context?.general?.MARKET_MAD === MARKET_TYPES.S3.value) {
|
||||
return value ? 'YES' : 'NO'
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
})
|
||||
.default(() => true),
|
||||
grid: { xs: 12, md: 6 },
|
||||
}
|
||||
|
||||
/** @type {Field} ACCESS_KEY_ID field */
|
||||
const ACCESS_KEY_ID = {
|
||||
name: 'ACCESS_KEY_ID',
|
||||
label: T['marketplace.form.configuration.s3.accessKey'],
|
||||
tooltip: T['marketplace.form.configuration.s3.accessKey.tooltip'],
|
||||
type: INPUT_TYPES.TEXT,
|
||||
dependOf: '$general.MARKET_MAD',
|
||||
htmlType: (type) => type !== MARKET_TYPES.S3.value && INPUT_TYPES.HIDDEN,
|
||||
validation: string()
|
||||
.trim()
|
||||
.when(
|
||||
'$general.MARKET_MAD',
|
||||
(type, schema) =>
|
||||
type &&
|
||||
(type !== MARKET_TYPES.S3.value ? schema.strip() : schema.required())
|
||||
)
|
||||
.default(() => undefined),
|
||||
grid: { md: 12 },
|
||||
}
|
||||
|
||||
/** @type {Field} SECRET_ACCESS_KEY field */
|
||||
const SECRET_ACCESS_KEY = {
|
||||
name: 'SECRET_ACCESS_KEY',
|
||||
label: T['marketplace.form.configuration.s3.secretAccessKey'],
|
||||
tooltip: T['marketplace.form.configuration.s3.secretAccessKey.tooltip'],
|
||||
type: INPUT_TYPES.TEXT,
|
||||
dependOf: '$general.MARKET_MAD',
|
||||
htmlType: (type) => type !== MARKET_TYPES.S3.value && INPUT_TYPES.HIDDEN,
|
||||
validation: string()
|
||||
.trim()
|
||||
.when(
|
||||
'$general.MARKET_MAD',
|
||||
(type, schema) =>
|
||||
type &&
|
||||
(type !== MARKET_TYPES.S3.value ? schema.strip() : schema.required())
|
||||
)
|
||||
.default(() => undefined),
|
||||
grid: { md: 12 },
|
||||
}
|
||||
|
||||
/** @type {Field} BUCKET field */
|
||||
const BUCKET = {
|
||||
name: 'BUCKET',
|
||||
label: T['marketplace.form.configuration.s3.bucket'],
|
||||
tooltip: T['marketplace.form.configuration.s3.bucket.tooltip'],
|
||||
type: INPUT_TYPES.TEXT,
|
||||
dependOf: '$general.MARKET_MAD',
|
||||
htmlType: (type) => type !== MARKET_TYPES.S3.value && INPUT_TYPES.HIDDEN,
|
||||
validation: string()
|
||||
.trim()
|
||||
.when(
|
||||
'$general.MARKET_MAD',
|
||||
(type, schema) =>
|
||||
type &&
|
||||
(type !== MARKET_TYPES.S3.value ? schema.strip() : schema.required())
|
||||
)
|
||||
.default(() => undefined),
|
||||
grid: { md: 12 },
|
||||
}
|
||||
|
||||
/** @type {Field} REGION field */
|
||||
const REGION = {
|
||||
name: 'REGION',
|
||||
label: T['marketplace.form.configuration.s3.region'],
|
||||
tooltip: T['marketplace.form.configuration.s3.region.tooltip'],
|
||||
type: INPUT_TYPES.TEXT,
|
||||
dependOf: '$general.MARKET_MAD',
|
||||
htmlType: (type) => type !== MARKET_TYPES.S3.value && INPUT_TYPES.HIDDEN,
|
||||
validation: string()
|
||||
.trim()
|
||||
.when(
|
||||
'$general.MARKET_MAD',
|
||||
(type, schema) =>
|
||||
type &&
|
||||
(type !== MARKET_TYPES.S3.value ? schema.strip() : schema.required())
|
||||
)
|
||||
.default(() => undefined),
|
||||
grid: { md: 12 },
|
||||
}
|
||||
|
||||
/** @type {Field} SIGNATURE_VERSION field */
|
||||
const SIGNATURE_VERSION = {
|
||||
name: 'SIGNATURE_VERSION',
|
||||
type: INPUT_TYPES.TEXT,
|
||||
htmlType: INPUT_TYPES.HIDDEN,
|
||||
validation: string()
|
||||
.trim()
|
||||
.afterSubmit((value, { context }) =>
|
||||
context?.general?.MARKET_MAD === MARKET_TYPES.S3.value &&
|
||||
!context?.configuration?.AWS
|
||||
? 's3'
|
||||
: undefined
|
||||
)
|
||||
.default(() => undefined),
|
||||
grid: { md: 12 },
|
||||
}
|
||||
|
||||
/** @type {Field} FORCE_PATH_STYLE field */
|
||||
const FORCE_PATH_STYLE = {
|
||||
name: 'FORCE_PATH_STYLE',
|
||||
type: INPUT_TYPES.TEXT,
|
||||
htmlType: INPUT_TYPES.HIDDEN,
|
||||
validation: string()
|
||||
.trim()
|
||||
.afterSubmit((value, { context }) =>
|
||||
context?.general?.MARKET_MAD === MARKET_TYPES.S3.value &&
|
||||
!context?.configuration?.AWS
|
||||
? 'YES'
|
||||
: undefined
|
||||
)
|
||||
.default(() => undefined),
|
||||
grid: { md: 12 },
|
||||
}
|
||||
|
||||
/** @type {Field} TOTAL_MB field */
|
||||
const TOTAL_MB = {
|
||||
name: 'TOTAL_MB',
|
||||
label: T['marketplace.form.configuration.s3.totalMB'],
|
||||
tooltip: T['marketplace.form.configuration.s3.totalMB.tooltip'],
|
||||
type: INPUT_TYPES.TEXT,
|
||||
dependOf: '$general.MARKET_MAD',
|
||||
htmlType: (type) =>
|
||||
type !== MARKET_TYPES.S3.value ? INPUT_TYPES.HIDDEN : 'number',
|
||||
validation: number()
|
||||
.when(
|
||||
'$general.MARKET_MAD',
|
||||
(type, schema) =>
|
||||
type &&
|
||||
(type !== MARKET_TYPES.S3.value ? schema.strip() : schema.notRequired())
|
||||
)
|
||||
.default(() => undefined),
|
||||
grid: { md: 12 },
|
||||
}
|
||||
|
||||
/** @type {Field} TOTAL_MB field */
|
||||
const READ_LENGTH = {
|
||||
name: 'READ_LENGTH',
|
||||
label: T['marketplace.form.configuration.s3.readLength'],
|
||||
tooltip: T['marketplace.form.configuration.s3.readLength.tooltip'],
|
||||
type: INPUT_TYPES.TEXT,
|
||||
dependOf: '$general.MARKET_MAD',
|
||||
htmlType: (type) =>
|
||||
type !== MARKET_TYPES.S3.value ? INPUT_TYPES.HIDDEN : 'number',
|
||||
validation: number()
|
||||
.when(
|
||||
'$general.MARKET_MAD',
|
||||
(type, schema) =>
|
||||
type &&
|
||||
(type !== MARKET_TYPES.S3.value ? schema.strip() : schema.notRequired())
|
||||
)
|
||||
.default(() => undefined),
|
||||
grid: { md: 12 },
|
||||
}
|
||||
|
||||
const FIELDS = [
|
||||
AWS,
|
||||
ACCESS_KEY_ID,
|
||||
SECRET_ACCESS_KEY,
|
||||
BUCKET,
|
||||
REGION,
|
||||
SIGNATURE_VERSION,
|
||||
FORCE_PATH_STYLE,
|
||||
TOTAL_MB,
|
||||
READ_LENGTH,
|
||||
]
|
||||
|
||||
export { FIELDS }
|
@ -0,0 +1,301 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import PropTypes from 'prop-types'
|
||||
import FormWithSchema from 'client/components/Forms/FormWithSchema'
|
||||
import { T, MARKET_TYPES } from 'client/constants'
|
||||
import { SCHEMA, FIELDS } from './schema'
|
||||
import { Grid, Card, CardContent, Typography, Alert } from '@mui/material'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { generateDocLink } from 'client/utils'
|
||||
import { useFormContext } from 'react-hook-form'
|
||||
import makeStyles from '@mui/styles/makeStyles'
|
||||
import { find } from 'lodash'
|
||||
|
||||
export const STEP_ID = 'configuration'
|
||||
|
||||
/**
|
||||
* Return content of the step.
|
||||
*
|
||||
* @param {string} version - OpenNebula version
|
||||
* @returns {object} - Content of the step
|
||||
*/
|
||||
const Content = (version) => {
|
||||
// Style for info message
|
||||
const useStyles = makeStyles(({ palette }) => ({
|
||||
groupInfo: {
|
||||
'&': {
|
||||
gridColumn: 'span 2',
|
||||
marginTop: '1em',
|
||||
backgroundColor: palette.background.paper,
|
||||
},
|
||||
},
|
||||
}))
|
||||
const classes = useStyles()
|
||||
|
||||
const { getValues } = useFormContext()
|
||||
const isDockerhub =
|
||||
getValues('general.MARKET_MAD') === MARKET_TYPES.DOCKERHUB.value
|
||||
const type = find(MARKET_TYPES, { value: getValues('general.MARKET_MAD') })
|
||||
|
||||
return (
|
||||
<Grid mt={2} container>
|
||||
<Grid item xs={8}>
|
||||
{isDockerhub && (
|
||||
<Alert
|
||||
severity="info"
|
||||
variant="outlined"
|
||||
className={classes.groupInfo}
|
||||
>
|
||||
{Tr(T['marketplace.form.configuration.dockerhub.info'])}
|
||||
</Alert>
|
||||
)}
|
||||
<FormWithSchema id={STEP_ID} cy={`${STEP_ID}`} fields={FIELDS} />
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<Card
|
||||
elevation={2}
|
||||
sx={{
|
||||
height: '100%',
|
||||
minHeight: '630px',
|
||||
maxHeight: '630px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
overflow: 'auto',
|
||||
marginLeft: '1em',
|
||||
marginTop: '1rem',
|
||||
}}
|
||||
>
|
||||
<CardContent
|
||||
sx={{
|
||||
flexGrow: 1,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '1em',
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6" component="div" gutterBottom>
|
||||
{Tr(T['marketplace.general.help.title'])}
|
||||
</Typography>
|
||||
|
||||
{type?.value === 'one' && (
|
||||
<>
|
||||
<Typography variant="body2" gutterBottom>
|
||||
<b>{Tr(T['marketplace.types.one'])}</b>
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body2" gutterBottom>
|
||||
{Tr(T['marketplace.form.configuration.one.help.paragraph.1'])}
|
||||
<a
|
||||
target="_blank"
|
||||
href="http://marketplace.opennebula.io/appliance"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{Tr(
|
||||
T[
|
||||
'marketplace.form.configuration.one.help.paragraph.1.link'
|
||||
]
|
||||
)}
|
||||
</a>
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body2" gutterBottom>
|
||||
{Tr(T['marketplace.form.configuration.one.help.paragraph.2'])}{' '}
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body2" gutterBottom>
|
||||
<a
|
||||
target="_blank"
|
||||
href={generateDocLink(
|
||||
version,
|
||||
'marketplace/public_marketplaces/opennebula.html'
|
||||
)}
|
||||
rel="noreferrer"
|
||||
>
|
||||
{Tr(T['marketplace.form.configuration.one.help.link'])}
|
||||
</a>
|
||||
</Typography>
|
||||
</>
|
||||
)}
|
||||
|
||||
{type?.value === 'http' && (
|
||||
<>
|
||||
<Typography variant="body2" gutterBottom>
|
||||
<b>{Tr(T['marketplace.types.http'])}</b>
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body2" gutterBottom>
|
||||
{Tr(
|
||||
T['marketplace.form.configuration.http.help.paragraph.1']
|
||||
)}
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body2" gutterBottom>
|
||||
{Tr(
|
||||
T['marketplace.form.configuration.http.help.paragraph.2']
|
||||
)}{' '}
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body2" gutterBottom>
|
||||
<a
|
||||
target="_blank"
|
||||
href={generateDocLink(
|
||||
version,
|
||||
'marketplace/private_marketplaces/market_http.html'
|
||||
)}
|
||||
rel="noreferrer"
|
||||
>
|
||||
{Tr(T['marketplace.form.configuration.http.help.link'])}
|
||||
</a>
|
||||
</Typography>
|
||||
</>
|
||||
)}
|
||||
|
||||
{type?.value === 's3' && (
|
||||
<>
|
||||
<Typography variant="body2" gutterBottom>
|
||||
<b>{Tr(T['marketplace.types.s3'])}</b>
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body2" gutterBottom>
|
||||
{Tr(T['marketplace.form.configuration.s3.help.paragraph.1'])}
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body2" gutterBottom>
|
||||
{Tr(T['marketplace.form.configuration.s3.help.paragraph.2'])}{' '}
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body2" gutterBottom>
|
||||
<a
|
||||
target="_blank"
|
||||
href={generateDocLink(
|
||||
version,
|
||||
'marketplace/private_marketplaces/market_s3.html'
|
||||
)}
|
||||
rel="noreferrer"
|
||||
>
|
||||
{Tr(T['marketplace.form.configuration.s3.help.link'])}
|
||||
</a>
|
||||
</Typography>
|
||||
</>
|
||||
)}
|
||||
|
||||
{type?.value === 'dockerhub' && (
|
||||
<>
|
||||
<Typography variant="body2" gutterBottom>
|
||||
<b>{Tr(T['marketplace.types.dockerhub'])}</b>
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body2" gutterBottom>
|
||||
{Tr(
|
||||
T[
|
||||
'marketplace.form.configuration.dockerhub.help.paragraph.1'
|
||||
]
|
||||
)}
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body2" gutterBottom>
|
||||
{Tr(
|
||||
T[
|
||||
'marketplace.form.configuration.dockerhub.help.paragraph.2'
|
||||
]
|
||||
)}{' '}
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body2" gutterBottom>
|
||||
<a
|
||||
target="_blank"
|
||||
href={generateDocLink(
|
||||
version,
|
||||
'marketplace/public_marketplaces/dockerhub.html'
|
||||
)}
|
||||
rel="noreferrer"
|
||||
>
|
||||
{Tr(
|
||||
T['marketplace.form.configuration.dockerhub.help.link']
|
||||
)}
|
||||
</a>
|
||||
</Typography>
|
||||
</>
|
||||
)}
|
||||
|
||||
{type?.value === 'docker_registry' && (
|
||||
<>
|
||||
<Typography variant="body2" gutterBottom>
|
||||
<b>{Tr(T['marketplace.types.dockerRegistry'])}</b>
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body2" gutterBottom>
|
||||
{Tr(
|
||||
T[
|
||||
'marketplace.form.configuration.dockerRegistry.help.paragraph.1'
|
||||
]
|
||||
)}
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body2" gutterBottom>
|
||||
{Tr(
|
||||
T[
|
||||
'marketplace.form.configuration.dockerRegistry.help.paragraph.2'
|
||||
]
|
||||
)}{' '}
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body2" gutterBottom>
|
||||
<a
|
||||
target="_blank"
|
||||
href={generateDocLink(
|
||||
version,
|
||||
'marketplace/private_marketplaces/docker_registry.html'
|
||||
)}
|
||||
rel="noreferrer"
|
||||
>
|
||||
{Tr(
|
||||
T[
|
||||
'marketplace.form.configuration.dockerRegistry.help.link'
|
||||
]
|
||||
)}
|
||||
</a>
|
||||
</Typography>
|
||||
</>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration attributes.
|
||||
*
|
||||
* @param {object} props - Step props
|
||||
* @param {string} props.version - OpenNebula version
|
||||
* @returns {object} AdvancedOptions configuration step
|
||||
*/
|
||||
const Configuration = ({ version }) => ({
|
||||
id: STEP_ID,
|
||||
label: T['marketplace.configuration.title'],
|
||||
resolver: SCHEMA,
|
||||
optionsValidate: { abortEarly: false },
|
||||
content: () => Content(version),
|
||||
})
|
||||
|
||||
Configuration.propTypes = {
|
||||
data: PropTypes.object,
|
||||
setFormData: PropTypes.func,
|
||||
}
|
||||
|
||||
export default Configuration
|
@ -0,0 +1,31 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { getObjectSchemaFromFields } from 'client/utils'
|
||||
|
||||
import { FIELDS as COMMON_FIELDS } from './fieldsCommon'
|
||||
import { FIELDS as HTTP_FIELDS } from './fieldsHttp'
|
||||
import { FIELDS as S3_FIELDS } from './fieldsS3'
|
||||
import { FIELDS as DOCKER_REGISTRY_FIELDS } from './fieldsDockerRegistry'
|
||||
|
||||
const FIELDS = [
|
||||
...HTTP_FIELDS,
|
||||
...S3_FIELDS,
|
||||
...DOCKER_REGISTRY_FIELDS,
|
||||
...COMMON_FIELDS,
|
||||
]
|
||||
const SCHEMA = getObjectSchemaFromFields(FIELDS)
|
||||
|
||||
export { SCHEMA, FIELDS }
|
@ -0,0 +1,115 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import PropTypes from 'prop-types'
|
||||
import FormWithSchema from 'client/components/Forms/FormWithSchema'
|
||||
import { T } from 'client/constants'
|
||||
import { SCHEMA, FIELDS } from './schema'
|
||||
import { Grid, Card, CardContent, Typography } from '@mui/material'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { generateDocLink } from 'client/utils'
|
||||
|
||||
export const STEP_ID = 'general'
|
||||
|
||||
const Content = (version) => (
|
||||
<Grid mt={2} container>
|
||||
<Grid item xs={8}>
|
||||
<FormWithSchema id={STEP_ID} cy={`${STEP_ID}`} fields={FIELDS} />
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<Card
|
||||
elevation={2}
|
||||
sx={{
|
||||
height: '100%',
|
||||
minHeight: '630px',
|
||||
maxHeight: '630px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
overflow: 'auto',
|
||||
marginLeft: '1em',
|
||||
marginTop: '1rem',
|
||||
}}
|
||||
>
|
||||
<CardContent
|
||||
sx={{
|
||||
flexGrow: 1,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '1em',
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6" component="div" gutterBottom>
|
||||
{' '}
|
||||
{Tr(T['marketplace.general.help.title'])}{' '}
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body2" gutterBottom>
|
||||
{Tr(T['marketplace.general.help.paragraph.1'])}{' '}
|
||||
</Typography>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<Typography variant="body2" gutterBottom>
|
||||
{Tr(T['marketplace.general.help.paragraph.2.1'])}{' '}
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography variant="body2" gutterBottom>
|
||||
{Tr(T['marketplace.general.help.paragraph.2.2'])}{' '}
|
||||
</Typography>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<Typography variant="body2" gutterBottom>
|
||||
{Tr(T['marketplace.general.help.paragraph.3'])}{' '}
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body2" gutterBottom>
|
||||
{' '}
|
||||
<a
|
||||
target="_blank"
|
||||
href={generateDocLink(version, 'marketplace/index.html')}
|
||||
rel="noreferrer"
|
||||
>
|
||||
{Tr(T['marketplace.form.create.help.link'])}
|
||||
</a>
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)
|
||||
|
||||
/**
|
||||
* General Group configuration.
|
||||
*
|
||||
* @param {object} props - Step props
|
||||
* @param {string} props.version - OpenNebula version
|
||||
* @returns {object} General configuration step
|
||||
*/
|
||||
const General = ({ version }) => ({
|
||||
id: STEP_ID,
|
||||
label: T.General,
|
||||
resolver: SCHEMA,
|
||||
optionsValidate: { abortEarly: false },
|
||||
content: () => Content(version),
|
||||
})
|
||||
|
||||
General.propTypes = {
|
||||
data: PropTypes.object,
|
||||
setFormData: PropTypes.func,
|
||||
}
|
||||
|
||||
export default General
|
@ -0,0 +1,65 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { INPUT_TYPES, T, MARKET_TYPES } from 'client/constants'
|
||||
import { Field, getObjectSchemaFromFields, arrayToOptions } from 'client/utils'
|
||||
import { string } from 'yup'
|
||||
|
||||
/** @type {Field} Name field */
|
||||
const NAME = {
|
||||
name: 'NAME',
|
||||
label: T['marketplace.form.create.general.name'],
|
||||
type: INPUT_TYPES.TEXT,
|
||||
validation: string()
|
||||
.trim()
|
||||
.required()
|
||||
.default(() => undefined),
|
||||
grid: { md: 12 },
|
||||
}
|
||||
|
||||
/** @type {Field} Name field */
|
||||
const DESCRIPTION = {
|
||||
name: 'DESCRIPTION',
|
||||
label: T['marketplace.form.create.general.description'],
|
||||
type: INPUT_TYPES.TEXT,
|
||||
validation: string()
|
||||
.trim()
|
||||
.notRequired()
|
||||
.default(() => undefined),
|
||||
grid: { md: 12 },
|
||||
}
|
||||
|
||||
/** @type {Field} Name field */
|
||||
const TYPE = {
|
||||
name: 'MARKET_MAD',
|
||||
label: T['marketplace.form.create.general.type'],
|
||||
type: INPUT_TYPES.SELECT,
|
||||
values: arrayToOptions(Object.keys(MARKET_TYPES), {
|
||||
addEmpty: true,
|
||||
getText: (key) => T[MARKET_TYPES[key].text],
|
||||
getValue: (key) => MARKET_TYPES[key].value,
|
||||
}),
|
||||
validation: string()
|
||||
.trim()
|
||||
.required()
|
||||
.default(() => undefined),
|
||||
grid: { md: 12 },
|
||||
}
|
||||
|
||||
const FIELDS = [NAME, DESCRIPTION, TYPE]
|
||||
|
||||
const SCHEMA = getObjectSchemaFromFields(FIELDS)
|
||||
|
||||
export { SCHEMA, FIELDS }
|
@ -0,0 +1,72 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import General, {
|
||||
STEP_ID as GENERAL_ID,
|
||||
} from 'client/components/Forms/Marketplace/CreateForm/Steps/General'
|
||||
|
||||
import Configuration, {
|
||||
STEP_ID as CONFIGURATION_ID,
|
||||
} from 'client/components/Forms/Marketplace/CreateForm/Steps/Configuration'
|
||||
|
||||
import { createSteps } from 'client/utils'
|
||||
|
||||
/**
|
||||
* Create steps for Marketplace Create Form:
|
||||
* 1. General: General attributes for marketplace
|
||||
* 2. Configuration: Configuration attributes for marketplace depending its type
|
||||
*/
|
||||
const Steps = createSteps([General, Configuration], {
|
||||
transformInitialValue: (marketplace, schema) => {
|
||||
const knownTemplate = schema.cast(
|
||||
{
|
||||
[GENERAL_ID]: {
|
||||
NAME: marketplace.NAME,
|
||||
DESCRIPTION: marketplace.TEMPLATE.DESCRIPTION,
|
||||
MARKET_MAD: marketplace.MARKET_MAD,
|
||||
},
|
||||
[CONFIGURATION_ID]: {
|
||||
...marketplace.TEMPLATE,
|
||||
BRIDGE_LIST: marketplace?.TEMPLATE.BRIDGE_LIST?.split(' '),
|
||||
},
|
||||
},
|
||||
{
|
||||
stripUnknown: true,
|
||||
}
|
||||
)
|
||||
|
||||
return knownTemplate
|
||||
},
|
||||
|
||||
transformBeforeSubmit: (formData, initialValues) => {
|
||||
// Get data from steps
|
||||
const { [GENERAL_ID]: generalData } = formData
|
||||
const { [CONFIGURATION_ID]: configurationData } = formData
|
||||
|
||||
// Check if the name has been changed
|
||||
const changeName =
|
||||
initialValues && initialValues?.NAME !== generalData?.NAME
|
||||
? generalData?.NAME
|
||||
: undefined
|
||||
|
||||
return {
|
||||
...generalData,
|
||||
...configurationData,
|
||||
changeName,
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
export default Steps
|
@ -0,0 +1,16 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
export { default } from 'client/components/Forms/Marketplace/CreateForm/Steps'
|
@ -0,0 +1,27 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { ReactElement } from 'react'
|
||||
import { AsyncLoadForm, ConfigurationProps } from 'client/components/HOC'
|
||||
import { CreateStepsCallback } from 'client/utils/schema'
|
||||
|
||||
/**
|
||||
* @param {ConfigurationProps} configProps - Configuration
|
||||
* @returns {ReactElement|CreateStepsCallback} Asynchronous loaded form
|
||||
*/
|
||||
const CreateForm = (configProps) =>
|
||||
AsyncLoadForm({ formPath: 'Marketplace/CreateForm' }, configProps)
|
||||
|
||||
export { CreateForm }
|
@ -35,7 +35,17 @@ const MarketplaceAppsTable = (props) => {
|
||||
searchProps['data-cy'] ??= `search-${DEFAULT_DATA_CY}`
|
||||
|
||||
const { view, getResourceView } = useViews()
|
||||
const { data = [], isFetching, refetch } = useGetMarketplaceAppsQuery()
|
||||
const {
|
||||
data: marketplaceApps = [],
|
||||
isFetching,
|
||||
refetch,
|
||||
} = useGetMarketplaceAppsQuery()
|
||||
|
||||
// Filter data if there is filter function
|
||||
const data =
|
||||
props?.filterData && typeof props?.filterData === 'function'
|
||||
? props?.filterData(marketplaceApps)
|
||||
: marketplaceApps
|
||||
|
||||
const columns = useMemo(
|
||||
() =>
|
||||
|
@ -0,0 +1,210 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { Typography } from '@mui/material'
|
||||
import { AddCircledOutline, Trash, MoreVert, Group } from 'iconoir-react'
|
||||
import { useMemo } from 'react'
|
||||
import { useHistory } from 'react-router-dom'
|
||||
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import {
|
||||
useRemoveMarketplaceMutation,
|
||||
useEnableMarketplaceMutation,
|
||||
useDisableMarketplaceMutation,
|
||||
useChangeMarketplaceOwnershipMutation,
|
||||
} from 'client/features/OneApi/marketplace'
|
||||
|
||||
import {
|
||||
createActions,
|
||||
GlobalAction,
|
||||
} from 'client/components/Tables/Enhanced/Utils'
|
||||
|
||||
import { PATH } from 'client/apps/sunstone/routesOne'
|
||||
import { Translate } from 'client/components/HOC'
|
||||
import { RESOURCE_NAMES, T, MARKETPLACE_ACTIONS } from 'client/constants'
|
||||
import { ChangeGroupForm, ChangeUserForm } from 'client/components/Forms/Vm'
|
||||
|
||||
const ListMarketplaceNames = ({ rows = [] }) =>
|
||||
rows?.map?.(({ id, original }) => {
|
||||
const { ID, NAME } = original
|
||||
|
||||
return (
|
||||
<Typography
|
||||
key={`group-${id}`}
|
||||
variant="inherit"
|
||||
component="span"
|
||||
display="block"
|
||||
>
|
||||
{`#${ID} ${NAME}`}
|
||||
</Typography>
|
||||
)
|
||||
})
|
||||
|
||||
const SubHeader = (rows) => <ListMarketplaceNames rows={rows} />
|
||||
|
||||
const MessageToConfirmAction = (rows, description) => (
|
||||
<>
|
||||
<ListMarketplaceNames rows={rows} />
|
||||
{description && <Translate word={description} />}
|
||||
<Translate word={T.DoYouWantProceed} />
|
||||
</>
|
||||
)
|
||||
|
||||
MessageToConfirmAction.displayName = 'MessageToConfirmAction'
|
||||
|
||||
/**
|
||||
* Generates the actions to operate resources on Groups table.
|
||||
*
|
||||
* @returns {GlobalAction} - Actions
|
||||
*/
|
||||
const Actions = () => {
|
||||
const history = useHistory()
|
||||
const { view, getResourceView } = useViews()
|
||||
const [remove] = useRemoveMarketplaceMutation()
|
||||
const [enable] = useEnableMarketplaceMutation()
|
||||
const [disable] = useDisableMarketplaceMutation()
|
||||
const [changeOwnership] = useChangeMarketplaceOwnershipMutation()
|
||||
|
||||
return useMemo(
|
||||
() =>
|
||||
createActions({
|
||||
filters: getResourceView(RESOURCE_NAMES.MARKETPLACE)?.actions,
|
||||
actions: [
|
||||
{
|
||||
accessor: MARKETPLACE_ACTIONS.CREATE_DIALOG,
|
||||
tooltip: T.Create,
|
||||
icon: AddCircledOutline,
|
||||
action: () => history.push(PATH.STORAGE.MARKETPLACES.CREATE),
|
||||
},
|
||||
{
|
||||
accessor: MARKETPLACE_ACTIONS.UPDATE_DIALOG,
|
||||
label: T.Update,
|
||||
tooltip: T.Update,
|
||||
selected: { max: 1 },
|
||||
color: 'secondary',
|
||||
action: (rows) => {
|
||||
const group = rows?.[0]?.original ?? {}
|
||||
const path = PATH.STORAGE.MARKETPLACES.CREATE
|
||||
|
||||
history.push(path, group)
|
||||
},
|
||||
},
|
||||
{
|
||||
tooltip: T.Enable,
|
||||
icon: MoreVert,
|
||||
selected: true,
|
||||
color: 'secondary',
|
||||
dataCy: 'marketplace-enable',
|
||||
options: [
|
||||
{
|
||||
accessor: MARKETPLACE_ACTIONS.ENABLE,
|
||||
name: T.Enable,
|
||||
isConfirmDialog: true,
|
||||
dialogProps: {
|
||||
title: T.Enable,
|
||||
children: MessageToConfirmAction,
|
||||
dataCy: `modal-${MARKETPLACE_ACTIONS.ENABLE}`,
|
||||
},
|
||||
onSubmit: (rows) => async () => {
|
||||
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(ids.map((id) => enable({ id })))
|
||||
},
|
||||
},
|
||||
{
|
||||
accessor: MARKETPLACE_ACTIONS.DISABLE,
|
||||
name: T.Disable,
|
||||
isConfirmDialog: true,
|
||||
dialogProps: {
|
||||
title: T.Disable,
|
||||
children: MessageToConfirmAction,
|
||||
dataCy: `modal-${MARKETPLACE_ACTIONS.DISABLE}`,
|
||||
},
|
||||
onSubmit: (rows) => async () => {
|
||||
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(ids.map((id) => disable({ id })))
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
tooltip: T.Ownership,
|
||||
icon: Group,
|
||||
selected: true,
|
||||
color: 'secondary',
|
||||
dataCy: 'marketplace-ownership',
|
||||
options: [
|
||||
{
|
||||
accessor: MARKETPLACE_ACTIONS.CHANGE_OWNER,
|
||||
name: T.ChangeOwner,
|
||||
dialogProps: {
|
||||
title: T.ChangeOwner,
|
||||
subheader: SubHeader,
|
||||
dataCy: `modal-${MARKETPLACE_ACTIONS.CHANGE_OWNER}`,
|
||||
},
|
||||
form: ChangeUserForm,
|
||||
onSubmit: (rows) => async (newOwnership) => {
|
||||
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(
|
||||
ids.map((id) => changeOwnership({ id, ...newOwnership }))
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
accessor: MARKETPLACE_ACTIONS.CHANGE_GROUP,
|
||||
name: T.ChangeGroup,
|
||||
dialogProps: {
|
||||
title: T.ChangeGroup,
|
||||
subheader: SubHeader,
|
||||
dataCy: `modal-${MARKETPLACE_ACTIONS.CHANGE_GROUP}`,
|
||||
},
|
||||
form: ChangeGroupForm,
|
||||
onSubmit: (rows) => async (newOwnership) => {
|
||||
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(
|
||||
ids.map((id) => changeOwnership({ id, ...newOwnership }))
|
||||
)
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
accessor: MARKETPLACE_ACTIONS.DELETE,
|
||||
tooltip: T.Delete,
|
||||
icon: Trash,
|
||||
color: 'error',
|
||||
selected: { min: 1 },
|
||||
dataCy: `marketplace_${MARKETPLACE_ACTIONS.DELETE}`,
|
||||
options: [
|
||||
{
|
||||
isConfirmDialog: true,
|
||||
dialogProps: {
|
||||
title: T.Delete,
|
||||
dataCy: `modal-${MARKETPLACE_ACTIONS.DELETE}`,
|
||||
children: MessageToConfirmAction,
|
||||
},
|
||||
onSubmit: (rows) => async () => {
|
||||
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(ids.map((id) => remove({ id })))
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
[view]
|
||||
)
|
||||
}
|
||||
|
||||
export default Actions
|
@ -0,0 +1,77 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { ReactElement } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Stack } from '@mui/material'
|
||||
import { MarketplaceAppsTable } from 'client/components/Tables'
|
||||
import { useGetMarketplaceQuery } from 'client/features/OneApi/marketplace'
|
||||
import { useHistory, generatePath } from 'react-router-dom'
|
||||
import { PATH } from 'client/apps/sunstone/routesOne'
|
||||
const _ = require('lodash')
|
||||
|
||||
/**
|
||||
* Renders marketplace apps tab showing the apps of the cluster.
|
||||
*
|
||||
* @param {object} props - Props
|
||||
* @param {string} props.id - Marketplace id
|
||||
* @returns {ReactElement} Marketplace apps tab
|
||||
*/
|
||||
const MarketplaceApps = ({ id }) => {
|
||||
// Get info about the marketplaceApps
|
||||
const { data: marketplace } = useGetMarketplaceQuery({ id })
|
||||
|
||||
// Define function to get details of a marketplace app
|
||||
const history = useHistory()
|
||||
const handleRowClick = (rowId) => {
|
||||
history.push(
|
||||
generatePath(PATH.STORAGE.MARKETPLACE_APPS.DETAIL, { id: String(rowId) })
|
||||
)
|
||||
}
|
||||
|
||||
// Get apps of the marketplace
|
||||
const apps = _.isEmpty(marketplace?.MARKETPLACEAPPS)
|
||||
? []
|
||||
: Array.isArray(marketplace?.MARKETPLACEAPPS?.ID)
|
||||
? marketplace?.MARKETPLACEAPPS?.ID
|
||||
: [marketplace?.MARKETPLACEAPPS?.ID]
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Stack
|
||||
display="grid"
|
||||
gap="1em"
|
||||
gridTemplateColumns="repeat(auto-fit, minmax(49%, 1fr))"
|
||||
padding={{ sm: '0.8em' }}
|
||||
>
|
||||
<MarketplaceAppsTable
|
||||
disableRowSelect
|
||||
filterData={(dataToFilter) =>
|
||||
dataToFilter.filter((app) => _.includes(apps, app.ID))
|
||||
}
|
||||
onRowClick={(row) => handleRowClick(row.ID)}
|
||||
/>
|
||||
</Stack>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
MarketplaceApps.propTypes = {
|
||||
id: PropTypes.string,
|
||||
}
|
||||
|
||||
MarketplaceApps.displayName = 'MarketplaceApps'
|
||||
|
||||
export default MarketplaceApps
|
@ -24,10 +24,12 @@ import { getAvailableInfoTabs } from 'client/models/Helper'
|
||||
|
||||
import Tabs from 'client/components/Tabs'
|
||||
import Info from 'client/components/Tabs/Marketplace/Info'
|
||||
import MarketplaceApps from 'client/components/Tabs/Marketplace/MarketplaceApps'
|
||||
|
||||
const getTabComponent = (tabName) =>
|
||||
({
|
||||
info: Info,
|
||||
apps: MarketplaceApps,
|
||||
}[tabName])
|
||||
|
||||
const MarketplaceTabs = memo(({ id }) => {
|
||||
|
@ -104,12 +104,13 @@ export const MARKETPLACE_APP_STATES = [
|
||||
/** @enum {string} Datastore actions */
|
||||
export const MARKETPLACE_ACTIONS = {
|
||||
CREATE_DIALOG: 'create_dialog',
|
||||
UPDATE_DIALOG: 'update_dialog',
|
||||
DELETE: 'delete',
|
||||
|
||||
// INFORMATION
|
||||
RENAME: ACTIONS.RENAME,
|
||||
CHANGE_OWNER: ACTIONS.CHANGE_OWNER,
|
||||
CHANGE_GROUP: ACTIONS.CHANGE_GROUP,
|
||||
ENABLE: 'enable',
|
||||
DISABLE: 'disable',
|
||||
}
|
||||
|
||||
/**
|
||||
@ -119,3 +120,26 @@ export const MARKETPLACE_ACTIONS = {
|
||||
export const MARKET_THRESHOLD = {
|
||||
CAPACITY: { high: 66, low: 33 },
|
||||
}
|
||||
|
||||
export const MARKET_TYPES = {
|
||||
OPENNEBULA: {
|
||||
text: 'marketplace.types.one',
|
||||
value: 'one',
|
||||
},
|
||||
HTTP: {
|
||||
text: 'marketplace.types.http',
|
||||
value: 'http',
|
||||
},
|
||||
S3: {
|
||||
text: 'marketplace.types.s3',
|
||||
value: 's3',
|
||||
},
|
||||
DOCKERHUB: {
|
||||
text: 'marketplace.types.dockerhub',
|
||||
value: 'dockerhub',
|
||||
},
|
||||
DOCKER_REGISTRY: {
|
||||
text: 'marketplace.types.dockerRegistry',
|
||||
value: 'docker_registry',
|
||||
},
|
||||
}
|
||||
|
@ -77,6 +77,7 @@ module.exports = {
|
||||
CreateFile: 'Create File',
|
||||
CreateHost: 'Create Host',
|
||||
CreateImage: 'Create Image',
|
||||
CreateMarketplace: 'Create Marketplace',
|
||||
CreateMarketApp: 'Create Marketplace App',
|
||||
CreateProvider: 'Create Provider',
|
||||
CreateProvision: 'Create Provision',
|
||||
@ -221,6 +222,7 @@ module.exports = {
|
||||
UpdateVirtualNetworkTemplate: 'Update Virtual Network Template',
|
||||
UpdateVmConfiguration: 'Update VM Configuration',
|
||||
UpdateVmTemplate: 'Update VM Template',
|
||||
UpdateMarketplace: 'Update Marketplace',
|
||||
|
||||
/* questions */
|
||||
Yes: 'Yes',
|
||||
@ -1482,6 +1484,107 @@ module.exports = {
|
||||
ReservedCpu: 'Allocated CPU',
|
||||
|
||||
/* Marketplace App schema */
|
||||
'marketplace.configuration.title': 'Configuration attributes',
|
||||
'marketplace.form.create.general.name': 'Name',
|
||||
'marketplace.form.create.general.description': 'Description',
|
||||
'marketplace.form.create.general.type': 'Storage backend',
|
||||
'marketplace.form.create.help.link':
|
||||
'See Open Nebula documentation to get more details about marketplaces.',
|
||||
'marketplace.general.help.title': 'Marketplace',
|
||||
'marketplace.general.help.paragraph.1':
|
||||
'OpenNebula Marketplaces provide a simple way to integrate your cloud with popular application/image providers. Think of them as external datastores. A Marketplace can be:',
|
||||
'marketplace.general.help.paragraph.2.1':
|
||||
'Public: accessible universally by all OpenNebula installations.',
|
||||
'marketplace.general.help.paragraph.2.2':
|
||||
'Private: local within an organization and specific for a single OpenNebula (a single zone) or shared by a federation (a collection of zones).',
|
||||
'marketplace.general.help.paragraph.3':
|
||||
'Please, select Name, Description and Storage backend of the Marketplace.',
|
||||
|
||||
'marketplace.form.configuration.one.url': 'Endpoint URL for marketplace',
|
||||
'marketplace.form.configuration.one.help.paragraph.1':
|
||||
'The OpenNebula Marketplace is a catalog of virtual appliances ready to run in OpenNebula environments available at ',
|
||||
'marketplace.form.configuration.one.help.paragraph.1.link':
|
||||
'http://marketplace.opennebula.io/appliance',
|
||||
'marketplace.form.configuration.one.help.paragraph.2':
|
||||
'Please, fill the configuration attributes for Markeplace OpenNebula Systems.',
|
||||
'marketplace.form.configuration.one.help.link':
|
||||
'See Open Nebula documentation to get more details about OpenNebula Systems marketplaces.',
|
||||
|
||||
'marketplace.form.configuration.http.url':
|
||||
'Base URL of the Marketplace HTTP endpoint',
|
||||
'marketplace.form.configuration.http.path': 'Marketapp directory path',
|
||||
'marketplace.form.configuration.http.path.tooltip':
|
||||
'Absolute directory path to place images (the HTTP server document root) in the Front-end or in the Hosts pointed at by the Storage bridge list',
|
||||
'marketplace.form.configuration.http.bridge': 'Storage bridge list',
|
||||
'marketplace.form.configuration.http.bridge.tooltip':
|
||||
'Space separated list of servers to access the public directory. If not defined, the public directory will be local to the Front-end',
|
||||
'marketplace.form.configuration.http.help.paragraph.1':
|
||||
'This Marketplace uses a conventional HTTP server to expose the images (Marketplace Appliances) uploaded to the Marketplace. The image will be placed in a specific directory (available on or at least accessible from the Front-end), that must be also served by a dedicated HTTP service.',
|
||||
'marketplace.form.configuration.http.help.paragraph.2':
|
||||
'Please, fill the configuration attributes for HTTP Marketplace.',
|
||||
'marketplace.form.configuration.http.help.link':
|
||||
'See Open Nebula documentation to get more details about HTTP marketplaces.',
|
||||
|
||||
'marketplace.form.configuration.s3.accessKey': 'Access Key Id',
|
||||
'marketplace.form.configuration.s3.accessKey.tooltip':
|
||||
'The access key of the S3 user',
|
||||
'marketplace.form.configuration.s3.secretAccessKey': 'Secret Access Key',
|
||||
'marketplace.form.configuration.s3.secretAccessKey.tooltip':
|
||||
'The secret key of the S3 user',
|
||||
'marketplace.form.configuration.s3.bucket': 'S3 bucket to store marketapps',
|
||||
'marketplace.form.configuration.s3.bucket.tooltip':
|
||||
'The bucket where the files will be stored',
|
||||
'marketplace.form.configuration.s3.region': 'Region',
|
||||
'marketplace.form.configuration.s3.region.tooltip':
|
||||
'The region to connect to. If you are using Ceph-S3 any value here will work',
|
||||
'marketplace.form.configuration.s3.aws': 'Use Amazon AWS S3 Service',
|
||||
'marketplace.form.configuration.s3.aws.toolkit':
|
||||
'Check in case that Amazon AWS S3 Service will be used instead Ceph S3',
|
||||
'marketplace.form.configuration.s3.endpoint': 'Endpoint URL for marketplace',
|
||||
'marketplace.form.configuration.s3.endpoint.tooltip':
|
||||
'This is only required if you are connecting to a service other than Amazon AWS S3. Preferably don’t use an endpoint that includes the bucket as the leading part of the host’s URL',
|
||||
'marketplace.form.configuration.s3.totalMB': 'Total Marketplace size in MB',
|
||||
'marketplace.form.configuration.s3.totalMB.tooltip':
|
||||
'This parameter defines the total size of the Marketplace in MB. It defaults to 1048576 (MB).',
|
||||
'marketplace.form.configuration.s3.readLength': 'Read block length in MB',
|
||||
'marketplace.form.configuration.s3.readLength.tooltip':
|
||||
'Split the file into chunks of this size in MB, never user a value larger than 100. Defaults to 32 (MB).',
|
||||
'marketplace.form.configuration.s3.help.paragraph.1':
|
||||
'This Marketplace uses an S3 API-capable service as the Back-end. This means Marketplace Appliances will be stored in the official AWS S3 service , or in services that implement that API, like Ceph Object Gateway S3.',
|
||||
'marketplace.form.configuration.s3.help.paragraph.2':
|
||||
'Please, fill the configuration attributes for S3 Marketplace.',
|
||||
'marketplace.form.configuration.s3.help.link':
|
||||
'See Open Nebula documentation to get more details about S3 marketplaces.',
|
||||
|
||||
'marketplace.form.configuration.dockerhub.info':
|
||||
'No configuration attributes are needed for Dockerhub.',
|
||||
'marketplace.form.configuration.dockerhub.help.paragraph.1':
|
||||
'The DockerHub Marketplace provide access to DockerHub Official Images. The OpenNebula context packages are installed during the import process so once an image is imported it’s fully prepared to be used.',
|
||||
'marketplace.form.configuration.dockerhub.help.paragraph.2':
|
||||
'Please, fill the configuration attributes for DockerHub Marketplace.',
|
||||
'marketplace.form.configuration.dockerhub.help.link':
|
||||
'See Open Nebula documentation to get more details about DockerHub marketplaces.',
|
||||
|
||||
'marketplace.form.configuration.dockerRegistry.url':
|
||||
'Marketplace Docker registry url',
|
||||
'marketplace.form.configuration.dockerRegistry.url.tooltip':
|
||||
'Base URL of the Marketplace Docker registry endpoint',
|
||||
'marketplace.form.configuration.dockerRegistry.ssl': 'SSL connection',
|
||||
'marketplace.form.configuration.dockerRegistry.ssl.tooltip':
|
||||
'Check if the registry is behind SSL proxy',
|
||||
'marketplace.form.configuration.dockerRegistry.help.paragraph.1':
|
||||
'This Marketplace uses a private Docker registry server to expose the images in it as Marketplace Appliances.',
|
||||
'marketplace.form.configuration.dockerRegistry.help.paragraph.2':
|
||||
'Please, fill the configuration attributes for Docker Registry Marketplace.',
|
||||
'marketplace.form.configuration.dockerRegistry.help.link':
|
||||
'See Open Nebula documentation to get more details about Docker Registry marketplaces.',
|
||||
|
||||
'marketplace.types.one': 'OpenNebula Systems',
|
||||
'marketplace.types.http': 'HTTP',
|
||||
'marketplace.types.s3': 'Amazon S3',
|
||||
'marketplace.types.dockerhub': 'DockerHub',
|
||||
'marketplace.types.dockerRegistry': 'Docker Registry',
|
||||
|
||||
/* Marketplace App - general */
|
||||
MarketplaceApp: 'Marketplace app',
|
||||
RegisteredAt: 'Registered %s',
|
||||
|
36
src/fireedge/src/client/containers/MarketplaceApps/Detail.js
Normal file
36
src/fireedge/src/client/containers/MarketplaceApps/Detail.js
Normal file
@ -0,0 +1,36 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { ReactElement } from 'react'
|
||||
import { useParams, Redirect } from 'react-router-dom'
|
||||
|
||||
import MarketplaceAppTabs from 'client/components/Tabs/MarketplaceApp'
|
||||
|
||||
/**
|
||||
* Displays the detail information about a Marketplace app.
|
||||
*
|
||||
* @returns {ReactElement} Host detail component.
|
||||
*/
|
||||
function MarketplaceAppDetail() {
|
||||
const { id } = useParams()
|
||||
|
||||
if (Number.isNaN(+id)) {
|
||||
return <Redirect to="/" />
|
||||
}
|
||||
|
||||
return <MarketplaceAppTabs id={id} />
|
||||
}
|
||||
|
||||
export default MarketplaceAppDetail
|
125
src/fireedge/src/client/containers/Marketplaces/Create.js
Normal file
125
src/fireedge/src/client/containers/Marketplaces/Create.js
Normal file
@ -0,0 +1,125 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import PropTypes from 'prop-types'
|
||||
import { ReactElement } from 'react'
|
||||
import { useHistory, useLocation } from 'react-router'
|
||||
|
||||
import { useGeneralApi } from 'client/features/General'
|
||||
import {
|
||||
useAllocateMarketplaceMutation,
|
||||
useGetMarketplaceQuery,
|
||||
useUpdateMarketplaceMutation,
|
||||
useRenameMarketplaceMutation,
|
||||
} from 'client/features/OneApi/marketplace'
|
||||
|
||||
import {
|
||||
DefaultFormStepper,
|
||||
SkeletonStepsForm,
|
||||
} from 'client/components/FormStepper'
|
||||
import { CreateForm } from 'client/components/Forms/Marketplace'
|
||||
import { PATH } from 'client/apps/sunstone/routesOne'
|
||||
import { jsonToXml } from 'client/models/Helper'
|
||||
|
||||
import systemApi from 'client/features/OneApi/system'
|
||||
|
||||
/**
|
||||
* Displays the creation form for a marketplace.
|
||||
*
|
||||
* @returns {ReactElement} - The marketplace form component
|
||||
*/
|
||||
function CreateMarketplace() {
|
||||
const history = useHistory()
|
||||
const { state: { ID: marketplaceId } = {} } = useLocation()
|
||||
|
||||
const { enqueueSuccess, enqueueError } = useGeneralApi()
|
||||
const [createMarketplace] = useAllocateMarketplaceMutation()
|
||||
const [updateMarketplace] = useUpdateMarketplaceMutation()
|
||||
const [renameMarketplace] = useRenameMarketplaceMutation()
|
||||
|
||||
const { data: views } = systemApi.useGetSunstoneAvalaibleViewsQuery()
|
||||
const { data: version } = systemApi.useGetOneVersionQuery()
|
||||
|
||||
const { data: marketplace } = marketplaceId
|
||||
? useGetMarketplaceQuery({ id: marketplaceId })
|
||||
: { data: undefined }
|
||||
|
||||
const onSubmit = async (template) => {
|
||||
try {
|
||||
// Request to create a marketplace but not to update
|
||||
if (!marketplaceId) {
|
||||
// Create marketplace
|
||||
const newMarketplaceId = await createMarketplace({
|
||||
template: jsonToXml(template),
|
||||
}).unwrap()
|
||||
|
||||
// Only show marketplace message
|
||||
enqueueSuccess(`Marketplace created - #${newMarketplaceId}`)
|
||||
} else {
|
||||
// Rename if the name has been changed
|
||||
if (template?.changeName) {
|
||||
await renameMarketplace({
|
||||
id: marketplaceId,
|
||||
name: template?.NAME,
|
||||
}).unwrap()
|
||||
delete template?.changeName
|
||||
}
|
||||
|
||||
// Name in not on the template
|
||||
delete template?.NAME
|
||||
|
||||
// Update marketplace
|
||||
await updateMarketplace({
|
||||
id: marketplaceId,
|
||||
template: jsonToXml(template),
|
||||
}).unwrap()
|
||||
|
||||
// Only show marketplace message
|
||||
enqueueSuccess(`Marketplace updated - #${marketplaceId}`)
|
||||
}
|
||||
|
||||
// Go to marketplaces list
|
||||
history.push(PATH.STORAGE.MARKETPLACES.LIST)
|
||||
} catch (error) {
|
||||
enqueueError('Error creating marketplace')
|
||||
}
|
||||
}
|
||||
|
||||
return views &&
|
||||
version &&
|
||||
(!marketplaceId || (marketplaceId && marketplace)) ? (
|
||||
<CreateForm
|
||||
onSubmit={onSubmit}
|
||||
initialValues={marketplace}
|
||||
stepProps={{
|
||||
views,
|
||||
version,
|
||||
}}
|
||||
fallback={<SkeletonStepsForm />}
|
||||
>
|
||||
{(config) => <DefaultFormStepper {...config} />}
|
||||
</CreateForm>
|
||||
) : (
|
||||
<SkeletonStepsForm />
|
||||
)
|
||||
}
|
||||
|
||||
CreateMarketplace.propTypes = {
|
||||
marketplace: PropTypes.object,
|
||||
views: PropTypes.object,
|
||||
system: PropTypes.object,
|
||||
}
|
||||
|
||||
export default CreateMarketplace
|
@ -33,6 +33,8 @@ import { SubmitButton } from 'client/components/FormControl'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { T, Marketplace } from 'client/constants'
|
||||
|
||||
import MarketplaceActions from 'client/components/Tables/Marketplaces/actions'
|
||||
|
||||
/**
|
||||
* Displays a list of Marketplaces with a split pane between the list and selected row(s).
|
||||
*
|
||||
@ -44,6 +46,8 @@ function Marketplaces() {
|
||||
const hasSelectedRows = selectedRows?.length > 0
|
||||
const moreThanOneSelected = selectedRows?.length > 1
|
||||
|
||||
const actions = MarketplaceActions()
|
||||
|
||||
return (
|
||||
<SplitPane gridTemplateRows="1fr auto 1fr">
|
||||
{({ getGridProps, GutterComponent }) => (
|
||||
@ -51,6 +55,7 @@ function Marketplaces() {
|
||||
<MarketplacesTable
|
||||
onSelectedRowsChange={onSelectedRowsChange}
|
||||
useUpdateMutation={useUpdateMarketplaceMutation}
|
||||
globalActions={actions}
|
||||
/>
|
||||
|
||||
{hasSelectedRows && (
|
||||
|
@ -88,7 +88,7 @@ const clusterApi = oneApi.injectEndpoints({
|
||||
|
||||
dispatch(
|
||||
clusterApi.util.updateQueryData(
|
||||
'getGClusters',
|
||||
'getClusters',
|
||||
undefined,
|
||||
updateResourceOnPool({ id, resourceFromQuery })
|
||||
)
|
||||
|
@ -27,7 +27,7 @@ import { xmlToJson } from 'client/models/Helper'
|
||||
* @param {string} resourceId - The resource ID
|
||||
* @returns {boolean} - True if the parameters are valid, false otherwise
|
||||
*/
|
||||
const isUpdateOnPool = (draft, resourceId) =>
|
||||
export const isUpdateOnPool = (draft, resourceId) =>
|
||||
Array.isArray(draft) && resourceId !== undefined
|
||||
|
||||
/**
|
||||
|
@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { Draft, ThunkAction } from '@reduxjs/toolkit'
|
||||
import { Actions, Commands } from 'server/utils/constants/commands/market'
|
||||
import {
|
||||
oneApi,
|
||||
@ -21,9 +22,54 @@ import {
|
||||
} from 'client/features/OneApi'
|
||||
import { Permission, Marketplace } from 'client/constants'
|
||||
|
||||
import {
|
||||
removeResourceOnPool,
|
||||
updateNameOnResource,
|
||||
updateResourceOnPool,
|
||||
isUpdateOnPool,
|
||||
} from 'client/features/OneApi/common'
|
||||
|
||||
import { xmlToJson } from 'client/models/Helper'
|
||||
|
||||
const { MARKETPLACE } = ONE_RESOURCES
|
||||
const { MARKETPLACE_POOL } = ONE_RESOURCES_POOL
|
||||
|
||||
/**
|
||||
* Update the resource markteplace in the store.
|
||||
*
|
||||
* @param {object} params - Request params
|
||||
* @param {number|string} params.id - The id of the resource
|
||||
* @param {string} params.template - The new user template contents on XML format
|
||||
* @param {0|1} params.replace
|
||||
* - Update type:
|
||||
* ``0``: Replace the whole template.
|
||||
* ``1``: Merge new template with the existing one.
|
||||
* @param {string} [templateAttribute] - The attribute name of the resource template. By default is `TEMPLATE`.
|
||||
* @returns {function(Draft):ThunkAction} - Dispatches the action
|
||||
*/
|
||||
export const updateMarketplaceOnResource =
|
||||
(
|
||||
{ id: resourceId, template: xml, replace = 0 },
|
||||
templateAttribute = 'TEMPLATE'
|
||||
) =>
|
||||
(draft) => {
|
||||
const updatePool = isUpdateOnPool(draft, resourceId)
|
||||
const newTemplateJson = xmlToJson(xml)
|
||||
|
||||
const resource = updatePool
|
||||
? draft.find(({ ID }) => +ID === +resourceId)
|
||||
: draft
|
||||
|
||||
if (updatePool && !resource) return
|
||||
|
||||
resource[templateAttribute] =
|
||||
+replace === 0
|
||||
? newTemplateJson
|
||||
: { ...resource[templateAttribute], ...newTemplateJson }
|
||||
|
||||
resource.MARKET_MAD = newTemplateJson?.MARKET_MAD
|
||||
}
|
||||
|
||||
const marketplaceApi = oneApi.injectEndpoints({
|
||||
endpoints: (builder) => ({
|
||||
getMarketplaces: builder.query({
|
||||
@ -70,6 +116,28 @@ const marketplaceApi = oneApi.injectEndpoints({
|
||||
},
|
||||
transformResponse: (data) => data?.MARKETPLACE ?? {},
|
||||
providesTags: (_, __, { id }) => [{ type: MARKETPLACE, id }],
|
||||
async onQueryStarted({ id }, { dispatch, queryFulfilled }) {
|
||||
try {
|
||||
const { data: resourceFromQuery } = await queryFulfilled
|
||||
|
||||
dispatch(
|
||||
marketplaceApi.util.updateQueryData(
|
||||
'getMarketplace',
|
||||
undefined,
|
||||
updateResourceOnPool({ id, resourceFromQuery })
|
||||
)
|
||||
)
|
||||
} catch {
|
||||
// if the query fails, we want to remove the resource from the pool
|
||||
dispatch(
|
||||
marketplaceApi.util.updateQueryData(
|
||||
'getMarketplaces',
|
||||
undefined,
|
||||
removeResourceOnPool({ id })
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
}),
|
||||
allocateMarketplace: builder.mutation({
|
||||
/**
|
||||
@ -104,6 +172,30 @@ const marketplaceApi = oneApi.injectEndpoints({
|
||||
return { params, command }
|
||||
},
|
||||
invalidatesTags: [MARKETPLACE_POOL],
|
||||
async onQueryStarted(params, { dispatch, queryFulfilled }) {
|
||||
try {
|
||||
const patchMarketplace = dispatch(
|
||||
marketplaceApi.util.updateQueryData(
|
||||
'getMarketplace',
|
||||
{ id: params.id },
|
||||
updateNameOnResource(params)
|
||||
)
|
||||
)
|
||||
|
||||
const patchMarketplaces = dispatch(
|
||||
marketplaceApi.util.updateQueryData(
|
||||
'getMarketplaces',
|
||||
undefined,
|
||||
updateNameOnResource(params)
|
||||
)
|
||||
)
|
||||
|
||||
queryFulfilled.catch(() => {
|
||||
patchMarketplace.undo()
|
||||
patchMarketplaces.undo()
|
||||
})
|
||||
} catch {}
|
||||
},
|
||||
}),
|
||||
updateMarketplace: builder.mutation({
|
||||
/**
|
||||
@ -126,6 +218,30 @@ const marketplaceApi = oneApi.injectEndpoints({
|
||||
return { params, command }
|
||||
},
|
||||
providesTags: (_, __, { id }) => [{ type: MARKETPLACE, id }],
|
||||
async onQueryStarted(params, { dispatch, queryFulfilled }) {
|
||||
try {
|
||||
const patchMarketplace = dispatch(
|
||||
marketplaceApi.util.updateQueryData(
|
||||
'getMarketplace',
|
||||
{ id: params.id },
|
||||
updateMarketplaceOnResource(params)
|
||||
)
|
||||
)
|
||||
|
||||
const patchMarketplaces = dispatch(
|
||||
marketplaceApi.util.updateQueryData(
|
||||
'getMarketplaces',
|
||||
undefined,
|
||||
updateMarketplaceOnResource(params)
|
||||
)
|
||||
)
|
||||
|
||||
queryFulfilled.catch(() => {
|
||||
patchMarketplace.undo()
|
||||
patchMarketplaces.undo()
|
||||
})
|
||||
} catch {}
|
||||
},
|
||||
}),
|
||||
changeMarketplacePermissions: builder.mutation({
|
||||
/**
|
||||
@ -193,10 +309,30 @@ const marketplaceApi = oneApi.injectEndpoints({
|
||||
|
||||
return { params, command }
|
||||
},
|
||||
invalidatesTags: (_, __, { id }) => [
|
||||
{ type: MARKETPLACE, id },
|
||||
MARKETPLACE_POOL,
|
||||
],
|
||||
async onQueryStarted(params, { dispatch, queryFulfilled }) {
|
||||
try {
|
||||
const patchMarketplace = dispatch(
|
||||
marketplaceApi.util.updateQueryData(
|
||||
'getMarketplace',
|
||||
{ id: params.id },
|
||||
updateNameOnResource(params)
|
||||
)
|
||||
)
|
||||
|
||||
const patchMarketplaces = dispatch(
|
||||
marketplaceApi.util.updateQueryData(
|
||||
'getMarketplaces',
|
||||
undefined,
|
||||
updateNameOnResource(params)
|
||||
)
|
||||
)
|
||||
|
||||
queryFulfilled.catch(() => {
|
||||
patchMarketplace.undo()
|
||||
patchMarketplaces.undo()
|
||||
})
|
||||
} catch {}
|
||||
},
|
||||
}),
|
||||
enableMarketplace: builder.mutation({
|
||||
/**
|
||||
|
@ -136,11 +136,11 @@ module.exports = {
|
||||
from: resource,
|
||||
default: 0,
|
||||
},
|
||||
userId: {
|
||||
user: {
|
||||
from: postBody,
|
||||
default: -1,
|
||||
},
|
||||
groupId: {
|
||||
group: {
|
||||
from: postBody,
|
||||
default: -1,
|
||||
},
|
||||
@ -166,7 +166,7 @@ module.exports = {
|
||||
params: {
|
||||
id: {
|
||||
from: resource,
|
||||
default: 0,
|
||||
default: -1,
|
||||
},
|
||||
enable: {
|
||||
from: postBody,
|
||||
|
Loading…
Reference in New Issue
Block a user