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

Move undo behaviour from common to own package #293

Merged
merged 2 commits into from Jun 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
74 changes: 0 additions & 74 deletions pkg/common/undo.go

This file was deleted.

7 changes: 4 additions & 3 deletions pkg/compute/deploy.go
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/fastly/cli/pkg/config"
"github.com/fastly/cli/pkg/errors"
"github.com/fastly/cli/pkg/text"
"github.com/fastly/cli/pkg/undo"
"github.com/fastly/go-fastly/v3/fastly"
"github.com/kennygrant/sanitize"
)
Expand Down Expand Up @@ -174,7 +175,7 @@ func (c *DeployCommand) Exec(in io.Reader, out io.Writer) (err error) {
progress = text.NewQuietProgress(out)
}

undoStack := common.NewUndoStack()
undoStack := undo.NewStack()

defer func() {
if err != nil {
Expand Down Expand Up @@ -562,7 +563,7 @@ func cfgBackend(backend string, backendPort uint, out io.Writer, in io.Reader, f

// createDomain creates the given domain and handle unrolling the stack in case
// of an error (i.e. will ensure the domain is deleted if there is an error).
func createDomain(progress text.Progress, client api.Interface, serviceID string, version int, domain string, undoStack common.Undoer) error {
func createDomain(progress text.Progress, client api.Interface, serviceID string, version int, domain string, undoStack undo.Stacker) error {
progress.Step("Creating domain...")

undoStack.Push(func() error {
Expand All @@ -587,7 +588,7 @@ func createDomain(progress text.Progress, client api.Interface, serviceID string

// createBackend creates the given domain and handle unrolling the stack in case
// of an error (i.e. will ensure the backend is deleted if there is an error).
func createBackend(progress text.Progress, client api.Interface, serviceID string, version int, backend string, backendPort uint, undoStack common.Undoer) error {
func createBackend(progress text.Progress, client api.Interface, serviceID string, version int, backend string, backendPort uint, undoStack undo.Stacker) error {
progress.Step("Creating backend...")

undoStack.Push(func() error {
Expand Down
2 changes: 2 additions & 0 deletions pkg/undo/doc.go
@@ -0,0 +1,2 @@
// Package undo contains abstractions for working with a stack of state changes.
package undo
74 changes: 74 additions & 0 deletions pkg/undo/undo.go
@@ -0,0 +1,74 @@
package undo

import (
"fmt"
"io"
)

// Fn is a function with no arguments which returns an error or nil.
type Fn func() error

// Stack models a simple undo stack which consumers can use to store undo
// stateful functions, such as a function to teardown API state if something
// goes wrong during procedural commands, for example deleting a Fastly service
// after it's been created.
type Stack struct {
states []Fn
}

// Stacker represents the API of a Stack.
type Stacker interface {
Pop() Fn
Push(elem Fn)
Len() int
RunIfError(w io.Writer, err error)
}

// NewStack constructs a new Stack.
func NewStack() *Stack {
s := make([]Fn, 0, 1)
stack := &Stack{
states: s,
}
return stack
}

// Pop method pops last added Fn element off the stack and returns it.
// If stack is empty Pop() returns nil.
func (s *Stack) Pop() Fn {
n := len(s.states)
if n == 0 {
return nil
}
v := s.states[n-1]
s.states = s.states[:n-1]
return v
}

// Push method pushes an element onto the Stack.
func (s *Stack) Push(elem Fn) {
s.states = append(s.states, elem)
}

// Len method returns the number of elements in the Stack.
func (s *Stack) Len() int {
return len(s.states)
}

// RunIfError unwinds the stack if a non-nil error is passed, by serially
// calling each Fn function state in FIFO order. If any Fn returns an
// error, it gets logged to the provided writer. Should be deferrerd, such as:
//
// undoStack := undo.NewStack()
// defer func() { undoStack.RunIfError(w, err) }()
//
func (s *Stack) RunIfError(w io.Writer, err error) {
if err == nil {
return
}
for i := len(s.states) - 1; i >= 0; i-- {
if err := s.states[i](); err != nil {
fmt.Fprintln(w, err)
}
}
}