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

Lines changed: 7 additions & 0 deletions
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

Lines changed: 57 additions & 0 deletions
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

Lines changed: 5 additions & 13 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy