mirror of
https://github.com/ansible/awx.git
synced 2026-03-10 14:09:28 -02:30
Merge pull request #6699 from mabashian/5879-gce
Adds support for GCE credentials in credential form(s) Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
This commit is contained in:
@@ -7,6 +7,7 @@ import FieldTooltip from './FieldTooltip';
|
|||||||
function FormField(props) {
|
function FormField(props) {
|
||||||
const {
|
const {
|
||||||
id,
|
id,
|
||||||
|
helperText,
|
||||||
name,
|
name,
|
||||||
label,
|
label,
|
||||||
tooltip,
|
tooltip,
|
||||||
@@ -25,6 +26,7 @@ function FormField(props) {
|
|||||||
{(type === 'textarea' && (
|
{(type === 'textarea' && (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
fieldId={id}
|
fieldId={id}
|
||||||
|
helperText={helperText}
|
||||||
helperTextInvalid={meta.error}
|
helperTextInvalid={meta.error}
|
||||||
isRequired={isRequired}
|
isRequired={isRequired}
|
||||||
isValid={isValid}
|
isValid={isValid}
|
||||||
@@ -46,6 +48,7 @@ function FormField(props) {
|
|||||||
)) || (
|
)) || (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
fieldId={id}
|
fieldId={id}
|
||||||
|
helperText={helperText}
|
||||||
helperTextInvalid={meta.error}
|
helperTextInvalid={meta.error}
|
||||||
isRequired={isRequired}
|
isRequired={isRequired}
|
||||||
isValid={isValid}
|
isValid={isValid}
|
||||||
@@ -70,6 +73,7 @@ function FormField(props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
FormField.propTypes = {
|
FormField.propTypes = {
|
||||||
|
helperText: PropTypes.string,
|
||||||
id: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
label: PropTypes.oneOfType([PropTypes.object, PropTypes.string]).isRequired,
|
label: PropTypes.oneOfType([PropTypes.object, PropTypes.string]).isRequired,
|
||||||
@@ -81,6 +85,7 @@ FormField.propTypes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
FormField.defaultProps = {
|
FormField.defaultProps = {
|
||||||
|
helperText: '',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
validate: () => {},
|
validate: () => {},
|
||||||
isRequired: false,
|
isRequired: false,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
mountWithContexts,
|
mountWithContexts,
|
||||||
waitForElement,
|
waitForElement,
|
||||||
} from '../../../testUtils/enzymeHelpers';
|
} from '../../../testUtils/enzymeHelpers';
|
||||||
import mockCredential from './shared/data.credential.json';
|
import mockCredential from './shared/data.scmCredential.json';
|
||||||
import mockOrgCredential from './shared/data.orgCredential.json';
|
import mockOrgCredential from './shared/data.orgCredential.json';
|
||||||
import Credential from './Credential';
|
import Credential from './Credential';
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,9 @@ function CredentialAdd({ me }) {
|
|||||||
try {
|
try {
|
||||||
const {
|
const {
|
||||||
data: { results: loadedCredentialTypes },
|
data: { results: loadedCredentialTypes },
|
||||||
} = await CredentialTypesAPI.read({ or__kind: ['scm', 'ssh'] });
|
} = await CredentialTypesAPI.read({
|
||||||
|
or__namespace: ['gce', 'scm', 'ssh'],
|
||||||
|
});
|
||||||
setCredentialTypes(loadedCredentialTypes);
|
setCredentialTypes(loadedCredentialTypes);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err);
|
setError(err);
|
||||||
@@ -65,7 +67,15 @@ function CredentialAdd({ me }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return <ContentLoading />;
|
return (
|
||||||
|
<PageSection>
|
||||||
|
<Card>
|
||||||
|
<CardBody>
|
||||||
|
<ContentLoading />
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
</PageSection>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<PageSection>
|
<PageSection>
|
||||||
|
|||||||
@@ -20,7 +20,9 @@ function CredentialEdit({ credential, me }) {
|
|||||||
try {
|
try {
|
||||||
const {
|
const {
|
||||||
data: { results: loadedCredentialTypes },
|
data: { results: loadedCredentialTypes },
|
||||||
} = await CredentialTypesAPI.read({ or__kind: ['scm', 'ssh'] });
|
} = await CredentialTypesAPI.read({
|
||||||
|
or__namespace: ['gce', 'scm', 'ssh'],
|
||||||
|
});
|
||||||
setCredentialTypes(loadedCredentialTypes);
|
setCredentialTypes(loadedCredentialTypes);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err);
|
setError(err);
|
||||||
|
|||||||
@@ -13,12 +13,17 @@ import {
|
|||||||
FormColumnLayout,
|
FormColumnLayout,
|
||||||
SubFormLayout,
|
SubFormLayout,
|
||||||
} from '../../../components/FormLayout';
|
} from '../../../components/FormLayout';
|
||||||
import { ManualSubForm, SourceControlSubForm } from './CredentialSubForms';
|
import {
|
||||||
|
GoogleComputeEngineSubForm,
|
||||||
|
ManualSubForm,
|
||||||
|
SourceControlSubForm,
|
||||||
|
} from './CredentialSubForms';
|
||||||
|
|
||||||
function CredentialFormFields({
|
function CredentialFormFields({
|
||||||
i18n,
|
i18n,
|
||||||
credentialTypes,
|
credentialTypes,
|
||||||
formik,
|
formik,
|
||||||
|
gceCredentialTypeId,
|
||||||
initialValues,
|
initialValues,
|
||||||
scmCredentialTypeId,
|
scmCredentialTypeId,
|
||||||
sshCredentialTypeId,
|
sshCredentialTypeId,
|
||||||
@@ -100,18 +105,18 @@ function CredentialFormFields({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
{formik.values.credential_type !== undefined &&
|
{credTypeField.value !== undefined && credTypeField.value !== '' && (
|
||||||
formik.values.credential_type !== '' && (
|
<SubFormLayout>
|
||||||
<SubFormLayout>
|
<Title size="md">{i18n._(t`Type Details`)}</Title>
|
||||||
<Title size="md">{i18n._(t`Type Details`)}</Title>
|
{
|
||||||
{
|
{
|
||||||
{
|
[gceCredentialTypeId]: <GoogleComputeEngineSubForm />,
|
||||||
[sshCredentialTypeId]: <ManualSubForm />,
|
[sshCredentialTypeId]: <ManualSubForm />,
|
||||||
[scmCredentialTypeId]: <SourceControlSubForm />,
|
[scmCredentialTypeId]: <SourceControlSubForm />,
|
||||||
}[formik.values.credential_type]
|
}[credTypeField.value]
|
||||||
}
|
}
|
||||||
</SubFormLayout>
|
</SubFormLayout>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -130,22 +135,26 @@ function CredentialForm({
|
|||||||
organization: credential?.summary_fields?.organization || null,
|
organization: credential?.summary_fields?.organization || null,
|
||||||
credential_type: credential.credential_type || '',
|
credential_type: credential.credential_type || '',
|
||||||
inputs: {
|
inputs: {
|
||||||
username: credential?.inputs?.username || '',
|
|
||||||
password: credential?.inputs?.password || '',
|
|
||||||
ssh_key_data: credential?.inputs?.ssh_key_data || '',
|
|
||||||
ssh_public_key_data: credential?.inputs?.ssh_public_key_data || '',
|
|
||||||
ssh_key_unlock: credential?.inputs?.ssh_key_unlock || '',
|
|
||||||
become_method: credential?.inputs?.become_method || '',
|
become_method: credential?.inputs?.become_method || '',
|
||||||
become_username: credential?.inputs?.become_username || '',
|
|
||||||
become_password: credential?.inputs?.become_password || '',
|
become_password: credential?.inputs?.become_password || '',
|
||||||
|
become_username: credential?.inputs?.become_username || '',
|
||||||
|
password: credential?.inputs?.password || '',
|
||||||
|
project: credential?.inputs?.project || '',
|
||||||
|
ssh_key_data: credential?.inputs?.ssh_key_data || '',
|
||||||
|
ssh_key_unlock: credential?.inputs?.ssh_key_unlock || '',
|
||||||
|
ssh_public_key_data: credential?.inputs?.ssh_public_key_data || '',
|
||||||
|
username: credential?.inputs?.username || '',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const scmCredentialTypeId = Object.keys(credentialTypes)
|
const scmCredentialTypeId = Object.keys(credentialTypes)
|
||||||
.filter(key => credentialTypes[key].kind === 'scm')
|
.filter(key => credentialTypes[key].namespace === 'scm')
|
||||||
.map(key => credentialTypes[key].id)[0];
|
.map(key => credentialTypes[key].id)[0];
|
||||||
const sshCredentialTypeId = Object.keys(credentialTypes)
|
const sshCredentialTypeId = Object.keys(credentialTypes)
|
||||||
.filter(key => credentialTypes[key].kind === 'ssh')
|
.filter(key => credentialTypes[key].namespace === 'ssh')
|
||||||
|
.map(key => credentialTypes[key].id)[0];
|
||||||
|
const gceCredentialTypeId = Object.keys(credentialTypes)
|
||||||
|
.filter(key => credentialTypes[key].namespace === 'gce')
|
||||||
.map(key => credentialTypes[key].id)[0];
|
.map(key => credentialTypes[key].id)[0];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -168,6 +177,7 @@ function CredentialForm({
|
|||||||
'become_username',
|
'become_username',
|
||||||
'become_password',
|
'become_password',
|
||||||
];
|
];
|
||||||
|
const gceKeys = ['username', 'ssh_key_data', 'project'];
|
||||||
if (parseInt(values.credential_type, 10) === scmCredentialTypeId) {
|
if (parseInt(values.credential_type, 10) === scmCredentialTypeId) {
|
||||||
Object.keys(values.inputs).forEach(key => {
|
Object.keys(values.inputs).forEach(key => {
|
||||||
if (scmKeys.indexOf(key) < 0) {
|
if (scmKeys.indexOf(key) < 0) {
|
||||||
@@ -182,6 +192,14 @@ function CredentialForm({
|
|||||||
delete values.inputs[key];
|
delete values.inputs[key];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else if (
|
||||||
|
parseInt(values.credential_type, 10) === gceCredentialTypeId
|
||||||
|
) {
|
||||||
|
Object.keys(values.inputs).forEach(key => {
|
||||||
|
if (gceKeys.indexOf(key) < 0) {
|
||||||
|
delete values.inputs[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
onSubmit(values);
|
onSubmit(values);
|
||||||
}}
|
}}
|
||||||
@@ -193,6 +211,7 @@ function CredentialForm({
|
|||||||
formik={formik}
|
formik={formik}
|
||||||
initialValues={initialValues}
|
initialValues={initialValues}
|
||||||
credentialTypes={credentialTypes}
|
credentialTypes={credentialTypes}
|
||||||
|
gceCredentialTypeId={gceCredentialTypeId}
|
||||||
scmCredentialTypeId={scmCredentialTypeId}
|
scmCredentialTypeId={scmCredentialTypeId}
|
||||||
sshCredentialTypeId={sshCredentialTypeId}
|
sshCredentialTypeId={sshCredentialTypeId}
|
||||||
{...rest}
|
{...rest}
|
||||||
|
|||||||
@@ -1,348 +1,25 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { act } from 'react-dom/test-utils';
|
import { act } from 'react-dom/test-utils';
|
||||||
import { mountWithContexts } from '../../../../testUtils/enzymeHelpers';
|
import { mountWithContexts } from '../../../../testUtils/enzymeHelpers';
|
||||||
|
import machineCredential from './data.machineCredential.json';
|
||||||
|
import gceCredential from './data.gceCredential.json';
|
||||||
|
import scmCredential from './data.scmCredential.json';
|
||||||
|
import credentialTypes from './data.credentialTypes.json';
|
||||||
import CredentialForm from './CredentialForm';
|
import CredentialForm from './CredentialForm';
|
||||||
|
|
||||||
const machineCredential = {
|
jest.mock('../../../api');
|
||||||
id: 3,
|
|
||||||
type: 'credential',
|
|
||||||
url: '/api/v2/credentials/3/',
|
|
||||||
related: {
|
|
||||||
named_url: '/api/v2/credentials/oersdgfasf++Machine+ssh++org/',
|
|
||||||
created_by: '/api/v2/users/1/',
|
|
||||||
modified_by: '/api/v2/users/1/',
|
|
||||||
organization: '/api/v2/organizations/1/',
|
|
||||||
activity_stream: '/api/v2/credentials/3/activity_stream/',
|
|
||||||
access_list: '/api/v2/credentials/3/access_list/',
|
|
||||||
object_roles: '/api/v2/credentials/3/object_roles/',
|
|
||||||
owner_users: '/api/v2/credentials/3/owner_users/',
|
|
||||||
owner_teams: '/api/v2/credentials/3/owner_teams/',
|
|
||||||
copy: '/api/v2/credentials/3/copy/',
|
|
||||||
input_sources: '/api/v2/credentials/3/input_sources/',
|
|
||||||
credential_type: '/api/v2/credential_types/1/',
|
|
||||||
},
|
|
||||||
summary_fields: {
|
|
||||||
organization: {
|
|
||||||
id: 1,
|
|
||||||
name: 'org',
|
|
||||||
description: '',
|
|
||||||
},
|
|
||||||
credential_type: {
|
|
||||||
id: 1,
|
|
||||||
name: 'Machine',
|
|
||||||
description: '',
|
|
||||||
},
|
|
||||||
created_by: {
|
|
||||||
id: 1,
|
|
||||||
username: 'admin',
|
|
||||||
first_name: '',
|
|
||||||
last_name: '',
|
|
||||||
},
|
|
||||||
modified_by: {
|
|
||||||
id: 1,
|
|
||||||
username: 'admin',
|
|
||||||
first_name: '',
|
|
||||||
last_name: '',
|
|
||||||
},
|
|
||||||
object_roles: {
|
|
||||||
admin_role: {
|
|
||||||
description: 'Can manage all aspects of the credential',
|
|
||||||
name: 'Admin',
|
|
||||||
id: 36,
|
|
||||||
},
|
|
||||||
use_role: {
|
|
||||||
description: 'Can use the credential in a job template',
|
|
||||||
name: 'Use',
|
|
||||||
id: 37,
|
|
||||||
},
|
|
||||||
read_role: {
|
|
||||||
description: 'May view settings for the credential',
|
|
||||||
name: 'Read',
|
|
||||||
id: 38,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
user_capabilities: {
|
|
||||||
edit: true,
|
|
||||||
delete: true,
|
|
||||||
copy: true,
|
|
||||||
use: true,
|
|
||||||
},
|
|
||||||
owners: [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
type: 'user',
|
|
||||||
name: 'admin',
|
|
||||||
description: ' ',
|
|
||||||
url: '/api/v2/users/1/',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
type: 'organization',
|
|
||||||
name: 'org',
|
|
||||||
description: '',
|
|
||||||
url: '/api/v2/organizations/1/',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
created: '2020-02-18T15:35:04.563928Z',
|
|
||||||
modified: '2020-02-18T15:35:04.563957Z',
|
|
||||||
name: 'oersdgfasf',
|
|
||||||
description: '',
|
|
||||||
organization: 1,
|
|
||||||
credential_type: 1,
|
|
||||||
inputs: {},
|
|
||||||
kind: 'ssh',
|
|
||||||
cloud: false,
|
|
||||||
kubernetes: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
const sourceControlCredential = {
|
|
||||||
id: 4,
|
|
||||||
type: 'credential',
|
|
||||||
url: '/api/v2/credentials/4/',
|
|
||||||
related: {
|
|
||||||
named_url: '/api/v2/credentials/joijoij++Source Control+scm++/',
|
|
||||||
created_by: '/api/v2/users/1/',
|
|
||||||
modified_by: '/api/v2/users/1/',
|
|
||||||
activity_stream: '/api/v2/credentials/4/activity_stream/',
|
|
||||||
access_list: '/api/v2/credentials/4/access_list/',
|
|
||||||
object_roles: '/api/v2/credentials/4/object_roles/',
|
|
||||||
owner_users: '/api/v2/credentials/4/owner_users/',
|
|
||||||
owner_teams: '/api/v2/credentials/4/owner_teams/',
|
|
||||||
copy: '/api/v2/credentials/4/copy/',
|
|
||||||
input_sources: '/api/v2/credentials/4/input_sources/',
|
|
||||||
credential_type: '/api/v2/credential_types/2/',
|
|
||||||
user: '/api/v2/users/1/',
|
|
||||||
},
|
|
||||||
summary_fields: {
|
|
||||||
credential_type: {
|
|
||||||
id: 2,
|
|
||||||
name: 'Source Control',
|
|
||||||
description: '',
|
|
||||||
},
|
|
||||||
created_by: {
|
|
||||||
id: 1,
|
|
||||||
username: 'admin',
|
|
||||||
first_name: '',
|
|
||||||
last_name: '',
|
|
||||||
},
|
|
||||||
modified_by: {
|
|
||||||
id: 1,
|
|
||||||
username: 'admin',
|
|
||||||
first_name: '',
|
|
||||||
last_name: '',
|
|
||||||
},
|
|
||||||
object_roles: {
|
|
||||||
admin_role: {
|
|
||||||
description: 'Can manage all aspects of the credential',
|
|
||||||
name: 'Admin',
|
|
||||||
id: 39,
|
|
||||||
},
|
|
||||||
use_role: {
|
|
||||||
description: 'Can use the credential in a job template',
|
|
||||||
name: 'Use',
|
|
||||||
id: 40,
|
|
||||||
},
|
|
||||||
read_role: {
|
|
||||||
description: 'May view settings for the credential',
|
|
||||||
name: 'Read',
|
|
||||||
id: 41,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
user_capabilities: {
|
|
||||||
edit: true,
|
|
||||||
delete: true,
|
|
||||||
copy: true,
|
|
||||||
use: true,
|
|
||||||
},
|
|
||||||
owners: [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
type: 'user',
|
|
||||||
name: 'admin',
|
|
||||||
description: ' ',
|
|
||||||
url: '/api/v2/users/1/',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
created: '2020-02-18T16:03:01.366287Z',
|
|
||||||
modified: '2020-02-18T16:03:01.366315Z',
|
|
||||||
name: 'joijoij',
|
|
||||||
description: 'ojiojojo',
|
|
||||||
organization: null,
|
|
||||||
credential_type: 2,
|
|
||||||
inputs: {
|
|
||||||
ssh_key_unlock: '$encrypted$',
|
|
||||||
},
|
|
||||||
kind: 'scm',
|
|
||||||
cloud: false,
|
|
||||||
kubernetes: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
const credentialTypes = [
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
type: 'credential_type',
|
|
||||||
url: '/api/v2/credential_types/2/',
|
|
||||||
related: {
|
|
||||||
credentials: '/api/v2/credential_types/2/credentials/',
|
|
||||||
activity_stream: '/api/v2/credential_types/2/activity_stream/',
|
|
||||||
},
|
|
||||||
summary_fields: {
|
|
||||||
user_capabilities: {
|
|
||||||
edit: false,
|
|
||||||
delete: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
created: '2020-02-12T19:42:43.551238Z',
|
|
||||||
modified: '2020-02-12T19:43:03.164800Z',
|
|
||||||
name: 'Source Control',
|
|
||||||
description: '',
|
|
||||||
kind: 'scm',
|
|
||||||
namespace: 'scm',
|
|
||||||
managed_by_tower: true,
|
|
||||||
inputs: {
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
id: 'username',
|
|
||||||
label: 'Username',
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'password',
|
|
||||||
label: 'Password',
|
|
||||||
type: 'string',
|
|
||||||
secret: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'ssh_key_data',
|
|
||||||
label: 'Source Control Private Key',
|
|
||||||
type: 'string',
|
|
||||||
format: 'ssh_private_key',
|
|
||||||
secret: true,
|
|
||||||
multiline: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'ssh_key_unlock',
|
|
||||||
label: 'Private Key Passphrase',
|
|
||||||
type: 'string',
|
|
||||||
secret: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
injectors: {},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
type: 'credential_type',
|
|
||||||
url: '/api/v2/credential_types/1/',
|
|
||||||
related: {
|
|
||||||
credentials: '/api/v2/credential_types/1/credentials/',
|
|
||||||
activity_stream: '/api/v2/credential_types/1/activity_stream/',
|
|
||||||
},
|
|
||||||
summary_fields: {
|
|
||||||
user_capabilities: {
|
|
||||||
edit: false,
|
|
||||||
delete: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
created: '2020-02-12T19:42:43.539626Z',
|
|
||||||
modified: '2020-02-12T19:43:03.159739Z',
|
|
||||||
name: 'Machine',
|
|
||||||
description: '',
|
|
||||||
kind: 'ssh',
|
|
||||||
namespace: 'ssh',
|
|
||||||
managed_by_tower: true,
|
|
||||||
inputs: {
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
id: 'username',
|
|
||||||
label: 'Username',
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'password',
|
|
||||||
label: 'Password',
|
|
||||||
type: 'string',
|
|
||||||
secret: true,
|
|
||||||
ask_at_runtime: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'ssh_key_data',
|
|
||||||
label: 'SSH Private Key',
|
|
||||||
type: 'string',
|
|
||||||
format: 'ssh_private_key',
|
|
||||||
secret: true,
|
|
||||||
multiline: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'ssh_public_key_data',
|
|
||||||
label: 'Signed SSH Certificate',
|
|
||||||
type: 'string',
|
|
||||||
multiline: true,
|
|
||||||
secret: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'ssh_key_unlock',
|
|
||||||
label: 'Private Key Passphrase',
|
|
||||||
type: 'string',
|
|
||||||
secret: true,
|
|
||||||
ask_at_runtime: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'become_method',
|
|
||||||
label: 'Privilege Escalation Method',
|
|
||||||
type: 'string',
|
|
||||||
help_text:
|
|
||||||
'Specify a method for "become" operations. This is equivalent to specifying the --become-method Ansible parameter.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'become_username',
|
|
||||||
label: 'Privilege Escalation Username',
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'become_password',
|
|
||||||
label: 'Privilege Escalation Password',
|
|
||||||
type: 'string',
|
|
||||||
secret: true,
|
|
||||||
ask_at_runtime: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
injectors: {},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
describe('<CredentialForm />', () => {
|
describe('<CredentialForm />', () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
let onCancel;
|
const onCancel = jest.fn();
|
||||||
let onSubmit;
|
const onSubmit = jest.fn();
|
||||||
|
|
||||||
const addFieldExpects = () => {
|
const addFieldExpects = () => {
|
||||||
|
expect(wrapper.find('FormGroup').length).toBe(4);
|
||||||
expect(wrapper.find('FormGroup[label="Name"]').length).toBe(1);
|
expect(wrapper.find('FormGroup[label="Name"]').length).toBe(1);
|
||||||
expect(wrapper.find('FormGroup[label="Description"]').length).toBe(1);
|
expect(wrapper.find('FormGroup[label="Description"]').length).toBe(1);
|
||||||
expect(wrapper.find('FormGroup[label="Organization"]').length).toBe(1);
|
expect(wrapper.find('FormGroup[label="Organization"]').length).toBe(1);
|
||||||
expect(wrapper.find('FormGroup[label="Credential Type"]').length).toBe(1);
|
expect(wrapper.find('FormGroup[label="Credential Type"]').length).toBe(1);
|
||||||
expect(wrapper.find('FormGroup[label="Username"]').length).toBe(0);
|
|
||||||
expect(wrapper.find('FormGroup[label="Password"]').length).toBe(0);
|
|
||||||
expect(wrapper.find('FormGroup[label="SSH Private Key"]').length).toBe(0);
|
|
||||||
expect(
|
|
||||||
wrapper.find('FormGroup[label="Signed SSH Certificate"]').length
|
|
||||||
).toBe(0);
|
|
||||||
expect(
|
|
||||||
wrapper.find('FormGroup[label="Private Key Passphrase"]').length
|
|
||||||
).toBe(0);
|
|
||||||
expect(
|
|
||||||
wrapper.find('FormGroup[label="Privelege Escalation Method"]').length
|
|
||||||
).toBe(0);
|
|
||||||
expect(
|
|
||||||
wrapper.find('FormGroup[label="Privilege Escalation Username"]').length
|
|
||||||
).toBe(0);
|
|
||||||
expect(
|
|
||||||
wrapper.find('FormGroup[label="Privilege Escalation Password"]').length
|
|
||||||
).toBe(0);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const machineFieldExpects = () => {
|
const machineFieldExpects = () => {
|
||||||
@@ -371,6 +48,7 @@ describe('<CredentialForm />', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const sourceFieldExpects = () => {
|
const sourceFieldExpects = () => {
|
||||||
|
expect(wrapper.find('FormGroup').length).toBe(8);
|
||||||
expect(wrapper.find('FormGroup[label="Name"]').length).toBe(1);
|
expect(wrapper.find('FormGroup[label="Name"]').length).toBe(1);
|
||||||
expect(wrapper.find('FormGroup[label="Description"]').length).toBe(1);
|
expect(wrapper.find('FormGroup[label="Description"]').length).toBe(1);
|
||||||
expect(wrapper.find('FormGroup[label="Organization"]').length).toBe(1);
|
expect(wrapper.find('FormGroup[label="Organization"]').length).toBe(1);
|
||||||
@@ -378,139 +56,218 @@ describe('<CredentialForm />', () => {
|
|||||||
expect(wrapper.find('FormGroup[label="Username"]').length).toBe(1);
|
expect(wrapper.find('FormGroup[label="Username"]').length).toBe(1);
|
||||||
expect(wrapper.find('FormGroup[label="Password"]').length).toBe(1);
|
expect(wrapper.find('FormGroup[label="Password"]').length).toBe(1);
|
||||||
expect(wrapper.find('FormGroup[label="SSH Private Key"]').length).toBe(1);
|
expect(wrapper.find('FormGroup[label="SSH Private Key"]').length).toBe(1);
|
||||||
expect(
|
|
||||||
wrapper.find('FormGroup[label="Signed SSH Certificate"]').length
|
|
||||||
).toBe(0);
|
|
||||||
expect(
|
expect(
|
||||||
wrapper.find('FormGroup[label="Private Key Passphrase"]').length
|
wrapper.find('FormGroup[label="Private Key Passphrase"]').length
|
||||||
).toBe(1);
|
).toBe(1);
|
||||||
expect(
|
|
||||||
wrapper.find('FormGroup[label="Privelege Escalation Method"]').length
|
|
||||||
).toBe(0);
|
|
||||||
expect(
|
|
||||||
wrapper.find('FormGroup[label="Privilege Escalation Username"]').length
|
|
||||||
).toBe(0);
|
|
||||||
expect(
|
|
||||||
wrapper.find('FormGroup[label="Privilege Escalation Password"]').length
|
|
||||||
).toBe(0);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
const gceFieldExpects = () => {
|
||||||
onCancel = jest.fn();
|
expect(wrapper.find('FormGroup').length).toBe(8);
|
||||||
onSubmit = jest.fn();
|
expect(wrapper.find('FormGroup[label="Name"]').length).toBe(1);
|
||||||
wrapper = mountWithContexts(
|
expect(wrapper.find('FormGroup[label="Description"]').length).toBe(1);
|
||||||
<CredentialForm
|
expect(wrapper.find('FormGroup[label="Organization"]').length).toBe(1);
|
||||||
onCancel={onCancel}
|
expect(wrapper.find('FormGroup[label="Credential Type"]').length).toBe(1);
|
||||||
onSubmit={onSubmit}
|
expect(
|
||||||
credential={machineCredential}
|
wrapper.find('FormGroup[label="Service account JSON file"]').length
|
||||||
credentialTypes={credentialTypes}
|
).toBe(1);
|
||||||
/>
|
expect(
|
||||||
);
|
wrapper.find('FormGroup[label="Service account email address"]').length
|
||||||
});
|
).toBe(1);
|
||||||
|
expect(wrapper.find('FormGroup[label="Project"]').length).toBe(1);
|
||||||
|
expect(wrapper.find('FormGroup[label="RSA private key"]').length).toBe(1);
|
||||||
|
};
|
||||||
|
|
||||||
afterEach(() => {
|
describe('Add', () => {
|
||||||
wrapper.unmount();
|
beforeAll(async () => {
|
||||||
});
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
test('Initially renders successfully', () => {
|
<CredentialForm
|
||||||
expect(wrapper.length).toBe(1);
|
onCancel={onCancel}
|
||||||
});
|
onSubmit={onSubmit}
|
||||||
|
credentialTypes={credentialTypes}
|
||||||
test('should display form fields on add properly', () => {
|
/>
|
||||||
wrapper = mountWithContexts(
|
);
|
||||||
<CredentialForm
|
|
||||||
onCancel={onCancel}
|
|
||||||
onSubmit={onSubmit}
|
|
||||||
credentialTypes={credentialTypes}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
addFieldExpects();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should display form fields for machine credential properly', () => {
|
|
||||||
wrapper = mountWithContexts(
|
|
||||||
<CredentialForm
|
|
||||||
onCancel={onCancel}
|
|
||||||
onSubmit={onSubmit}
|
|
||||||
credential={machineCredential}
|
|
||||||
credentialTypes={credentialTypes}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
machineFieldExpects();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should display form fields for source control credential properly', () => {
|
|
||||||
wrapper = mountWithContexts(
|
|
||||||
<CredentialForm
|
|
||||||
onCancel={onCancel}
|
|
||||||
onSubmit={onSubmit}
|
|
||||||
credential={sourceControlCredential}
|
|
||||||
credentialTypes={credentialTypes}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
sourceFieldExpects();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should update form values', async () => {
|
|
||||||
// name and description change
|
|
||||||
act(() => {
|
|
||||||
wrapper.find('input#credential-name').simulate('change', {
|
|
||||||
target: { value: 'new Foo', name: 'name' },
|
|
||||||
});
|
|
||||||
wrapper.find('input#credential-description').simulate('change', {
|
|
||||||
target: { value: 'new Bar', name: 'description' },
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
wrapper.update();
|
afterAll(() => {
|
||||||
expect(wrapper.find('input#credential-name').prop('value')).toEqual(
|
wrapper.unmount();
|
||||||
'new Foo'
|
});
|
||||||
);
|
test('should display form fields on add properly', async () => {
|
||||||
expect(wrapper.find('input#credential-description').prop('value')).toEqual(
|
addFieldExpects();
|
||||||
'new Bar'
|
});
|
||||||
);
|
test('should update form values', async () => {
|
||||||
// organization change
|
// name and description change
|
||||||
act(() => {
|
await act(async () => {
|
||||||
wrapper.find('OrganizationLookup').invoke('onBlur')();
|
wrapper.find('input#credential-name').simulate('change', {
|
||||||
wrapper.find('OrganizationLookup').invoke('onChange')({
|
target: { value: 'new Foo', name: 'name' },
|
||||||
|
});
|
||||||
|
wrapper.find('input#credential-description').simulate('change', {
|
||||||
|
target: { value: 'new Bar', name: 'description' },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
expect(wrapper.find('input#credential-name').prop('value')).toEqual(
|
||||||
|
'new Foo'
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
wrapper.find('input#credential-description').prop('value')
|
||||||
|
).toEqual('new Bar');
|
||||||
|
// organization change
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('OrganizationLookup').invoke('onBlur')();
|
||||||
|
wrapper.find('OrganizationLookup').invoke('onChange')({
|
||||||
|
id: 3,
|
||||||
|
name: 'organization',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
expect(wrapper.find('OrganizationLookup').prop('value')).toEqual({
|
||||||
id: 3,
|
id: 3,
|
||||||
name: 'organization',
|
name: 'organization',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
wrapper.update();
|
test('should display cred type subform when scm type select has a value', async () => {
|
||||||
expect(wrapper.find('OrganizationLookup').prop('value')).toEqual({
|
await act(async () => {
|
||||||
id: 3,
|
await wrapper
|
||||||
name: 'organization',
|
.find('AnsibleSelect[id="credential_type"]')
|
||||||
|
.invoke('onChange')(null, 1);
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
machineFieldExpects();
|
||||||
|
await act(async () => {
|
||||||
|
await wrapper
|
||||||
|
.find('AnsibleSelect[id="credential_type"]')
|
||||||
|
.invoke('onChange')(null, 2);
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
sourceFieldExpects();
|
||||||
|
});
|
||||||
|
test('should update expected fields when gce service account json file uploaded', async () => {
|
||||||
|
await act(async () => {
|
||||||
|
await wrapper
|
||||||
|
.find('AnsibleSelect[id="credential_type"]')
|
||||||
|
.invoke('onChange')(null, 10);
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
gceFieldExpects();
|
||||||
|
expect(wrapper.find('input#credential-username').prop('value')).toBe('');
|
||||||
|
expect(wrapper.find('input#credential-project').prop('value')).toBe('');
|
||||||
|
expect(wrapper.find('textarea#credential-sshKeyData').prop('value')).toBe(
|
||||||
|
''
|
||||||
|
);
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('FileUpload').invoke('onChange')({
|
||||||
|
name: 'foo.json',
|
||||||
|
text: () =>
|
||||||
|
'{"client_email":"testemail@ansible.com","project_id":"test123","private_key":"-----BEGIN PRIVATE KEY-----\\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\\n-----END PRIVATE KEY-----\\n"}',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
expect(wrapper.find('input#credential-username').prop('value')).toBe(
|
||||||
|
'testemail@ansible.com'
|
||||||
|
);
|
||||||
|
expect(wrapper.find('input#credential-project').prop('value')).toBe(
|
||||||
|
'test123'
|
||||||
|
);
|
||||||
|
expect(wrapper.find('textarea#credential-sshKeyData').prop('value')).toBe(
|
||||||
|
'-----BEGIN PRIVATE KEY-----\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n-----END PRIVATE KEY-----\n'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test('should clear expected fields when file clear button clicked', async () => {
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('FileUploadField').invoke('onClearButtonClick')();
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
expect(wrapper.find('input#credential-username').prop('value')).toBe('');
|
||||||
|
expect(wrapper.find('input#credential-project').prop('value')).toBe('');
|
||||||
|
expect(wrapper.find('textarea#credential-sshKeyData').prop('value')).toBe(
|
||||||
|
''
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test('should show error when error thrown parsing JSON', async () => {
|
||||||
|
expect(wrapper.find('#credential-gce-file-helper').text()).toBe(
|
||||||
|
'Select a JSON formatted service account key to autopopulate the following fields.'
|
||||||
|
);
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('FileUpload').invoke('onChange')({
|
||||||
|
name: 'foo.json',
|
||||||
|
text: () => '{not good json}',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
expect(wrapper.find('#credential-gce-file-helper').text()).toBe(
|
||||||
|
'There was an error parsing the file. Please check the file formatting and try again.'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test('should call handleCancel when Cancel button is clicked', async () => {
|
||||||
|
expect(onCancel).not.toHaveBeenCalled();
|
||||||
|
wrapper.find('button[aria-label="Cancel"]').invoke('onClick')();
|
||||||
|
expect(onCancel).toBeCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should display cred type subform when scm type select has a value', async () => {
|
describe('Edit', () => {
|
||||||
wrapper = mountWithContexts(
|
afterEach(() => {
|
||||||
<CredentialForm
|
wrapper.unmount();
|
||||||
onCancel={onCancel}
|
|
||||||
onSubmit={onSubmit}
|
|
||||||
credentialTypes={credentialTypes}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
addFieldExpects();
|
|
||||||
await act(async () => {
|
|
||||||
await wrapper
|
|
||||||
.find('AnsibleSelect[id="credential_type"]')
|
|
||||||
.invoke('onChange')(null, 1);
|
|
||||||
});
|
});
|
||||||
wrapper.update();
|
test('Initially renders successfully', async () => {
|
||||||
machineFieldExpects();
|
await act(async () => {
|
||||||
await act(async () => {
|
wrapper = mountWithContexts(
|
||||||
await wrapper
|
<CredentialForm
|
||||||
.find('AnsibleSelect[id="credential_type"]')
|
onCancel={onCancel}
|
||||||
.invoke('onChange')(null, 2);
|
onSubmit={onSubmit}
|
||||||
});
|
credential={machineCredential}
|
||||||
wrapper.update();
|
credentialTypes={credentialTypes}
|
||||||
sourceFieldExpects();
|
/>
|
||||||
});
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test('should call handleCancel when Cancel button is clicked', async () => {
|
expect(wrapper.length).toBe(1);
|
||||||
expect(onCancel).not.toHaveBeenCalled();
|
});
|
||||||
wrapper.find('button[aria-label="Cancel"]').invoke('onClick')();
|
|
||||||
expect(onCancel).toBeCalled();
|
test('should display form fields for machine credential properly', async () => {
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<CredentialForm
|
||||||
|
onCancel={onCancel}
|
||||||
|
onSubmit={onSubmit}
|
||||||
|
credential={machineCredential}
|
||||||
|
credentialTypes={credentialTypes}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
machineFieldExpects();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should display form fields for source control credential properly', async () => {
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<CredentialForm
|
||||||
|
onCancel={onCancel}
|
||||||
|
onSubmit={onSubmit}
|
||||||
|
credential={scmCredential}
|
||||||
|
credentialTypes={credentialTypes}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
sourceFieldExpects();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should display form fields for gce credential properly', async () => {
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<CredentialForm
|
||||||
|
onCancel={onCancel}
|
||||||
|
onSubmit={onSubmit}
|
||||||
|
credential={gceCredential}
|
||||||
|
credentialTypes={credentialTypes}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
gceFieldExpects();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,123 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { withI18n } from '@lingui/react';
|
||||||
|
import { t } from '@lingui/macro';
|
||||||
|
import { useField } from 'formik';
|
||||||
|
import { FileUpload, FormGroup } from '@patternfly/react-core';
|
||||||
|
import FormField from '../../../../components/FormField';
|
||||||
|
import {
|
||||||
|
FormColumnLayout,
|
||||||
|
FormFullWidthLayout,
|
||||||
|
} from '../../../../components/FormLayout';
|
||||||
|
import { required } from '../../../../util/validators';
|
||||||
|
|
||||||
|
const GoogleComputeEngineSubForm = ({ i18n }) => {
|
||||||
|
const [fileError, setFileError] = useState(null);
|
||||||
|
const [filename, setFilename] = useState('');
|
||||||
|
const [file, setFile] = useState('');
|
||||||
|
const inputsUsernameHelpers = useField({
|
||||||
|
name: 'inputs.username',
|
||||||
|
})[2];
|
||||||
|
const inputsProjectHelpers = useField({
|
||||||
|
name: 'inputs.project',
|
||||||
|
})[2];
|
||||||
|
const inputsSSHKeyDataHelpers = useField({
|
||||||
|
name: 'inputs.ssh_key_data',
|
||||||
|
})[2];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormColumnLayout>
|
||||||
|
<FormGroup
|
||||||
|
fieldId="credential-gce-file"
|
||||||
|
isValid={!fileError}
|
||||||
|
label={i18n._(t`Service account JSON file`)}
|
||||||
|
helperText={i18n._(
|
||||||
|
t`Select a JSON formatted service account key to autopopulate the following fields.`
|
||||||
|
)}
|
||||||
|
helperTextInvalid={fileError}
|
||||||
|
>
|
||||||
|
<FileUpload
|
||||||
|
id="credential-gce-file"
|
||||||
|
value={file}
|
||||||
|
filename={filename}
|
||||||
|
filenamePlaceholder={i18n._(t`Choose a .json file`)}
|
||||||
|
onChange={async value => {
|
||||||
|
if (value) {
|
||||||
|
try {
|
||||||
|
setFile(value);
|
||||||
|
setFilename(value.name);
|
||||||
|
const fileText = await value.text();
|
||||||
|
const fileJSON = JSON.parse(fileText);
|
||||||
|
if (
|
||||||
|
!fileJSON.client_email &&
|
||||||
|
!fileJSON.project_id &&
|
||||||
|
!fileJSON.private_key
|
||||||
|
) {
|
||||||
|
setFileError(
|
||||||
|
i18n._(
|
||||||
|
t`Expected at least one of client_email, project_id or private_key to be present in the file.`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
inputsUsernameHelpers.setValue(fileJSON.client_email || '');
|
||||||
|
inputsProjectHelpers.setValue(fileJSON.project_id || '');
|
||||||
|
inputsSSHKeyDataHelpers.setValue(fileJSON.private_key || '');
|
||||||
|
setFileError(null);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
setFileError(
|
||||||
|
i18n._(
|
||||||
|
t`There was an error parsing the file. Please check the file formatting and try again.`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setFile('');
|
||||||
|
setFilename('');
|
||||||
|
inputsUsernameHelpers.setValue('');
|
||||||
|
inputsProjectHelpers.setValue('');
|
||||||
|
inputsSSHKeyDataHelpers.setValue('');
|
||||||
|
setFileError(null);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
dropzoneProps={{
|
||||||
|
accept: '.json',
|
||||||
|
onDropRejected: () => {
|
||||||
|
setFileError(
|
||||||
|
i18n._(
|
||||||
|
t`File upload rejected. Please select a single .json file.`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
<FormField
|
||||||
|
id="credential-username"
|
||||||
|
label={i18n._(t`Service account email address`)}
|
||||||
|
name="inputs.username"
|
||||||
|
type="email"
|
||||||
|
validate={required(null, i18n)}
|
||||||
|
isRequired
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
id="credential-project"
|
||||||
|
label={i18n._(t`Project`)}
|
||||||
|
name="inputs.project"
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
|
<FormFullWidthLayout>
|
||||||
|
<FormField
|
||||||
|
id="credential-sshKeyData"
|
||||||
|
label={i18n._(t`RSA private key`)}
|
||||||
|
name="inputs.ssh_key_data"
|
||||||
|
type="textarea"
|
||||||
|
rows={6}
|
||||||
|
validate={required(null, i18n)}
|
||||||
|
isRequired
|
||||||
|
/>
|
||||||
|
</FormFullWidthLayout>
|
||||||
|
</FormColumnLayout>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withI18n()(GoogleComputeEngineSubForm);
|
||||||
@@ -26,6 +26,7 @@ export const SSHKeyDataField = withI18n()(({ i18n }) => (
|
|||||||
label={i18n._(t`SSH Private Key`)}
|
label={i18n._(t`SSH Private Key`)}
|
||||||
name="inputs.ssh_key_data"
|
name="inputs.ssh_key_data"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
|
rows={6}
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|||||||
@@ -12,16 +12,14 @@ import {
|
|||||||
} from './SharedFields';
|
} from './SharedFields';
|
||||||
|
|
||||||
const SourceControlSubForm = () => (
|
const SourceControlSubForm = () => (
|
||||||
<>
|
<FormColumnLayout>
|
||||||
<FormColumnLayout>
|
<UsernameFormField />
|
||||||
<UsernameFormField />
|
<PasswordFormField />
|
||||||
<PasswordFormField />
|
<SSHKeyUnlockField />
|
||||||
<SSHKeyUnlockField />
|
|
||||||
</FormColumnLayout>
|
|
||||||
<FormFullWidthLayout>
|
<FormFullWidthLayout>
|
||||||
<SSHKeyDataField />
|
<SSHKeyDataField />
|
||||||
</FormFullWidthLayout>
|
</FormFullWidthLayout>
|
||||||
</>
|
</FormColumnLayout>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default withI18n()(SourceControlSubForm);
|
export default withI18n()(SourceControlSubForm);
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
|
export { default as GoogleComputeEngineSubForm } from './GoogleComputeEngineSubForm';
|
||||||
export { default as ManualSubForm } from './ManualSubForm';
|
export { default as ManualSubForm } from './ManualSubForm';
|
||||||
export { default as SourceControlSubForm } from './SourceControlSubForm';
|
export { default as SourceControlSubForm } from './SourceControlSubForm';
|
||||||
|
|||||||
@@ -0,0 +1,182 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"type": "credential_type",
|
||||||
|
"url": "/api/v2/credential_types/2/",
|
||||||
|
"related": {
|
||||||
|
"credentials": "/api/v2/credential_types/2/credentials/",
|
||||||
|
"activity_stream": "/api/v2/credential_types/2/activity_stream/"
|
||||||
|
},
|
||||||
|
"summary_fields": {
|
||||||
|
"user_capabilities": {
|
||||||
|
"edit": false,
|
||||||
|
"delete": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"created": "2020-02-12T19:42:43.551238Z",
|
||||||
|
"modified": "2020-02-12T19:43:03.164800Z",
|
||||||
|
"name": "Source Control",
|
||||||
|
"description": "",
|
||||||
|
"kind": "scm",
|
||||||
|
"namespace": "scm",
|
||||||
|
"managed_by_tower": true,
|
||||||
|
"inputs": {
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"id": "username",
|
||||||
|
"label": "Username",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "password",
|
||||||
|
"label": "Password",
|
||||||
|
"type": "string",
|
||||||
|
"secret": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ssh_key_data",
|
||||||
|
"label": "Source Control Private Key",
|
||||||
|
"type": "string",
|
||||||
|
"format": "ssh_private_key",
|
||||||
|
"secret": true,
|
||||||
|
"multiline": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ssh_key_unlock",
|
||||||
|
"label": "Private Key Passphrase",
|
||||||
|
"type": "string",
|
||||||
|
"secret": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"injectors": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"type": "credential_type",
|
||||||
|
"url": "/api/v2/credential_types/1/",
|
||||||
|
"related": {
|
||||||
|
"credentials": "/api/v2/credential_types/1/credentials/",
|
||||||
|
"activity_stream": "/api/v2/credential_types/1/activity_stream/"
|
||||||
|
},
|
||||||
|
"summary_fields": {
|
||||||
|
"user_capabilities": {
|
||||||
|
"edit": false,
|
||||||
|
"delete": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"created": "2020-02-12T19:42:43.539626Z",
|
||||||
|
"modified": "2020-02-12T19:43:03.159739Z",
|
||||||
|
"name": "Machine",
|
||||||
|
"description": "",
|
||||||
|
"kind": "ssh",
|
||||||
|
"namespace": "ssh",
|
||||||
|
"managed_by_tower": true,
|
||||||
|
"inputs": {
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"id": "username",
|
||||||
|
"label": "Username",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "password",
|
||||||
|
"label": "Password",
|
||||||
|
"type": "string",
|
||||||
|
"secret": true,
|
||||||
|
"ask_at_runtime": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ssh_key_data",
|
||||||
|
"label": "SSH Private Key",
|
||||||
|
"type": "string",
|
||||||
|
"format": "ssh_private_key",
|
||||||
|
"secret": true,
|
||||||
|
"multiline": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ssh_public_key_data",
|
||||||
|
"label": "Signed SSH Certificate",
|
||||||
|
"type": "string",
|
||||||
|
"multiline": true,
|
||||||
|
"secret": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ssh_key_unlock",
|
||||||
|
"label": "Private Key Passphrase",
|
||||||
|
"type": "string",
|
||||||
|
"secret": true,
|
||||||
|
"ask_at_runtime": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "become_method",
|
||||||
|
"label": "Privilege Escalation Method",
|
||||||
|
"type": "string",
|
||||||
|
"help_text": "Specify a method for \"become\" operations. This is equivalent to specifying the --become-method Ansible parameter."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "become_username",
|
||||||
|
"label": "Privilege Escalation Username",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "become_password",
|
||||||
|
"label": "Privilege Escalation Password",
|
||||||
|
"type": "string",
|
||||||
|
"secret": true,
|
||||||
|
"ask_at_runtime": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"injectors": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 10,
|
||||||
|
"type": "credential_type",
|
||||||
|
"url": "/api/v2/credential_types/10/",
|
||||||
|
"related": {
|
||||||
|
"credentials": "/api/v2/credential_types/10/credentials/",
|
||||||
|
"activity_stream": "/api/v2/credential_types/10/activity_stream/"
|
||||||
|
},
|
||||||
|
"summary_fields": {
|
||||||
|
"user_capabilities": {
|
||||||
|
"edit": false,
|
||||||
|
"delete": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"created": "2020-04-09T19:20:27.090665Z",
|
||||||
|
"modified": "2020-04-09T19:21:11.575214Z",
|
||||||
|
"name": "Google Compute Engine",
|
||||||
|
"description": "",
|
||||||
|
"kind": "cloud",
|
||||||
|
"namespace": "gce",
|
||||||
|
"managed_by_tower": true,
|
||||||
|
"inputs": {
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"id": "username",
|
||||||
|
"label": "Service Account Email Address",
|
||||||
|
"type": "string",
|
||||||
|
"help_text": "The email address assigned to the Google Compute Engine service account."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "project",
|
||||||
|
"label": "Project",
|
||||||
|
"type": "string",
|
||||||
|
"help_text": "The Project ID is the GCE assigned identification. It is often constructed as three words or two words followed by a three-digit number. Examples: project-id-000 and another-project-id"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ssh_key_data",
|
||||||
|
"label": "RSA Private Key",
|
||||||
|
"type": "string",
|
||||||
|
"format": "ssh_private_key",
|
||||||
|
"secret": true,
|
||||||
|
"multiline": true,
|
||||||
|
"help_text": "Paste the contents of the PEM file associated with the service account email."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"required": ["username", "ssh_key_data"]
|
||||||
|
},
|
||||||
|
"injectors": {}
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
{
|
||||||
|
"id": 9,
|
||||||
|
"type": "credential",
|
||||||
|
"url": "/api/v2/credentials/9/",
|
||||||
|
"related": {
|
||||||
|
"named_url": "/api/v2/credentials/a gce cred++Google Compute Engine+cloud++Default/",
|
||||||
|
"created_by": "/api/v2/users/1/",
|
||||||
|
"modified_by": "/api/v2/users/1/",
|
||||||
|
"organization": "/api/v2/organizations/4/",
|
||||||
|
"activity_stream": "/api/v2/credentials/9/activity_stream/",
|
||||||
|
"access_list": "/api/v2/credentials/9/access_list/",
|
||||||
|
"object_roles": "/api/v2/credentials/9/object_roles/",
|
||||||
|
"owner_users": "/api/v2/credentials/9/owner_users/",
|
||||||
|
"owner_teams": "/api/v2/credentials/9/owner_teams/",
|
||||||
|
"copy": "/api/v2/credentials/9/copy/",
|
||||||
|
"input_sources": "/api/v2/credentials/9/input_sources/",
|
||||||
|
"credential_type": "/api/v2/credential_types/10/"
|
||||||
|
},
|
||||||
|
"summary_fields": {
|
||||||
|
"organization": {
|
||||||
|
"id": 4,
|
||||||
|
"name": "Default",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"credential_type": {
|
||||||
|
"id": 10,
|
||||||
|
"name": "Google Compute Engine",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"created_by": {
|
||||||
|
"id": 1,
|
||||||
|
"username": "admin",
|
||||||
|
"first_name": "",
|
||||||
|
"last_name": ""
|
||||||
|
},
|
||||||
|
"modified_by": {
|
||||||
|
"id": 1,
|
||||||
|
"username": "admin",
|
||||||
|
"first_name": "",
|
||||||
|
"last_name": ""
|
||||||
|
},
|
||||||
|
"object_roles": {
|
||||||
|
"admin_role": {
|
||||||
|
"description": "Can manage all aspects of the credential",
|
||||||
|
"name": "Admin",
|
||||||
|
"id": 287
|
||||||
|
},
|
||||||
|
"use_role": {
|
||||||
|
"description": "Can use the credential in a job template",
|
||||||
|
"name": "Use",
|
||||||
|
"id": 288
|
||||||
|
},
|
||||||
|
"read_role": {
|
||||||
|
"description": "May view settings for the credential",
|
||||||
|
"name": "Read",
|
||||||
|
"id": 289
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"user_capabilities": {
|
||||||
|
"edit": true,
|
||||||
|
"delete": true,
|
||||||
|
"copy": true,
|
||||||
|
"use": true
|
||||||
|
},
|
||||||
|
"owners": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"type": "user",
|
||||||
|
"name": "admin",
|
||||||
|
"description": " ",
|
||||||
|
"url": "/api/v2/users/1/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"type": "organization",
|
||||||
|
"name": "Default",
|
||||||
|
"description": "",
|
||||||
|
"url": "/api/v2/organizations/4/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"created": "2020-04-13T17:33:27.625773Z",
|
||||||
|
"modified": "2020-04-13T17:33:27.625882Z",
|
||||||
|
"name": "a gce cred",
|
||||||
|
"description": "",
|
||||||
|
"organization": 4,
|
||||||
|
"credential_type": 10,
|
||||||
|
"inputs": {
|
||||||
|
"project": "test123",
|
||||||
|
"username": "test123.iam.gserviceaccount.com",
|
||||||
|
"ssh_key_data": "$encrypted$"
|
||||||
|
},
|
||||||
|
"kind": "gce",
|
||||||
|
"cloud": true,
|
||||||
|
"kubernetes": false
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"type": "credential",
|
||||||
|
"url": "/api/v2/credentials/3/",
|
||||||
|
"related": {
|
||||||
|
"named_url": "/api/v2/credentials/oersdgfasf++Machine+ssh++org/",
|
||||||
|
"created_by": "/api/v2/users/1/",
|
||||||
|
"modified_by": "/api/v2/users/1/",
|
||||||
|
"organization": "/api/v2/organizations/1/",
|
||||||
|
"activity_stream": "/api/v2/credentials/3/activity_stream/",
|
||||||
|
"access_list": "/api/v2/credentials/3/access_list/",
|
||||||
|
"object_roles": "/api/v2/credentials/3/object_roles/",
|
||||||
|
"owner_users": "/api/v2/credentials/3/owner_users/",
|
||||||
|
"owner_teams": "/api/v2/credentials/3/owner_teams/",
|
||||||
|
"copy": "/api/v2/credentials/3/copy/",
|
||||||
|
"input_sources": "/api/v2/credentials/3/input_sources/",
|
||||||
|
"credential_type": "/api/v2/credential_types/1/"
|
||||||
|
},
|
||||||
|
"summary_fields": {
|
||||||
|
"organization": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "org",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"credential_type": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "Machine",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"created_by": {
|
||||||
|
"id": 1,
|
||||||
|
"username": "admin",
|
||||||
|
"first_name": "",
|
||||||
|
"last_name": ""
|
||||||
|
},
|
||||||
|
"modified_by": {
|
||||||
|
"id": 1,
|
||||||
|
"username": "admin",
|
||||||
|
"first_name": "",
|
||||||
|
"last_name": ""
|
||||||
|
},
|
||||||
|
"object_roles": {
|
||||||
|
"admin_role": {
|
||||||
|
"description": "Can manage all aspects of the credential",
|
||||||
|
"name": "Admin",
|
||||||
|
"id": 36
|
||||||
|
},
|
||||||
|
"use_role": {
|
||||||
|
"description": "Can use the credential in a job template",
|
||||||
|
"name": "Use",
|
||||||
|
"id": 37
|
||||||
|
},
|
||||||
|
"read_role": {
|
||||||
|
"description": "May view settings for the credential",
|
||||||
|
"name": "Read",
|
||||||
|
"id": 38
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"user_capabilities": {
|
||||||
|
"edit": true,
|
||||||
|
"delete": true,
|
||||||
|
"copy": true,
|
||||||
|
"use": true
|
||||||
|
},
|
||||||
|
"owners": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"type": "user",
|
||||||
|
"name": "admin",
|
||||||
|
"description": " ",
|
||||||
|
"url": "/api/v2/users/1/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"type": "organization",
|
||||||
|
"name": "org",
|
||||||
|
"description": "",
|
||||||
|
"url": "/api/v2/organizations/1/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"created": "2020-02-18T15:35:04.563928Z",
|
||||||
|
"modified": "2020-02-18T15:35:04.563957Z",
|
||||||
|
"name": "oersdgfasf",
|
||||||
|
"description": "",
|
||||||
|
"organization": 1,
|
||||||
|
"credential_type": 1,
|
||||||
|
"inputs": {},
|
||||||
|
"kind": "ssh",
|
||||||
|
"cloud": false,
|
||||||
|
"kubernetes": false
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user