Skip to content

Commit

Permalink
Merge pull request #4955 from karthikjeeyar/vikramraj-4868
Browse files Browse the repository at this point in the history
Add credential management section in start pipeline modal.
  • Loading branch information
openshift-merge-robot committed Apr 16, 2020
2 parents 897779d + 563551d commit 40d1692
Show file tree
Hide file tree
Showing 15 changed files with 498 additions and 8 deletions.
12 changes: 12 additions & 0 deletions frontend/packages/dev-console/src/components/pipelines/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,15 @@ export enum VolumeTypes {
Secret = 'Secret',
PVC = 'PVC',
}

export enum SecretAnnotationId {
Git = 'git',
Image = 'docker',
}

export const SecretAnnotationType = {
[SecretAnnotationId.Git]: 'Git Server',
[SecretAnnotationId.Image]: 'Docker Registry',
};

export const PIPELINE_SERVICE_ACCOUNT = 'pipeline';
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.odc-pipeline-secret-section {
margin-top: var(--pf-global--spacer--sm);
padding-left: var(--pf-global--spacer--lg);
&__secret-form {
border: 1px dashed var(--pf-global--BorderColor--100);
padding: var(--pf-global--spacer--md);
}
&__secret-action {
margin-top: var(--pf-global--spacer--sm);
padding-left: 0;
}
}
.odc-pipeline-expandable-content {
margin-top: var(--pf-global--spacer--sm);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import * as React from 'react';
import { Formik, useField, useFormikContext, FormikValues } from 'formik';
import { PlusCircleIcon } from '@patternfly/react-icons';
import { Button } from '@patternfly/react-core';
import { ExpandCollapse } from '@console/internal/components/utils';
import { SecretType } from '@console/internal/components/secrets/create-secret';
import { SecretModel } from '@console/internal/models';
import { k8sCreate } from '@console/internal/module/k8s';
import SecretForm from './SecretForm';
import {
associateServiceAccountToSecret,
getSecretAnnotations,
} from '../../../utils/pipeline-utils';
import SecretsList from './SecretsList';
import { advancedSectionValidationSchema } from './pipelineForm-validation-utils';
import { SecretAnnotationId } from '../const';

import './PipelineSecretSection.scss';

const initialValues = {
secretName: '',
annotations: { key: SecretAnnotationId.Image, value: '' },
type: SecretType.dockerconfigjson,
formData: {},
};

type PipelineSecretSectionProps = {
namespace: string;
};

const PipelineSecretSection: React.FC<PipelineSecretSectionProps> = ({ namespace }) => {
const [secretOpenField] = useField<boolean>('secretOpen');
const { setFieldValue } = useFormikContext<FormikValues>();

const handleSubmit = (values, actions) => {
actions.setSubmitting(true);
const newSecret = {
apiVersion: SecretModel.apiVersion,
kind: SecretModel.kind,
metadata: {
name: values.secretName,
namespace,
annotations: getSecretAnnotations(values.annotations),
},
type: values.type,
stringData: values.formData,
};
k8sCreate(SecretModel, newSecret)
.then((resp) => {
actions.setSubmitting(false);
setFieldValue(secretOpenField.name, false);
associateServiceAccountToSecret(resp, namespace);
})
.catch((err) => {
actions.setSubmitting(false);
setFieldValue(secretOpenField.name, false);
actions.setStatus({ submitError: err.message });
});
};

const handleReset = (values, actions) => {
actions.resetForm({ values: initialValues, status: {} });
setFieldValue(secretOpenField.name, false);
};

return (
<ExpandCollapse textExpanded="Hide Credential Options" textCollapsed="Show Credential Options">
<div className="odc-pipeline-secret-section">
<p>The following secrets are available for all pipelines in this namespace:</p>
<div className="odc-pipeline-secret-section__secrets">
<SecretsList namespace={namespace} />
{secretOpenField.value ? (
<div className="odc-pipeline-secret-section__secret-form">
<Formik
initialValues={initialValues}
validationSchema={advancedSectionValidationSchema}
onSubmit={handleSubmit}
onReset={handleReset}
>
{(props) => <SecretForm {...props} />}
</Formik>
</div>
) : (
<Button
variant="link"
onClick={() => {
setFieldValue(secretOpenField.name, true);
}}
className="odc-pipeline-secret-section__secret-action"
icon={<PlusCircleIcon />}
>
Add Secret
</Button>
)}
</div>
</div>
</ExpandCollapse>
);
};

export default PipelineSecretSection;
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import * as React from 'react';
import { TextInputTypes } from '@patternfly/react-core';
import { InputField, DropdownField } from '@console/shared';
import { SecretAnnotationType } from '../const';

type SecretAnnotationParam = {
fieldName: string;
isReadOnly?: boolean;
};

const SecretAnnotation: React.FC<SecretAnnotationParam> = (props) => {
const { fieldName, isReadOnly = false } = props;
return (
<div className="row">
<div className="col-lg-6">
<DropdownField
name={`${fieldName}.key`}
items={SecretAnnotationType}
label="Access to"
disabled={isReadOnly}
fullWidth
required
/>
</div>
<div className="col-lg-6">
<InputField
name={`${fieldName}.value`}
type={TextInputTypes.text}
isReadOnly={isReadOnly}
label="Server URL"
required
/>
</div>
</div>
);
};

export default SecretAnnotation;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.odc-secret-form {
&__title {
margin-top: 0;
}
&__help-text {
margin: var(--pf-global--spacer--md) 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import * as React from 'react';
import * as _ from 'lodash';
import { FormikValues, useFormikContext } from 'formik';
import { ActionGroup, Button, ButtonVariant, TextInputTypes } from '@patternfly/react-core';
import { CheckIcon, CloseIcon } from '@patternfly/react-icons';
import { ButtonBar } from '@console/internal/components/utils';
import {
SecretType,
BasicAuthSubform,
SSHAuthSubform,
CreateConfigSubform,
} from '@console/internal/components/secrets/create-secret';
import { DropdownField, InputField } from '@console/shared';
import SecretAnnotation from './SecretAnnotation';
import './SecretForm.scss';

const authTypes = {
[SecretType.dockerconfigjson]: 'Image Registry Credentials',
[SecretType.basicAuth]: 'Basic Authentication',
[SecretType.sshAuth]: 'SSH Key',
};

const renderSecretForm = (
type: SecretType,
stringData: {
[key: string]: any;
},
onDataChanged: (value: string) => void,
) => {
switch (type) {
case SecretType.basicAuth:
return (
<BasicAuthSubform onChange={onDataChanged} stringData={stringData[SecretType.basicAuth]} />
);
case SecretType.sshAuth:
return (
<SSHAuthSubform onChange={onDataChanged} stringData={stringData[SecretType.sshAuth]} />
);
case SecretType.dockerconfigjson:
return (
<CreateConfigSubform
onChange={onDataChanged}
stringData={stringData[SecretType.dockerconfigjson]}
/>
);
default:
return null;
}
};

const SecretForm: React.FC<FormikValues> = ({
handleSubmit,
handleReset,
status,
isSubmitting,
}) => {
const { values, setFieldValue } = useFormikContext<FormikValues>();
const [stringData, setStringData] = React.useState({
[SecretType.basicAuth]: {},
[SecretType.sshAuth]: {},
[SecretType.dockerconfigjson]: {},
});

const setValues = (type: SecretType) => {
if (type === SecretType.dockerconfigjson) {
setFieldValue(
'formData',
_.mapValues({ '.dockerconfigjson': stringData[type] }, JSON.stringify),
);
} else {
setFieldValue('formData', stringData[type]);
}
};

const onDataChanged = (value: string) => {
setStringData(_.merge(stringData, { [values.type]: value }));
setValues(values.type);
};

return (
<div className="odc-secret-form">
<h1 className="odc-secret-form__title">Create Secret</h1>
<p className="odc-secret-form__help-text help-block">
Source secrets let you authenticate against a Git server.
</p>
<div className="form-group">
<InputField
type={TextInputTypes.text}
required
name="secretName"
label="Secret Name"
helpText="Unique name of the new secret."
/>
</div>
<div className="form-group">
<SecretAnnotation fieldName="annotations" />
</div>
<div className="form-group">
<DropdownField
name="type"
label="Authentication Type"
items={authTypes}
title={authTypes[values.type]}
onChange={(type: SecretType) => setValues(type)}
fullWidth
required
/>
</div>
{renderSecretForm(values.type, stringData, onDataChanged)}
<ButtonBar errorMessage={status && status.submitError} inProgress={isSubmitting}>
<ActionGroup className="pf-c-form pf-c-form__actions--right pf-c-form__group--no-top-margin">
<Button
type="button"
variant={ButtonVariant.link}
onClick={handleSubmit}
className="odc-pipeline-resource-param__action-btn"
aria-label="create"
>
<CheckIcon />
</Button>
<Button
type="button"
className="odc-pipeline-resource-param__action-btn"
variant={ButtonVariant.plain}
onClick={handleReset}
aria-label="close"
>
<CloseIcon />
</Button>
</ActionGroup>
</ButtonBar>
</div>
);
};

export default SecretForm;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.odc-secrets-list {
&__secretsItem {
margin-top: var(--pf-global--spacer--sm);
}
}

0 comments on commit 40d1692

Please sign in to comment.