Skip to content
This repository was archived by the owner on Aug 30, 2024. It is now read-only.

Commit 99a751f

Browse files
committed
Add new clog.ErrGroup
1 parent d32d196 commit 99a751f

File tree

3 files changed

+69
-13
lines changed

3 files changed

+69
-13
lines changed

internal/clog/doc.go

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// package clog provides rich error types and logging helpers for coder-cli.
2+
//
3+
// To preserve the modularity of the code, clog encourages returning error types rather than
4+
// logging them and failing with os.Exit as they happen.
5+
// Error, Fatal, and Warn allow downstream functions to return errors with rich formatting information
6+
// while preserving the original, single-line error chain.
7+
package clog

internal/clog/errgroup.go

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package clog
2+
3+
import (
4+
"fmt"
5+
"sync/atomic"
6+
7+
"golang.org/x/sync/errgroup"
8+
"golang.org/x/xerrors"
9+
)
10+
11+
// ErrGroup wraps the /x/sync/errgroup.(Group) and adds clog logging and rich error propagation.
12+
//
13+
// Take for example, a case in which we are concurrently stopping a slice of environments.
14+
// In this case, we want to log errors as they happen, not pass them through the callstack as errors.
15+
// When the operations complete, we want to log how many, if any, failed.
16+
type ErrGroup interface {
17+
Go(f func() error)
18+
Wait() error
19+
}
20+
21+
type group struct {
22+
egroup errgroup.Group
23+
failures int32
24+
}
25+
26+
// NewErrGroup gives an error group with logging and error propagation handled automatically.
27+
func NewErrGroup() ErrGroup {
28+
return &group{
29+
egroup: errgroup.Group{},
30+
failures: 0,
31+
}
32+
}
33+
34+
func (g *group) Go(f func() error) {
35+
g.egroup.Go(func() error {
36+
if err := f(); err != nil {
37+
atomic.AddInt32(&g.failures, 1)
38+
Log(err)
39+
40+
// this error does not matter because we discard it in Wait.
41+
return xerrors.New("")
42+
}
43+
return nil
44+
})
45+
}
46+
47+
func (g *group) Wait() error {
48+
_ = g.egroup.Wait() // ignore this error because we are already tracking failures manually
49+
if g.failures == 0 {
50+
return nil
51+
}
52+
failureWord := "failure"
53+
if g.failures > 1 {
54+
failureWord += "s"
55+
}
56+
return Fatal(fmt.Sprintf("%d %s emitted", g.failures, failureWord))
57+
}

internal/cmd/envs.go

+5-13
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,11 @@ import (
44
"encoding/json"
55
"fmt"
66
"os"
7-
"sync/atomic"
87

98
"cdr.dev/coder-cli/coder-sdk"
109
"cdr.dev/coder-cli/internal/clog"
1110
"cdr.dev/coder-cli/internal/x/xtabwriter"
1211
"github.com/spf13/cobra"
13-
"golang.org/x/sync/errgroup"
1412
"golang.org/x/xerrors"
1513
)
1614

@@ -102,34 +100,28 @@ coder envs --user charlie@coder.com ls -o json \
102100
return xerrors.Errorf("new client: %w", err)
103101
}
104102

105-
var egroup errgroup.Group
106-
var fails int32
103+
egroup := clog.NewErrGroup()
107104
for _, envName := range args {
108105
envName := envName
109106
egroup.Go(func() error {
110107
env, err := findEnv(cmd.Context(), client, envName, *user)
111108
if err != nil {
112-
atomic.AddInt32(&fails, 1)
113-
clog.Log(err)
114-
return xerrors.Errorf("find env by name: %w", err)
109+
return err
115110
}
116111

117112
if err = client.StopEnvironment(cmd.Context(), env.ID); err != nil {
118-
atomic.AddInt32(&fails, 1)
119-
err = clog.Fatal(fmt.Sprintf("stop environment %q", env.Name),
113+
return clog.Fatal(fmt.Sprintf("stop environment %q", env.Name),
120114
clog.Causef(err.Error()), clog.BlankLine,
121115
clog.Hintf("current environment status is %q", env.LatestStat.ContainerStatus),
122116
)
123-
clog.Log(err)
124-
return err
125117
}
126118
clog.LogSuccess(fmt.Sprintf("successfully stopped environment %q", envName))
127119
return nil
128120
})
129121
}
130122

131-
if err = egroup.Wait(); err != nil {
132-
return clog.Fatal(fmt.Sprintf("%d failure(s) emitted", fails))
123+
if err := egroup.Wait(); err != nil {
124+
return err
133125
}
134126
return nil
135127
},

0 commit comments

Comments
 (0)