Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SR] Delete and execute SLM policies #41934

Merged
merged 7 commits into from
Aug 1, 2019

Conversation

jen-huang
Copy link
Contributor

@jen-huang jen-huang commented Jul 24, 2019

Summary

This PR adds the ability to delete and execute SLM policies from the UI. Executing a policy is referred to as "Run" (i.e. "Run policy") in the UI and this action means that a snapshot will be taken immediately using the configuration specified in the policy. Delete is self explanatory and uses our single/multi-delete pattern used in other apps.

Screenshots

Actions in table and details .

Run and delete actions in table row
image

Bulk delete from selecting multiple rows
image

Run and delete actions in policy details
image

Delete policy(ies) .

Single delete modal
image

Bulk delete modal
image

Run policy

Run policy modal
image

@jen-huang jen-huang added release_note:enhancement v8.0.0 Team:Kibana Management Dev Tools, Index Management, Upgrade Assistant, ILM, Ingest Node Pipelines, and more Feature:Snapshot and Restore Elasticsearch snapshots and repositories UI v7.4.0 labels Jul 24, 2019
@elasticmachine
Copy link
Contributor

Pinging @elastic/es-ui

@@ -49,7 +49,9 @@ export const RepositoryDeleteProvider: React.FunctionComponent<Props> = ({ child

const deleteRepository = () => {
const repositoriesToDelete = [...repositoryNames];
deleteRepositories(repositoriesToDelete).then(({ data: { itemsDeleted, errors }, error }) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

while working on policy deletion, I found that for server errors such as 404 or 500, which don't return a data object, early destructuring here causes an undefined object error, so no toast notification is shown. I adjusted this for policy deletion and fixed it for other delete providers in this app such as here.

@@ -180,7 +242,7 @@ export const PolicyDetails: React.FunctionComponent<Props> = ({ policyName, onCl
data-test-subj="policyDetail"
aria-labelledby="srPolicyDetailsFlyoutTitle"
size="m"
maxWidth={400}
maxWidth={550}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I bumped up the max width of all flyouts in this app as recent changes in EUI made them too narrow

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

Copy link
Contributor

@alisonelizabeth alisonelizabeth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jen-huang tested locally and everything LGTM. Left a couple minor comments.

return (
<EuiButtonEmpty
color="danger"
data-test-subj="srPoicyDetailsDeleteActionButton"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo here i think: srPoicyDetailsDeleteActionButton --> srPolicyDetailsDeleteActionButton

}
fill
color="primary"
data-test-subj="srPoicyDetailsExecuteActionButton"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

srPoicyDetailsExecuteActionButton --> srPolicyDetailsExecuteActionButton

aria-label={i18n.translate(
'xpack.snapshotRestore.policyList.table.actionExecuteAriaLabel',
{
defaultMessage: 'Run policy `{name}`',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be

`Run policy '{name}'`

aria-label={i18n.translate(
'xpack.snapshotRestore.policyList.table.actionDeleteAriaLabel',
{
defaultMessage: 'Delete policy `{name}`',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be

`Delete policy '{name}'`

@elasticmachine
Copy link
Contributor

💔 Build Failed

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@jen-huang jen-huang merged commit 6860574 into elastic:master Aug 1, 2019
@jen-huang jen-huang deleted the feature/slm-delete branch August 1, 2019 00:40
jen-huang added a commit that referenced this pull request Aug 1, 2019
* Add single and bulk policy delete

* Add policy execution

* Remove early destructuring of provider request responses

* Address PR feedback

* Adjust policy requests for useRequest changes

* Fix policy reload
Copy link
Contributor

@cjcenizal cjcenizal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks awesome! I know this is a bit late, but I had some suggestions for your perusal.

};

const deletePolicy = () => {
const policiesToDelete = [...policyNames];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we create a copy of policyNames here? Does something bad happen if we delete policyNames directly?

const deletePolicy = () => {
const policiesToDelete = [...policyNames];
deletePolicies(policiesToDelete).then(({ data, error }) => {
const { itemsDeleted, errors } = data || { itemsDeleted: undefined, errors: undefined };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: we could also assign a default value to the data parameter above, which would be a bit terser. We could also set a default value to itemsDeleted and errors, which would make lines 52 and 72 terser as well.

    deletePolicies(policiesToDelete).then(({ data = {}, error }) => {
      const { itemsDeleted = [], errors = [] } = data;
// ln 52
      if (itemsDeleted.length) {
// ln 72
      if (error || errors.length) {

I just realized that this won't quite work with the current logic for displaying error messages, but I have a suggestion that could make it work in the next comment.

// Surface error notifications
// `error` is generic server error
// `data.errors` are specific errors with removing particular policy(ies)
if (error || (errors && errors.length)) {
Copy link
Contributor

@cjcenizal cjcenizal Aug 1, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was having trouble following this logic because it contains two parallel conditional branches. How about something like this to make the two branches easier to discern and compare?

      function createErrorMessageMultiple(count) {
        return i18n.translate('xpack.snapshotRestore.deletePolicy.errorMultipleNotificationTitle', {
          defaultMessage: 'Error deleting {count} policies',
          values: { count },
        });
      }

      function createErrorMessageSingle(name) {
        return i18n.translate('xpack.snapshotRestore.deletePolicy.errorSingleNotificationTitle', {
          defaultMessage: "Error deleting policy '{name}'",
          values: { name },
        });
      }

      // Surface error notifications
      let errorMessage;

      if (errors.length) {
        // `data.errors` are specific errors with removing particular policy(ies)
        errorMessage = errors.length > 1
          ? createErrorMessageMultiple(errors.length)
          : createErrorMessageSingle(errors[0].name);
      } else if (error) {
        // `error` is generic server error
        errorMessage = policiesToDelete.length > 1
          ? createErrorMessageMultiple(policiesToDelete.length)
          : createErrorMessageSingle(policiesToDelete[0])
      }

      if (errorMessage) {
        toastNotifications.addDanger(errorMessage);
      }

@@ -49,7 +49,9 @@ export const RepositoryDeleteProvider: React.FunctionComponent<Props> = ({ child

const deleteRepository = () => {
const repositoriesToDelete = [...repositoryNames];
deleteRepositories(repositoriesToDelete).then(({ data: { itemsDeleted, errors }, error }) => {
deleteRepositories(repositoriesToDelete).then(({ data, error }) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be fixed like this?

// Default `data` to an empty object if it's not provided, which will have
// undefined `itemsDeleted` and `errors` properties.
deleteRepositories(repositoriesToDelete).then(({ data: { itemsDeleted, errors } = {}, error }) => {

onClick={() =>
executePolicyPrompt(policyName, () => {
onPolicyExecuted();
reload();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this needed? It looks like policy_list calls reload itself.


export const deleteHandler: RouterRouteHandler = async (req, callWithRequest) => {
const { names } = req.params;
const policyNames = names.split(',');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dumb question: are commas disallowed in policy names?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature:Snapshot and Restore Elasticsearch snapshots and repositories UI release_note:enhancement Team:Kibana Management Dev Tools, Index Management, Upgrade Assistant, ILM, Ingest Node Pipelines, and more v7.4.0 v8.0.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants