diff --git a/package.json b/package.json index bb7f9494..c0e49a8c 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "engines": { - "node": "16 || 18" + "node": ">=18.20.0" }, "scripts": { "dev": "NODE_ENV=development concurrently \"yarn start\" \"yarn start-backend\"", diff --git a/plugins/cad-backend/src/service/config.ts b/plugins/cad-backend/src/service/config.ts index 0ae231a4..1bdb09a3 100644 --- a/plugins/cad-backend/src/service/config.ts +++ b/plugins/cad-backend/src/service/config.ts @@ -24,6 +24,7 @@ export enum GitOpsDeliveryTool { export enum ClusterLocatorMethodType { CURRENT_CONTEXT = 'current-context', IN_CLUSTER = 'in-cluster', + LOCAL_PROXY = 'local-proxy', } export enum ClusterLocatorAuthProvider { @@ -31,6 +32,7 @@ export enum ClusterLocatorAuthProvider { GOOGLE = 'google', OIDC = 'oidc', SERVICE_ACCOUNT = 'service-account', + NONE = 'none', } export enum OIDCTokenProvider { @@ -39,17 +41,11 @@ export enum OIDCTokenProvider { OKTA = 'okta', } -export const getMaxRequestSize = (config: Config): string => { - const maxRequestSize = config.getOptionalString('maxRequestSize') ?? '1mb'; - - return maxRequestSize; -}; +export const getMaxRequestSize = (config: Config): string => + config.getOptionalString('maxRequestSize') ?? '1mb'; -export const getResourcesNamespace = (config: Config): string => { - const namespace = config.getString('resourcesNamespace'); - - return namespace; -}; +export const getResourcesNamespace = (config: Config): string => + config.getString('resourcesNamespace'); export const getGitOpsDeliveryTool = (config: Config): GitOpsDeliveryTool => { const gitOpsTool = config.getString('gitOpsDeliveryTool'); diff --git a/plugins/cad-backend/src/service/lib.ts b/plugins/cad-backend/src/service/lib.ts index f0174460..a5861bb1 100644 --- a/plugins/cad-backend/src/service/lib.ts +++ b/plugins/cad-backend/src/service/lib.ts @@ -29,6 +29,12 @@ export const getKubernetesConfig = ( case ClusterLocatorMethodType.CURRENT_CONTEXT: kubeConfig.loadFromDefault(); break; + case ClusterLocatorMethodType.LOCAL_PROXY: + kubeConfig.loadFromClusterAndUser( + { name: 'local-proxy', server: 'http://localhost:8001' }, + { name: 'local-user' }, + ); + break; default: throw new Error( `Unknown cluster locator method type, ${clusterLocatorMethodType}`, diff --git a/plugins/cad-backend/src/service/router.ts b/plugins/cad-backend/src/service/router.ts index a5b7661c..bfeaaea6 100644 --- a/plugins/cad-backend/src/service/router.ts +++ b/plugins/cad-backend/src/service/router.ts @@ -62,6 +62,7 @@ const getClientAuthentication = ( case ClusterLocatorAuthProvider.SERVICE_ACCOUNT: case ClusterLocatorAuthProvider.CURRENT_CONTEXT: + case ClusterLocatorAuthProvider.NONE: return 'none'; default: diff --git a/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditorSelector.tsx b/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditorSelector.tsx index d631ce6d..35d9b09e 100644 --- a/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditorSelector.tsx +++ b/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditorSelector.tsx @@ -23,6 +23,7 @@ import { DeploymentEditor, StatefulSetEditor, } from './FirstClassEditors/DeploymentEditor'; +import { CapacityEditor } from './FirstClassEditors/CapacityEditor'; import { IngressEditor } from './FirstClassEditors/IngressEditor'; import { KptfileEditor } from './FirstClassEditors/KptfileEditor'; import { NamespaceEditor } from './FirstClassEditors/NamespaceEditor'; @@ -33,6 +34,8 @@ import { ServiceAccountEditor } from './FirstClassEditors/ServiceAccountEditor'; import { ServiceEditor } from './FirstClassEditors/ServiceEditor'; import { SetLabelsEditor } from './FirstClassEditors/SetLabelsEditor'; import { PackageVariantSetEditor } from './FirstClassEditors/PackageVariantSetEditor'; +import { NephioTokenEditor } from './FirstClassEditors/NephioTokenEditor'; +import { WorkloadClusterEditor } from './FirstClassEditors/WorkloadClusterEditor'; type OnUpdatedYamlFn = (yaml: string) => void; type OnNoNamedEditorFn = () => void; @@ -110,6 +113,14 @@ export const FirstClassEditorSelector = ({ case 'fn.kpt.dev/v1alpha1/SetLabels': return ; + case 'infra.nephio.org/v1alpha1/Token': + return ; + + case 'infra.nephio.org/v1alpha1/WorkloadCluster': + return ( + + ); + case 'kpt.dev/v1/Kptfile': return ( ); + case 'req.nephio.org/v1alpha1/Capacity': + return ; + case 'v1/ConfigMap': return ; diff --git a/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditors/CapacityEditor/CapacityEditor.tsx b/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditors/CapacityEditor/CapacityEditor.tsx new file mode 100644 index 00000000..468fe667 --- /dev/null +++ b/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditors/CapacityEditor/CapacityEditor.tsx @@ -0,0 +1,143 @@ +/** + * Copyright 2024 The Nephio Authors. + * + * 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 { TextField } from '@material-ui/core'; +import React, { useState } from 'react'; +import { useSetStateAndCall } from '../../../../../hooks/useSetStateAndCall'; +import { getNumber } from '../../../../../utils/string'; +import { + Capacity, + CapacityMetadata, + CapacitySpec, +} from '../../../../../types/Capacity'; +import { dumpYaml, loadYaml } from '../../../../../utils/yaml'; +import { EditorAccordion, ResourceMetadataAccordion } from '../Controls'; +import { useEditorStyles } from '../styles'; + +type OnUpdatedYamlFn = (yaml: string) => void; + +type ResourceEditorProps = { + yaml: string; + onUpdatedYaml: OnUpdatedYamlFn; +}; + +type State = { + metadata: CapacityMetadata; + spec: CapacitySpec; +}; + +export const CapacityEditor = ({ + yaml, + onUpdatedYaml, +}: ResourceEditorProps) => { + const classes = useEditorStyles(); + const resourceYaml = loadYaml(yaml) as Capacity; + + const createResourceState = (): State => ({ + metadata: resourceYaml.metadata, + spec: resourceYaml.spec, + }); + + const [state, setState] = useState(createResourceState()); + const [expanded, setExpanded] = useState(); + + const setStateAndCall = useSetStateAndCall([state, setState], newState => { + onUpdatedYaml(dumpYaml({ ...resourceYaml, ...newState })); + }); + + return ( +
+ setStateAndCall(s => ({ ...s, metadata }))} + /> + + + {/* TODO Consider adding regex-based validation of both throughput values. */} + { + setStateAndCall(s => ({ + ...s, + spec: { ...s.spec, maxUplinkThroughput: e.target.value }, + })); + }} + fullWidth + /> + + { + setStateAndCall(s => ({ + ...s, + spec: { ...s.spec, maxDownlinkThroughput: e.target.value }, + })); + }} + fullWidth + /> + + { + setStateAndCall(s => ({ + ...s, + spec: { ...s.spec, maxNFConnections: getNumber(e.target.value) }, + })); + }} + fullWidth + /> + + { + setStateAndCall(s => ({ + ...s, + spec: { ...s.spec, maxSessions: getNumber(e.target.value) }, + })); + }} + fullWidth + /> + + { + setStateAndCall(s => ({ + ...s, + spec: { ...s.spec, maxSubscribers: getNumber(e.target.value) }, + })); + }} + fullWidth + /> + +
+ ); +}; diff --git a/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditors/CapacityEditor/index.ts b/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditors/CapacityEditor/index.ts new file mode 100644 index 00000000..6be676e5 --- /dev/null +++ b/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditors/CapacityEditor/index.ts @@ -0,0 +1,17 @@ +/** + * Copyright 2024 The Nephio Authors. + * + * 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 { CapacityEditor } from './CapacityEditor'; diff --git a/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditors/Controls/ValueListEditorAccordion.tsx b/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditors/Controls/ValueListEditorAccordion.tsx new file mode 100644 index 00000000..466da73e --- /dev/null +++ b/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditors/Controls/ValueListEditorAccordion.tsx @@ -0,0 +1,102 @@ +/** + * Copyright 2024 The Nephio Authors. + * + * 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 { Button, TextField } from '@material-ui/core'; +import AddIcon from '@material-ui/icons/Add'; +import DeleteIcon from '@material-ui/icons/Delete'; +import React, { Fragment, useRef } from 'react'; +import { KubernetesValueList } from '../../../../../types/KubernetesResource'; +import { toLowerCase } from '../../../../../utils/string'; +import { IconButton } from '../../../../Controls'; +import { useEditorStyles } from '../styles'; +import { AccordionState, EditorAccordion } from './EditorAccordion'; + +type ValueListEditorProps = { + id: string; + title: string; + state: AccordionState; + valueList: KubernetesValueList; + onUpdatedValueList: (valueList: KubernetesValueList) => void; +}; + +export const ValueListEditorAccordion = ({ + id, + title, + state, + valueList, + onUpdatedValueList, +}: ValueListEditorProps) => { + const refViewModel = useRef<{ value: string }[]>( + valueList.map(value => ({ value })), + ); + + const classes = useEditorStyles(); + + const valueUpdated = (): void => { + onUpdatedValueList( + refViewModel.current.map(valueHolder => valueHolder.value), + ); + }; + + const addRow = () => { + refViewModel.current.push({ value: '' }); + valueUpdated(); + }; + + const description = `${refViewModel.current.length} ${toLowerCase(title)}`; + + return ( + + + {refViewModel.current.map((valueHolder, index) => ( +
+ { + valueHolder.value = e.target.value; + valueUpdated(); + }} + fullWidth + /> + { + refViewModel.current = refViewModel.current.filter( + thisValueHolder => thisValueHolder !== valueHolder, + ); + valueUpdated(); + }} + > + + +
+ ))} + + +
+
+ ); +}; diff --git a/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditors/Controls/index.ts b/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditors/Controls/index.ts index bbc33232..a11272d4 100644 --- a/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditors/Controls/index.ts +++ b/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditors/Controls/index.ts @@ -18,3 +18,4 @@ export { EditorAccordion } from './EditorAccordion'; export { KeyValueEditorAccordion } from './KeyValueEditorAccordion'; export { ResourceMetadataAccordion } from './ResourceMetadataAccordion'; export { SingleTextFieldAccordion } from './SingleTextFieldAccordion'; +export { ValueListEditorAccordion } from './ValueListEditorAccordion'; diff --git a/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditors/DeploymentEditor/DeploymentEditor.tsx b/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditors/DeploymentEditor/DeploymentEditor.tsx index 2e0f9476..691366ac 100644 --- a/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditors/DeploymentEditor/DeploymentEditor.tsx +++ b/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditors/DeploymentEditor/DeploymentEditor.tsx @@ -22,16 +22,16 @@ import { Deployment, DeploymentMetadata, DeploymentStrategy, - LabelSelector, } from '../../../../../types/Deployment'; import { Container, PodSecurityContext, Volume, } from '../../../../../types/Pod'; +import { LabelSelector } from '../../../../../types/Selectors'; import { PackageResource } from '../../../../../utils/packageRevisionResources'; import { dumpYaml, loadYaml } from '../../../../../utils/yaml'; -import { ResourceMetadataAccordion } from '../Controls/ResourceMetadataAccordion'; +import { ResourceMetadataAccordion } from '../Controls'; import { useEditorStyles } from '../styles'; import { Deletable, diff --git a/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditors/DeploymentEditor/StatefulSetEditor.tsx b/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditors/DeploymentEditor/StatefulSetEditor.tsx index b1547365..46d00d97 100644 --- a/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditors/DeploymentEditor/StatefulSetEditor.tsx +++ b/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditors/DeploymentEditor/StatefulSetEditor.tsx @@ -23,15 +23,15 @@ import { PodSecurityContext, Volume, } from '../../../../../types/Pod'; +import { LabelSelector } from '../../../../../types/Selectors'; import { - LabelSelector, StatefulSet, StatefulSetMetadata, StatefulSetUpdateStrategy, } from '../../../../../types/StatefulSet'; import { PackageResource } from '../../../../../utils/packageRevisionResources'; import { dumpYaml, loadYaml } from '../../../../../utils/yaml'; -import { ResourceMetadataAccordion } from '../Controls/ResourceMetadataAccordion'; +import { ResourceMetadataAccordion } from '../Controls'; import { useEditorStyles } from '../styles'; import { Deletable, diff --git a/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditors/DeploymentEditor/components/DeploymentDetailsEditorAccordion.tsx b/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditors/DeploymentEditor/components/DeploymentDetailsEditorAccordion.tsx index 2541cbe0..cb9e1dc3 100644 --- a/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditors/DeploymentEditor/components/DeploymentDetailsEditorAccordion.tsx +++ b/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditors/DeploymentEditor/components/DeploymentDetailsEditorAccordion.tsx @@ -17,11 +17,9 @@ import { TextField } from '@material-ui/core'; import { clone } from 'lodash'; import React, { Fragment, useRef, useState } from 'react'; -import { - DeploymentStrategy, - LabelSelector, -} from '../../../../../../types/Deployment'; +import { DeploymentStrategy } from '../../../../../../types/Deployment'; import { Volume } from '../../../../../../types/Pod'; +import { LabelSelector } from '../../../../../../types/Selectors'; import { getNumber } from '../../../../../../utils/string'; import { KeyValueEditorAccordion } from '../../Controls'; import { diff --git a/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditors/DeploymentEditor/components/StatefulSetDetailsEditorAccordion.tsx b/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditors/DeploymentEditor/components/StatefulSetDetailsEditorAccordion.tsx index a613cd43..0b3bd2cd 100644 --- a/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditors/DeploymentEditor/components/StatefulSetDetailsEditorAccordion.tsx +++ b/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditors/DeploymentEditor/components/StatefulSetDetailsEditorAccordion.tsx @@ -18,10 +18,8 @@ import { TextField } from '@material-ui/core'; import { clone } from 'lodash'; import React, { Fragment, useMemo, useRef, useState } from 'react'; import { Volume } from '../../../../../../types/Pod'; -import { - LabelSelector, - StatefulSetUpdateStrategy, -} from '../../../../../../types/StatefulSet'; +import { LabelSelector } from '../../../../../../types/Selectors'; +import { StatefulSetUpdateStrategy } from '../../../../../../types/StatefulSet'; import { getDeployableResources, PackageResource, diff --git a/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditors/NephioTokenEditor/NephioTokenEditor.tsx b/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditors/NephioTokenEditor/NephioTokenEditor.tsx new file mode 100644 index 00000000..92a5b4a4 --- /dev/null +++ b/plugins/cad/src/components/ResourceEditorDialog/components/FirstClassEditors/NephioTokenEditor/NephioTokenEditor.tsx @@ -0,0 +1,107 @@ +/** + * Copyright 2024 The Nephio Authors. + * + * 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 { SelectItem } from '@backstage/core-components'; +import React, { useState } from 'react'; +import { Select } from '../../../../Controls'; +import { useSetStateAndCall } from '../../../../../hooks/useSetStateAndCall'; +import { dumpYaml, loadYaml } from '../../../../../utils/yaml'; +import { + NephioToken, + NephioTokenMetadata, + NephioTokenSpec, +} from '../../../../../types/Token'; +import { EditorAccordion, ResourceMetadataAccordion } from '../Controls'; +import { useEditorStyles } from '../styles'; + +const DELETION_POLICY_DEFAULT = 'default'; +const DELETION_POLICY_SELECT_ITEMS: SelectItem[] = [ + { value: DELETION_POLICY_DEFAULT, label: 'Default' }, + { value: 'delete', label: 'Delete' }, + { value: 'orphan', label: 'Orphan' }, +]; + +type OnUpdatedYamlFn = (yaml: string) => void; + +type ResourceEditorProps = { + yaml: string; + onUpdatedYaml: OnUpdatedYamlFn; +}; + +type State = { + metadata: NephioTokenMetadata; + spec: NephioTokenSpec; +}; + +const getNephioTokenDescription = (spec: NephioTokenSpec) => + `Deletion policy: ${ + spec.lifecycle?.deletionPolicy ?? DELETION_POLICY_DEFAULT + }`; + +export const NephioTokenEditor = ({ + yaml, + onUpdatedYaml, +}: ResourceEditorProps) => { + const classes = useEditorStyles(); + const resourceYaml = loadYaml(yaml) as NephioToken; + + const createResourceState = (): State => ({ + metadata: resourceYaml.metadata, + spec: resourceYaml.spec, + }); + + const [state, setState] = useState(createResourceState()); + const [expanded, setExpanded] = useState(); + + const setStateAndCall = useSetStateAndCall([state, setState], newState => { + onUpdatedYaml(dumpYaml({ ...resourceYaml, ...newState })); + }); + + return ( +
+ setStateAndCall(s => ({ ...s, metadata }))} + /> + + +