Skip to content

Commit 0a51a06

Browse files
authored
8 better error handling (#13)
* wip * clean up errors
1 parent c0fa6e6 commit 0a51a06

File tree

4 files changed

+147
-65
lines changed

4 files changed

+147
-65
lines changed

container.go

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,27 +11,26 @@ import (
1111
// Run starts the container and waits for the container to exit before returning the container logs.
1212
func (c *Container) Run(ctx context.Context) (*MuxedReadCloser, error) {
1313
if err := c.image.client.ContainerStart(ctx, c.id, types.ContainerStartOptions{}); err != nil {
14-
return nil, fmt.Errorf("starting container with id `%s` for image `%s` failed with: %s", c.id, c.image.image, err)
14+
return nil, errorContainerStart(c.id, c.image.image, err)
1515
}
1616

17-
err := c.Wait(ctx)
18-
if err != nil {
19-
return nil, fmt.Errorf("waiting for container with id `%s` for image `%s` failed with: %s", c.id, c.image.image, err)
17+
if err := c.Wait(ctx); err != nil {
18+
return nil, err
2019
}
2120

2221
info, err := c.image.client.ContainerInspect(ctx, c.id)
2322
if err != nil {
24-
return nil, fmt.Errorf("inspecting container with id `%s` for image `%s` failed with: %s", c.id, c.image.image, err)
23+
return nil, errorContainerInspect(c.id, c.image.image, err)
2524
}
2625

2726
var RetCodeErr error
2827
if info.ContainerJSONBase.State.ExitCode != 0 {
29-
RetCodeErr = fmt.Errorf("running container with id `%s` for image `%s` failed with exit-code: %d", c.id, c.image.image, info.ContainerJSONBase.State.ExitCode)
28+
RetCodeErr = errorContainerExitCode(c.id, c.image.image, info.ContainerJSONBase.State.ExitCode)
3029
}
3130

3231
muxed, err := c.image.client.ContainerLogs(ctx, c.id, types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true})
3332
if err != nil {
34-
return nil, fmt.Errorf("getting container logs for image `%s` failed with %s", c.image.image, err)
33+
return nil, errorContainerLogs(c.id, c.image.image, err)
3534
}
3635

3736
c.Cleanup(ctx)
@@ -46,7 +45,7 @@ func (c *Container) Wait(ctx context.Context) error {
4645
select {
4746
case err := <-errCh:
4847
if err != nil {
49-
return fmt.Errorf("container with id `%s` returned error: %s", c.id, err)
48+
return errorContainerWait(c.id, c.image.image, err)
5049
}
5150
case <-statusCh:
5251
}

errors.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package containers
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
)
7+
8+
var errorBasicFormat = "%s with: %w"
9+
10+
// Image Method Errors
11+
var (
12+
ErrorImageOptions = errors.New("image options failed")
13+
ErrorImageBuild = errors.New("building image failed")
14+
ErrorImagePull = errors.New("pulling image failed")
15+
ErrorClientPull = errors.New("client pull failed")
16+
ErrorImageBuildDockerFile = errors.New("building Dockerfile failed")
17+
ErrorImageBuildResCopy = errors.New("copying response from image build failed")
18+
ErrorImagePullStatus = errors.New("copying pull status failed")
19+
20+
ErrorContainerOptions = errors.New("container options failed")
21+
ErrorContainerCreate = errors.New("creating container failed")
22+
23+
errorImageFormat = "%s for image `%s` with: %w"
24+
)
25+
26+
func errorImageOptions(image string, err error) error {
27+
return fmt.Errorf(errorImageFormat, ErrorImageOptions, image, err)
28+
}
29+
30+
func errorImageBuild(image string, err error) error {
31+
return fmt.Errorf(errorImageFormat, ErrorImageBuild, image, err)
32+
}
33+
34+
func errorImagePull(image string, err error) error {
35+
return fmt.Errorf(errorImageFormat, ErrorImagePull, image, err)
36+
}
37+
38+
func errorClientPull(err error) error {
39+
return fmt.Errorf(errorBasicFormat, ErrorClientPull, err)
40+
}
41+
42+
func errorImageBuildDockerFile(err error) error {
43+
return fmt.Errorf(errorBasicFormat, ErrorImageBuildDockerFile, err)
44+
}
45+
46+
func errorImageBuildResCopy(err error) error {
47+
return fmt.Errorf(errorBasicFormat, ErrorImageBuildResCopy, err)
48+
}
49+
50+
func errorImagePullStatus(err error) error {
51+
return fmt.Errorf(errorBasicFormat, ErrorImagePullStatus, err)
52+
}
53+
54+
func errorContainerOptions(image string, err error) error {
55+
return fmt.Errorf(errorImageFormat, ErrorContainerOptions, image, err)
56+
}
57+
58+
func errorContainerCreate(image string, err error) error {
59+
return fmt.Errorf(errorImageFormat, ErrorContainerCreate, image, err)
60+
}
61+
62+
// Container Method Errors
63+
var (
64+
ErrorContainerStart = errors.New("start container failed")
65+
ErrorContainerWait = errors.New("container wait failed")
66+
ErrorClientWait = errors.New("client wait failed")
67+
ErrorContainerInspect = errors.New("inspecting container failed")
68+
ErrorExitCode = errors.New("exit-code")
69+
ErrorContainerLogs = errors.New("getting container logs failed")
70+
71+
errorContainerFormat = "%s for container Id:`%s` image:`%s` with: %w"
72+
)
73+
74+
func errorContainerStart(id, image string, err error) error {
75+
return fmt.Errorf(errorContainerFormat, ErrorContainerStart, id, image, err)
76+
}
77+
78+
func errorContainerWait(id, image string, err error) error {
79+
return fmt.Errorf(errorContainerFormat, ErrorContainerWait, id, image, err)
80+
}
81+
82+
func errorContainerInspect(id, image string, err error) error {
83+
return fmt.Errorf(errorContainerFormat, ErrorContainerInspect, id, image, err)
84+
}
85+
86+
func errorContainerExitCode(id, image string, code int) error {
87+
return fmt.Errorf("container Id:`%s` image:`%s` failed with %w:%d", id, image, ErrorExitCode, code)
88+
}
89+
90+
func errorContainerLogs(id, image string, err error) error {
91+
return fmt.Errorf(errorContainerFormat, ErrorContainerLogs, id, image, err)
92+
}

image.go

Lines changed: 47 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -17,49 +17,39 @@ import (
1717

1818
// Image initializes the given image, and attempts to pull the container from docker hub.
1919
// If the Build() Option is provided then the given DockerFile tarball is built and returned.
20-
func (c *Client) Image(ctx context.Context, image string, options ...ImageOption) (*Image, error) {
21-
_image := &Image{
20+
func (c *Client) Image(ctx context.Context, name string, options ...ImageOption) (image *Image, err error) {
21+
image = &Image{
2222
client: c,
23-
image: image,
23+
image: name,
2424
}
2525

2626
for _, opt := range options {
27-
err := opt(_image)
28-
if err != nil {
29-
return nil, fmt.Errorf("running image options for image `%s` failed with: %s", image, err)
27+
if err := opt(image); err != nil {
28+
return nil, errorImageOptions(name, err)
3029
}
3130
}
3231

33-
imageExists := _image.checkImageExists(ctx)
34-
if _image.buildTarball != nil {
35-
if ForceRebuild || !imageExists {
36-
err := _image.buildImage(ctx)
37-
if err != nil {
38-
return nil, fmt.Errorf("building image `%s` failed with %s", image, err)
39-
}
32+
imageExists := image.checkImageExists(ctx)
33+
if image.buildTarball != nil && (ForceRebuild || !imageExists) {
34+
if err := image.buildImage(ctx); err != nil {
35+
return nil, errorImageBuild(name, err)
36+
}
37+
} else {
38+
if image, err = image.Pull(ctx); err != nil {
39+
return nil, errorImagePull(name, err)
4040
}
41-
42-
return _image, nil
43-
}
44-
45-
_image, err := _image.Pull(ctx)
46-
if err != nil && !imageExists {
47-
return nil, fmt.Errorf("pulling image `%s` failed with %s", image, err)
4841
}
4942

50-
return _image, nil
43+
return
5144
}
5245

5346
// checkImage checks the docker host client if the image is known.
5447
func (i *Image) checkImageExists(ctx context.Context) bool {
5548
res, err := i.client.ImageList(ctx, types.ImageListOptions{
5649
Filters: NewFilter("reference", i.image),
5750
})
58-
if err != nil || len(res) < 1 {
59-
return false
60-
}
6151

62-
return true
52+
return err == nil && len(res) > 0
6353
}
6454

6555
// buildImage builds a DockerFile tarball as a docker image.
@@ -71,16 +61,16 @@ func (i *Image) buildImage(ctx context.Context) error {
7161
Context: i.buildTarball,
7262
Dockerfile: "Dockerfile",
7363
Tags: []string{i.image},
74-
Remove: true})
64+
Remove: true,
65+
},
66+
)
7567
if err != nil {
76-
return fmt.Errorf("building Dockerfile for image `%s` failed with: %s", i.image, err)
68+
return errorImageBuildDockerFile(err)
7769
}
78-
7970
defer imageBuildResponse.Body.Close()
8071

81-
_, err = io.Copy(os.Stdout, imageBuildResponse.Body)
82-
if err != nil {
83-
return fmt.Errorf("copying response from image `%s` build failed with :%s", i.image, err)
72+
if _, err = io.Copy(os.Stdout, imageBuildResponse.Body); err != nil {
73+
return errorImageBuildResCopy(err)
8474
}
8575

8676
return nil
@@ -90,14 +80,12 @@ func (i *Image) buildImage(ctx context.Context) error {
9080
func (i *Image) Pull(ctx context.Context) (*Image, error) {
9181
reader, err := i.client.ImagePull(ctx, i.image, types.ImagePullOptions{})
9282
if err != nil {
93-
return i, fmt.Errorf("pulling image `%s`, failed with %s", i.image, err)
83+
return i, errorClientPull(err)
9484
}
95-
9685
defer reader.Close()
97-
fileScanner := bufio.NewScanner(reader)
9886

87+
fileScanner := bufio.NewScanner(reader)
9988
fileScanner.Split(bufio.ScanLines)
100-
10189
for fileScanner.Scan() {
10290
var line = fileScanner.Bytes()
10391
var status struct {
@@ -108,9 +96,9 @@ func (i *Image) Pull(ctx context.Context) (*Image, error) {
10896
}
10997
Id string
11098
}
111-
err = json.Unmarshal(line, &status)
112-
if err != nil {
113-
return i, fmt.Errorf("unmarshaling status from docker pull on image `%s` failed with: %s", i.image, err)
99+
100+
if err = json.Unmarshal(line, &status); err != nil {
101+
return i, errorImagePullStatus(err)
114102
}
115103
}
116104

@@ -125,18 +113,17 @@ func (i *Image) Instantiate(ctx context.Context, options ...ContainerOption) (*C
125113
for _, opt := range options {
126114
err := opt(c)
127115
if err != nil {
128-
return nil, fmt.Errorf("running image options for image `%s` failed with: %s", i.image, err)
116+
return nil, errorContainerOptions(i.image, err)
129117
}
130118
}
131119

132-
var mounts []mount.Mount
133-
_volume := mount.Mount{
134-
Type: mount.TypeBind,
135-
}
136-
for _, volume := range c.volumes {
137-
_volume.Source = volume.source
138-
_volume.Target = volume.target
139-
mounts = append(mounts, _volume)
120+
mounts := make([]mount.Mount, len(c.volumes))
121+
for idx, volume := range c.volumes {
122+
mounts[idx] = mount.Mount{
123+
Type: mount.TypeBind,
124+
Source: volume.source,
125+
Target: volume.target,
126+
}
140127
}
141128

142129
config := &container.Config{
@@ -146,14 +133,13 @@ func (i *Image) Instantiate(ctx context.Context, options ...ContainerOption) (*C
146133
Tty: false,
147134
Env: c.env,
148135
}
149-
150-
if len(c.workDir) != 0 {
136+
if len(c.workDir) > 0 {
151137
config.WorkingDir = c.workDir
152138
}
153139

154140
resp, err := c.image.client.ContainerCreate(ctx, config, &container.HostConfig{Mounts: mounts}, nil, nil, "")
155141
if err != nil {
156-
return nil, fmt.Errorf("creating container for image `%s` failed with: %s", c.image.image, err)
142+
return nil, errorContainerCreate(c.image.Name(), err)
157143
}
158144
c.id = resp.ID
159145

@@ -162,21 +148,26 @@ func (i *Image) Instantiate(ctx context.Context, options ...ContainerOption) (*C
162148

163149
// Clean removes all docker images that match the given filter, and max age.
164150
func (c *Client) Clean(ctx context.Context, age time.Duration, filter filters.Args) error {
165-
images, _ := c.ImageList(context.Background(), types.ImageListOptions{Filters: filter})
151+
images, _ := c.ImageList(ctx, types.ImageListOptions{Filters: filter})
166152
timeNow := time.Now()
153+
154+
var err error
167155
for _, image := range images {
168156
if time.Unix(image.Created, 0).Add(age).Before(timeNow) {
169-
_, err := c.ImageRemove(ctx, image.ID, types.ImageRemoveOptions{
157+
if _, _err := c.ImageRemove(ctx, image.ID, types.ImageRemoveOptions{
170158
Force: true,
171159
PruneChildren: true,
172-
})
173-
if err != nil {
174-
return fmt.Errorf("cleaning image `%s` from docker host failed with %s", image.ID, err)
160+
}); _err != nil {
161+
if err != nil {
162+
err = fmt.Errorf("%s:%w", err, _err)
163+
} else {
164+
err = _err
165+
}
175166
}
176167
}
177168
}
178169

179-
return nil
170+
return err
180171
}
181172

182173
// Name returns the name of the image

new.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ func New() (dockerClient *Client, err error) {
1111
dockerClient = new(Client)
1212
dockerClient.Client, err = client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
1313
if err != nil {
14-
return nil, fmt.Errorf("creating new docker client failed with: %s", err)
14+
return nil, fmt.Errorf("new docker client failed with: %w", err)
1515
}
1616

1717
return

0 commit comments

Comments
 (0)