From 44a87a8b05fda8b4bbca29011a3880dd9f989100 Mon Sep 17 00:00:00 2001 From: Steffen Siering Date: Thu, 9 May 2019 18:32:46 +0200 Subject: [PATCH] Add package libbeat/common/cleanup (#12134) * Add package libbeat/common/cleanup The cleanup package adds helpers for deferred optional cleanup on errors. For example: ``` ok := False defer cleanup.IfNot(&ok, func() { ... }) // continue initialization ok = True return // some value ``` * Add changelog entry --- CHANGELOG-developer.next.asciidoc | 1 + libbeat/common/cleanup/cleanup.go | 74 ++++++++++++++++++++++++++ libbeat/common/cleanup/cleanup_test.go | 65 ++++++++++++++++++++++ libbeat/common/cleanup/multi.go | 46 ++++++++++++++++ 4 files changed, 186 insertions(+) create mode 100644 libbeat/common/cleanup/cleanup.go create mode 100644 libbeat/common/cleanup/cleanup_test.go create mode 100644 libbeat/common/cleanup/multi.go diff --git a/CHANGELOG-developer.next.asciidoc b/CHANGELOG-developer.next.asciidoc index c0a07bcd80e5..1162a5835753 100644 --- a/CHANGELOG-developer.next.asciidoc +++ b/CHANGELOG-developer.next.asciidoc @@ -37,3 +37,4 @@ The list below covers the major changes between 7.0.0-rc2 and master only. - Update Jinja2 version to 2.10.1. {pull}11817[11817] - Reduce idxmgmt.Supporter interface and rework export commands to reuse logic. {pull}11777[11777], {pull}12065[12065], {pull}12067[12067] - Update urllib3 version to 1.24.2 {pull}11930[11930] +- Add libbeat/common/cleanup package. {pull}12134[12134] diff --git a/libbeat/common/cleanup/cleanup.go b/libbeat/common/cleanup/cleanup.go new file mode 100644 index 000000000000..5b1354697064 --- /dev/null +++ b/libbeat/common/cleanup/cleanup.go @@ -0,0 +1,74 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// Package cleanup provides common helpers for common cleanup patterns on defer +// +// Use the helpers with `defer`. For example use IfNot with `defer`, such that +// cleanup functions will be executed if `check` is false, no matter if an +// error has been returned or an panic has occured. +// +// initOK := false +// defer cleanup.IfNot(&initOK, func() { +// cleanup +// }) +// +// ... // init structures... +// +// initOK = true // notify handler cleanup code must not be executed +package cleanup + +// If will run the cleanup function if the bool value is true. +func If(check *bool, cleanup func()) { + if *check { + cleanup() + } +} + +// IfNot will run the cleanup function if the bool value is false. +func IfNot(check *bool, cleanup func()) { + if !(*check) { + cleanup() + } +} + +// IfPred will run the cleanup function if pred returns true. +func IfPred(pred func() bool, cleanup func()) { + if pred() { + cleanup() + } +} + +// IfNotPred will run the cleanup function if pred returns false. +func IfNotPred(pred func() bool, cleanup func()) { + if !pred() { + cleanup() + } +} + +// WithError returns a cleanup function calling a custom handler if an error occured. +func WithError(fn func(error), cleanup func() error) func() { + return func() { + if err := cleanup(); err != nil { + fn(err) + } + } +} + +// IgnoreError silently ignores errors in the cleanup function. +func IgnoreError(cleanup func() error) func() { + return func() { _ = cleanup() } +} diff --git a/libbeat/common/cleanup/cleanup_test.go b/libbeat/common/cleanup/cleanup_test.go new file mode 100644 index 000000000000..5208dc750aa4 --- /dev/null +++ b/libbeat/common/cleanup/cleanup_test.go @@ -0,0 +1,65 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package cleanup_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/elastic/beats/libbeat/common/cleanup" +) + +func TestIfBool(t *testing.T) { + testcases := []struct { + title string + fn func(*bool, func()) + value bool + cleanup bool + }{ + { + "IfNot runs cleanup", + cleanup.IfNot, false, true, + }, + { + "IfNot does not run cleanup", + cleanup.IfNot, true, false, + }, + { + "If runs cleanup", + cleanup.If, true, true, + }, + { + "If does not run cleanup", + cleanup.If, false, false, + }, + } + + for _, test := range testcases { + test := test + t.Run(test.title, func(t *testing.T) { + executed := false + func() { + v := test.value + defer test.fn(&v, func() { executed = true }) + }() + + assert.Equal(t, test.cleanup, executed) + }) + } +} diff --git a/libbeat/common/cleanup/multi.go b/libbeat/common/cleanup/multi.go new file mode 100644 index 000000000000..54f831f96197 --- /dev/null +++ b/libbeat/common/cleanup/multi.go @@ -0,0 +1,46 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package cleanup + +// FailClean keeps track of functions to be executed of FailClean did +// not receive a success signal. +type FailClean struct { + success bool + fns []func() +} + +// Signal sends a success or fail signal to FailClean. +func (f *FailClean) Signal(success bool) { + f.success = success +} + +// Add adds another cleanup handler. The last added handler will be run first. +func (f *FailClean) Add(fn func()) { + f.fns = append(f.fns, fn) +} + +// Cleanup runs all cleanup handlers in reverse order. +func (f *FailClean) Cleanup() { + if f.success { + return + } + + for i := len(f.fns) - 1; i >= 0; i-- { + f.fns[i]() + } +}