Skip to content

Commit

Permalink
UI/database mysql (hashicorp#11532)
Browse files Browse the repository at this point in the history
* Add MySQL DB Support

* Add other versions of MySQL to database options

* Save incoming root_credentials_rotate_statements as root_rotation_statements for display

* Handle errors correctly on database connection form for edit

* Add tests for mysql database

* Add UI feature changelog
  • Loading branch information
hashishaw authored and jartek committed Sep 11, 2021
1 parent 5314ebd commit ed001dd
Show file tree
Hide file tree
Showing 7 changed files with 224 additions and 9 deletions.
3 changes: 3 additions & 0 deletions changelog/11532 | MySQL Database UI.txt
@@ -0,0 +1,3 @@
```release-note:feature
**MySQL Database UI**: The UI now supports adding and editing MySQL connections in the database secret engine
```
12 changes: 9 additions & 3 deletions ui/app/components/database-connection.js
Expand Up @@ -102,9 +102,15 @@ export default class DatabaseConnectionEdit extends Component {
evt.preventDefault();
let secret = this.args.model;
let secretId = secret.name;
secret.save().then(() => {
this.transitionToRoute(SHOW_ROUTE, secretId);
});
secret
.save()
.then(() => {
this.transitionToRoute(SHOW_ROUTE, secretId);
})
.catch(e => {
const errorMessage = getErrorMessage(e.errors);
this.flashMessages.danger(errorMessage);
});
}

@action
Expand Down
8 changes: 8 additions & 0 deletions ui/app/components/database-role-setting-form.js
Expand Up @@ -27,11 +27,19 @@ const STATEMENT_FIELDS = {
default: ['rotation_statements'],
'mongodb-database-plugin': [],
'mssql-database-plugin': [],
'mysql-database-plugin': [],
'mysql-aurora-database-plugin': [],
'mysql-rds-database-plugin': [],
'mysql-legacy-database-plugin': [],
},
dynamic: {
default: ['creation_statements', 'revocation_statements', 'rollback_statements', 'renew_statements'],
'mongodb-database-plugin': ['creation_statement', 'revocation_statement'],
'mssql-database-plugin': ['creation_statements', 'revocation_statements'],
'mysql-database-plugin': ['creation_statements', 'revocation_statements'],
'mysql-aurora-database-plugin': ['creation_statements', 'revocation_statements'],
'mysql-rds-database-plugin': ['creation_statements', 'revocation_statements'],
'mysql-legacy-database-plugin': ['creation_statements', 'revocation_statements'],
},
};
export default class DatabaseRoleSettingForm extends Component {
Expand Down
80 changes: 80 additions & 0 deletions ui/app/models/database/connection.js
Expand Up @@ -41,6 +41,86 @@ const AVAILABLE_PLUGIN_TYPES = [
{ attr: 'root_rotation_statements', group: 'statements' },
],
},
{
value: 'mysql-database-plugin',
displayName: 'MySQL/MariaDB',
fields: [
{ attr: 'plugin_name' },
{ attr: 'name' },
{ attr: 'verify_connection' },
{ attr: 'password_policy' },
{ attr: 'connection_url', group: 'pluginConfig' },
{ attr: 'username', group: 'pluginConfig', show: false },
{ attr: 'password', group: 'pluginConfig', show: false },
{ attr: 'max_open_connections', group: 'pluginConfig' },
{ attr: 'max_idle_connections', group: 'pluginConfig' },
{ attr: 'max_connection_lifetime', group: 'pluginConfig' },
{ attr: 'username_template', group: 'pluginConfig' },
{ attr: 'tls', group: 'pluginConfig', subgroup: 'TLS options' },
{ attr: 'tls_ca', group: 'pluginConfig', subgroup: 'TLS options' },
{ attr: 'root_rotation_statements', group: 'statements' },
],
},
{
value: 'mysql-aurora-database-plugin',
displayName: 'MySQL (Aurora)',
fields: [
{ attr: 'plugin_name' },
{ attr: 'name' },
{ attr: 'verify_connection' },
{ attr: 'password_policy' },
{ attr: 'connection_url', group: 'pluginConfig' },
{ attr: 'username', group: 'pluginConfig', show: false },
{ attr: 'password', group: 'pluginConfig', show: false },
{ attr: 'max_open_connections', group: 'pluginConfig' },
{ attr: 'max_idle_connections', group: 'pluginConfig' },
{ attr: 'max_connection_lifetime', group: 'pluginConfig' },
{ attr: 'username_template', group: 'pluginConfig' },
{ attr: 'tls', group: 'pluginConfig', subgroup: 'TLS options' },
{ attr: 'tls_ca', group: 'pluginConfig', subgroup: 'TLS options' },
{ attr: 'root_rotation_statements', group: 'statements' },
],
},
{
value: 'mysql-rds-database-plugin',
displayName: 'MySQL (RDS)',
fields: [
{ attr: 'plugin_name' },
{ attr: 'name' },
{ attr: 'verify_connection' },
{ attr: 'password_policy' },
{ attr: 'connection_url', group: 'pluginConfig' },
{ attr: 'username', group: 'pluginConfig', show: false },
{ attr: 'password', group: 'pluginConfig', show: false },
{ attr: 'max_open_connections', group: 'pluginConfig' },
{ attr: 'max_idle_connections', group: 'pluginConfig' },
{ attr: 'max_connection_lifetime', group: 'pluginConfig' },
{ attr: 'username_template', group: 'pluginConfig' },
{ attr: 'tls', group: 'pluginConfig', subgroup: 'TLS options' },
{ attr: 'tls_ca', group: 'pluginConfig', subgroup: 'TLS options' },
{ attr: 'root_rotation_statements', group: 'statements' },
],
},
{
value: 'mysql-legacy-database-plugin',
displayName: 'MySQL (Legacy)',
fields: [
{ attr: 'plugin_name' },
{ attr: 'name' },
{ attr: 'verify_connection' },
{ attr: 'password_policy' },
{ attr: 'connection_url', group: 'pluginConfig' },
{ attr: 'username', group: 'pluginConfig', show: false },
{ attr: 'password', group: 'pluginConfig', show: false },
{ attr: 'max_open_connections', group: 'pluginConfig' },
{ attr: 'max_idle_connections', group: 'pluginConfig' },
{ attr: 'max_connection_lifetime', group: 'pluginConfig' },
{ attr: 'username_template', group: 'pluginConfig' },
{ attr: 'tls', group: 'pluginConfig', subgroup: 'TLS options' },
{ attr: 'tls_ca', group: 'pluginConfig', subgroup: 'TLS options' },
{ attr: 'root_rotation_statements', group: 'statements' },
],
},
];

/**
Expand Down
6 changes: 5 additions & 1 deletion ui/app/serializers/database/connection.js
Expand Up @@ -19,13 +19,17 @@ export default RESTSerializer.extend({
return connections;
}
// Query single record response:
return {
let response = {
id: payload.id,
name: payload.id,
backend: payload.backend,
...payload.data,
...payload.data.connection_details,
};
if (payload.data.root_credentials_rotate_statements) {
response.root_rotation_statements = payload.data.root_credentials_rotate_statements;
}
return response;
},

normalizeResponse(store, primaryModelClass, payload, id, requestType) {
Expand Down
104 changes: 99 additions & 5 deletions ui/tests/acceptance/secrets/backend/database/secret-test.js
Expand Up @@ -47,19 +47,87 @@ const connectionTests = [
plugin: 'mongodb-database-plugin',
url: `mongodb://127.0.0.1:4321/test`,
requiredFields: async (assert, name) => {
assert.dom('[data-test-input="username').exists(`Username field exists for ${name}`);
assert.dom('[data-test-input="password').exists(`Password field exists for ${name}`);
assert.dom('[data-test-input="write_concern').exists(`Write concern field exists for ${name}`);
assert.dom('[data-test-input="username"]').exists(`Username field exists for ${name}`);
assert.dom('[data-test-input="password"]').exists(`Password field exists for ${name}`);
assert.dom('[data-test-input="write_concern"]').exists(`Write concern field exists for ${name}`);
assert.dom('[data-test-toggle-group="TLS options"]').exists('TLS options toggle exists');
assert
.dom('[data-test-input="root_rotation_statements"]')
.exists(`Root rotation statements exists for ${name}`);
},
},
{
name: 'mssql-connection',
plugin: 'mssql-database-plugin',
url: `mssql://127.0.0.1:4321/test`,
requiredFields: async (assert, name) => {
assert.dom('[data-test-input="username').exists(`Username field exists for ${name}`);
assert.dom('[data-test-input="password').exists(`Password field exists for ${name}`);
assert.dom('[data-test-input="username"]').exists(`Username field exists for ${name}`);
assert.dom('[data-test-input="password"]').exists(`Password field exists for ${name}`);
assert
.dom('[data-test-input="max_open_connections"]')
.exists(`Max open connections exists for ${name}`);
assert
.dom('[data-test-input="max_idle_connections"]')
.exists(`Max idle connections exists for ${name}`);
assert
.dom('[data-test-input="max_connection_lifetime"]')
.exists(`Max connection lifetime exists for ${name}`);
assert
.dom('[data-test-input="root_rotation_statements"]')
.exists(`Root rotation statements exists for ${name}`);
},
},
{
name: 'mysql-connection',
plugin: 'mysql-database-plugin',
url: `{{username}}:{{password}}@tcp(127.0.0.1:3306)/test`,
requiredFields: async (assert, name) => {
assert.dom('[data-test-input="username"]').exists(`Username field exists for ${name}`);
assert.dom('[data-test-input="password"]').exists(`Password field exists for ${name}`);
assert
.dom('[data-test-input="max_open_connections"]')
.exists(`Max open connections exists for ${name}`);
assert
.dom('[data-test-input="max_idle_connections"]')
.exists(`Max idle connections exists for ${name}`);
assert
.dom('[data-test-input="max_connection_lifetime"]')
.exists(`Max connection lifetime exists for ${name}`);
assert.dom('[data-test-toggle-group="TLS options"]').exists('TLS options toggle exists');
assert
.dom('[data-test-input="root_rotation_statements"]')
.exists(`Root rotation statements exists for ${name}`);
},
},
{
name: 'mysql-aurora-connection',
plugin: 'mysql-aurora-database-plugin',
url: `{{username}}:{{password}}@tcp(127.0.0.1:3306)/test`,
requiredFields: async (assert, name) => {
assert.dom('[data-test-input="username"]').exists(`Username field exists for ${name}`);
assert.dom('[data-test-input="password"]').exists(`Password field exists for ${name}`);
assert
.dom('[data-test-input="max_open_connections"]')
.exists(`Max open connections exists for ${name}`);
assert
.dom('[data-test-input="max_idle_connections"]')
.exists(`Max idle connections exists for ${name}`);
assert
.dom('[data-test-input="max_connection_lifetime"]')
.exists(`Max connection lifetime exists for ${name}`);
assert.dom('[data-test-toggle-group="TLS options"]').exists('TLS options toggle exists');
assert
.dom('[data-test-input="root_rotation_statements"]')
.exists(`Root rotation statements exists for ${name}`);
},
},
{
name: 'mysql-rds-connection',
plugin: 'mysql-rds-database-plugin',
url: `{{username}}:{{password}}@tcp(127.0.0.1:3306)/test`,
requiredFields: async (assert, name) => {
assert.dom('[data-test-input="username"]').exists(`Username field exists for ${name}`);
assert.dom('[data-test-input="password"]').exists(`Password field exists for ${name}`);
assert
.dom('[data-test-input="max_open_connections"]')
.exists(`Max open connections exists for ${name}`);
Expand All @@ -69,6 +137,32 @@ const connectionTests = [
assert
.dom('[data-test-input="max_connection_lifetime"]')
.exists(`Max connection lifetime exists for ${name}`);
assert.dom('[data-test-toggle-group="TLS options"]').exists('TLS options toggle exists');
assert
.dom('[data-test-input="root_rotation_statements"]')
.exists(`Root rotation statements exists for ${name}`);
},
},
{
name: 'mysql-legacy-connection',
plugin: 'mysql-legacy-database-plugin',
url: `{{username}}:{{password}}@tcp(127.0.0.1:3306)/test`,
requiredFields: async (assert, name) => {
assert.dom('[data-test-input="username"]').exists(`Username field exists for ${name}`);
assert.dom('[data-test-input="password"]').exists(`Password field exists for ${name}`);
assert
.dom('[data-test-input="max_open_connections"]')
.exists(`Max open connections exists for ${name}`);
assert
.dom('[data-test-input="max_idle_connections"]')
.exists(`Max idle connections exists for ${name}`);
assert
.dom('[data-test-input="max_connection_lifetime"]')
.exists(`Max connection lifetime exists for ${name}`);
assert.dom('[data-test-toggle-group="TLS options"]').exists('TLS options toggle exists');
assert
.dom('[data-test-input="root_rotation_statements"]')
.exists(`Root rotation statements exists for ${name}`);
},
},
];
Expand Down
20 changes: 20 additions & 0 deletions ui/tests/integration/components/database-role-setting-form-test.js
Expand Up @@ -30,6 +30,26 @@ const testCases = [
staticRoleFields: ['username', 'rotation_period'],
dynamicRoleFields: ['creation_statements', 'revocation_statements', 'ttl', 'max_ttl'],
},
{
pluginType: 'mysql-database-plugin',
staticRoleFields: ['username', 'rotation_period'],
dynamicRoleFields: ['creation_statements', 'revocation_statements', 'ttl', 'max_ttl'],
},
{
pluginType: 'mysql-aurora-database-plugin',
staticRoleFields: ['username', 'rotation_period'],
dynamicRoleFields: ['creation_statements', 'revocation_statements', 'ttl', 'max_ttl'],
},
{
pluginType: 'mysql-rds-database-plugin',
staticRoleFields: ['username', 'rotation_period'],
dynamicRoleFields: ['creation_statements', 'revocation_statements', 'ttl', 'max_ttl'],
},
{
pluginType: 'mysql-legacy-database-plugin',
staticRoleFields: ['username', 'rotation_period'],
dynamicRoleFields: ['creation_statements', 'revocation_statements', 'ttl', 'max_ttl'],
},
];

// used to calculate checks that fields do NOT show up
Expand Down

0 comments on commit ed001dd

Please sign in to comment.