diff --git a/frontend/src/components/FieldList.tsx b/frontend/src/components/FieldList.tsx index dc37a03b23..75851bf90a 100644 --- a/frontend/src/components/FieldList.tsx +++ b/frontend/src/components/FieldList.tsx @@ -24,7 +24,7 @@ type InputFieldProps = { value: string; }; -const FieldListField = ({ options, onChange, value }: InputFieldProps) => { +export const FieldListField = ({ options, onChange, value }: InputFieldProps) => { const ComponentField = options.isPassword ? PasswordInput : TextInput; return ( diff --git a/frontend/src/concepts/pipelines/NoPipelineServer.tsx b/frontend/src/concepts/pipelines/NoPipelineServer.tsx index ce7d920ae2..2e1ac9aed9 100644 --- a/frontend/src/concepts/pipelines/NoPipelineServer.tsx +++ b/frontend/src/concepts/pipelines/NoPipelineServer.tsx @@ -16,11 +16,13 @@ type NoPipelineServerProps = { const NoPipelineServer: React.FC = ({ variant }) => ( } headingLevel="h3" /> - To import a pipeline, first create a pipeline server. + + To create and manage pipelines, first enable them by configuring a pipeline server. + diff --git a/frontend/src/concepts/pipelines/content/ViewPipelineServerModal.tsx b/frontend/src/concepts/pipelines/content/ViewPipelineServerModal.tsx index 75e5ff48d2..fb59f2149d 100644 --- a/frontend/src/concepts/pipelines/content/ViewPipelineServerModal.tsx +++ b/frontend/src/concepts/pipelines/content/ViewPipelineServerModal.tsx @@ -10,19 +10,10 @@ import { } from '@patternfly/react-core'; import { usePipelinesAPI } from '~/concepts/pipelines/context'; import PasswordHiddenText from '~/components/PasswordHiddenText'; -import useDataConnections from '~/pages/projects/screens/detail/data-connections/useDataConnections'; -import { DataConnection } from '~/pages/projects/types'; -import { useContextResourceData } from '~/utilities/useContextResourceData'; -import { - convertAWSSecretData, - getDataConnectionDisplayName, -} from '~/pages/projects/screens/detail/data-connections/utils'; import { dataEntryToRecord } from '~/utilities/dataEntryToRecord'; -import { AWS_KEYS } from '~/pages/projects/dataConnections/const'; import useNamespaceSecret from '~/concepts/projects/apiHooks/useNamespaceSecret'; import { EXTERNAL_DATABASE_SECRET } from '~/concepts/pipelines/content/configurePipelinesServer/const'; import { DSPipelineKind } from '~/k8sTypes'; - type ViewPipelineServerModalProps = { isOpen: boolean; onClose: () => void; @@ -35,22 +26,14 @@ const ViewPipelineServerModal: React.FC = ({ pipelineNamespaceCR, }) => { const { namespace } = usePipelinesAPI(); - const { data: dataConnections } = useContextResourceData( - useDataConnections(namespace), + const [pipelineResult] = useNamespaceSecret( + namespace, + pipelineNamespaceCR?.spec.objectStorage.externalStorage?.s3CredentialsSecret.secretName ?? '', ); + const pipelineSecret = dataEntryToRecord(pipelineResult?.values?.data ?? []); const [result] = useNamespaceSecret(namespace, EXTERNAL_DATABASE_SECRET.NAME); const databaseSecret = dataEntryToRecord(result?.values?.data ?? []); - const objectStorageDataConnection = dataConnections.find( - (dc) => - dc.data.metadata.name === - pipelineNamespaceCR?.spec.objectStorage?.externalStorage?.s3CredentialsSecret?.secretName, - ); - - const objectStorageRecord: Partial> = objectStorageDataConnection - ? dataEntryToRecord(convertAWSSecretData(objectStorageDataConnection)) - : {}; - return ( = ({ > {pipelineNamespaceCR && ( - {!!objectStorageDataConnection && - !!pipelineNamespaceCR?.spec.objectStorage?.externalStorage && - !!objectStorageRecord && ( + {!!pipelineNamespaceCR.spec.objectStorage && + !!pipelineNamespaceCR.spec.objectStorage.externalStorage && + !!pipelineNamespaceCR.spec.objectStorage.externalStorage.s3CredentialsSecret + .secretName && ( <> Object storage connection - - {/* TODO: is this the pipeline name or the secret name? */} - Name - - {getDataConnectionDisplayName(objectStorageDataConnection)} - - Access key - {objectStorageRecord?.AWS_ACCESS_KEY_ID} + {pipelineSecret?.AWS_ACCESS_KEY_ID || ''} Secret key - + @@ -106,10 +81,6 @@ const ViewPipelineServerModal: React.FC = ({ {pipelineNamespaceCR.spec.objectStorage.externalStorage?.bucket} - - Folder path - /pipelines - )} {!!pipelineNamespaceCR?.spec.database && diff --git a/frontend/src/concepts/pipelines/content/configurePipelinesServer/ConfigurePipelinesServerModal.tsx b/frontend/src/concepts/pipelines/content/configurePipelinesServer/ConfigurePipelinesServerModal.tsx index 67341d1bbe..ee4ee6a464 100644 --- a/frontend/src/concepts/pipelines/content/configurePipelinesServer/ConfigurePipelinesServerModal.tsx +++ b/frontend/src/concepts/pipelines/content/configurePipelinesServer/ConfigurePipelinesServerModal.tsx @@ -1,12 +1,10 @@ import * as React from 'react'; import { Alert, Button, Form, Modal, Stack, StackItem } from '@patternfly/react-core'; -import { EMPTY_AWS_SECRET_DATA } from '~/pages/projects/dataConnections/const'; import './ConfigurePipelinesServerModal.scss'; -import { convertAWSSecretData } from '~/pages/projects/screens/detail/data-connections/utils'; import { usePipelinesAPI } from '~/concepts/pipelines/context'; -import { isAWSValid } from '~/pages/projects/screens/spawner/spawnerUtils'; import { createPipelinesCR, deleteSecret } from '~/api'; import useDataConnections from '~/pages/projects/screens/detail/data-connections/useDataConnections'; +import { EMPTY_AWS_PIPELINE_DATA } from '~/pages/projects/dataConnections/const'; import { PipelinesDatabaseSection } from './PipelinesDatabaseSection'; import { ObjectStorageSection } from './ObjectStorageSection'; import { @@ -15,7 +13,7 @@ import { EMPTY_DATABASE_CONNECTION, EXTERNAL_DATABASE_SECRET, } from './const'; -import { configureDSPipelineResourceSpec } from './utils'; +import { configureDSPipelineResourceSpec, objectStorageIsValid } from './utils'; import { PipelineServerConfigType } from './types'; type ConfigurePipelinesServerModalProps = { @@ -25,7 +23,7 @@ type ConfigurePipelinesServerModalProps = { const FORM_DEFAULTS: PipelineServerConfigType = { database: { useDefault: true, value: EMPTY_DATABASE_CONNECTION }, - objectStorage: { useExisting: true, existingName: '', existingValue: EMPTY_AWS_SECRET_DATA }, + objectStorage: { newValue: EMPTY_AWS_PIPELINE_DATA }, }; export const ConfigurePipelinesServerModal: React.FC = ({ @@ -33,7 +31,7 @@ export const ConfigurePipelinesServerModal: React.FC { const { project, namespace } = usePipelinesAPI(); - const [dataConnections, , , refresh] = useDataConnections(namespace); + const [dataConnections, loaded, , refresh] = useDataConnections(namespace); const [fetching, setFetching] = React.useState(false); const [error, setError] = React.useState(null); const [config, setConfig] = React.useState(FORM_DEFAULTS); @@ -44,23 +42,18 @@ export const ConfigurePipelinesServerModal: React.FC { - const databaseIsValid = config.database.useDefault - ? true - : config.database.value.every(({ key, value }) => - DATABASE_CONNECTION_FIELDS.filter((field) => field.isRequired) - .map((field) => field.key) - .includes(key as DATABASE_CONNECTION_KEYS) - ? !!value - : true, - ); + const databaseIsValid = config.database.useDefault + ? true + : config.database.value.every(({ key, value }) => + DATABASE_CONNECTION_FIELDS.filter((field) => field.isRequired) + .map((field) => field.key) + .includes(key as DATABASE_CONNECTION_KEYS) + ? !!value + : true, + ); - const objectStorageIsValid = config.objectStorage.useExisting - ? !!config.objectStorage.existingName - : isAWSValid(config.objectStorage.newValue); - - return databaseIsValid && objectStorageIsValid; - }; + const objectIsValid = objectStorageIsValid(config.objectStorage.newValue); + const canSubmit = databaseIsValid && objectIsValid; const onBeforeClose = () => { onClose(); @@ -70,25 +63,9 @@ export const ConfigurePipelinesServerModal: React.FC { - let objectStorage: PipelineServerConfigType['objectStorage']; - if (config.objectStorage.useExisting) { - const existingName = config.objectStorage.existingName; - const existingValue = dataConnections?.find((dc) => dc.data.metadata.name === existingName); - if (existingValue) { - objectStorage = { - existingValue: convertAWSSecretData(existingValue), - existingName, - useExisting: true, - }; - } else { - throw new Error('Selected data connection does not exist'); - } - } else { - objectStorage = { - newValue: config.objectStorage.newValue, - useExisting: false, - }; - } + const objectStorage: PipelineServerConfigType['objectStorage'] = { + newValue: config.objectStorage.newValue, + }; setFetching(true); setError(null); @@ -121,17 +98,18 @@ export const ConfigurePipelinesServerModal: React.FC - Configure + Configure pipeline server , diff --git a/frontend/src/pages/projects/dataConnections/const.ts b/frontend/src/pages/projects/dataConnections/const.ts index 50987adb93..c85fea40ee 100644 --- a/frontend/src/pages/projects/dataConnections/const.ts +++ b/frontend/src/pages/projects/dataConnections/const.ts @@ -9,7 +9,12 @@ export enum AWS_KEYS { DEFAULT_REGION = 'AWS_DEFAULT_REGION', AWS_S3_BUCKET = 'AWS_S3_BUCKET', } - +export const PIPELINE_AWS_KEY = [ + AWS_KEYS.ACCESS_KEY_ID, + AWS_KEYS.SECRET_ACCESS_KEY, + AWS_KEYS.S3_ENDPOINT, + AWS_KEYS.AWS_S3_BUCKET, +]; export const AWS_FIELDS: FieldOptions[] = [ { key: AWS_KEYS.NAME, @@ -41,6 +46,29 @@ export const AWS_FIELDS: FieldOptions[] = [ label: 'Bucket', }, ]; +export const PIPELINE_AWS_FIELDS: FieldOptions[] = [ + { + key: AWS_KEYS.ACCESS_KEY_ID, + label: 'Access key', + isRequired: true, + }, + { + key: AWS_KEYS.SECRET_ACCESS_KEY, + label: 'Secret key', + isPassword: true, + isRequired: true, + }, + { + key: AWS_KEYS.S3_ENDPOINT, + label: 'Endpoint', + isRequired: true, + }, + { + key: AWS_KEYS.AWS_S3_BUCKET, + label: 'Bucket', + isRequired: true, + }, +]; export const EMPTY_AWS_SECRET_DATA: AWSDataEntry = [ { @@ -68,3 +96,21 @@ export const EMPTY_AWS_SECRET_DATA: AWSDataEntry = [ value: '', }, ]; +export const EMPTY_AWS_PIPELINE_DATA: AWSDataEntry = [ + { + key: AWS_KEYS.ACCESS_KEY_ID, + value: '', + }, + { + key: AWS_KEYS.SECRET_ACCESS_KEY, + value: '', + }, + { + key: AWS_KEYS.AWS_S3_BUCKET, + value: '', + }, + { + key: AWS_KEYS.S3_ENDPOINT, + value: '', + }, +];