Skip to content

Commit

Permalink
docker graceful exits when image pull fails #382
Browse files Browse the repository at this point in the history
added check for Decode statement

added unit tests

added unit tests

added unit tests
  • Loading branch information
Cbkhare committed Mar 17, 2021
1 parent 1b178da commit 1387b5a
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 4 deletions.
32 changes: 28 additions & 4 deletions cmd/tink-worker/internal/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"encoding/base64"
"encoding/json"
"io"
"os"

"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
Expand All @@ -22,6 +21,17 @@ type RegistryConnDetails struct {
logger log.Logger
}

// ImagePullStatus is the status of the downloaded Image chunk
type ImagePullStatus struct {
Status string `json:"status"`
Error string `json:"error"`
Progress string `json:"progress"`
ProgressDetail struct {
Current int `json:"current"`
Total int `json:"total"`
} `json:"progressDetail"`
}

// NewRegistryConnDetails creates a new RegistryConnDetails
func NewRegistryConnDetails(registry, user, pwd string, logger log.Logger) *RegistryConnDetails {
return &RegistryConnDetails{
Expand All @@ -46,8 +56,12 @@ func (r *RegistryConnDetails) NewClient() (*client.Client, error) {
return c, nil
}

type ImagePuller interface {
ImagePull(context.Context, string, types.ImagePullOptions) (io.ReadCloser, error)
}

// pullImage outputs to stdout the contents of the requested image (relative to the registry)
func (r *RegistryConnDetails) pullImage(ctx context.Context, cli *client.Client, image string) error {
func (r *RegistryConnDetails) pullImage(ctx context.Context, cli ImagePuller, image string) error {
authConfig := types.AuthConfig{
Username: r.user,
Password: r.pwd,
Expand All @@ -64,8 +78,18 @@ func (r *RegistryConnDetails) pullImage(ctx context.Context, cli *client.Client,
return errors.Wrap(err, "DOCKER PULL")
}
defer out.Close()
if _, err := io.Copy(os.Stdout, out); err != nil {
return err
fd := json.NewDecoder(out)
var status *ImagePullStatus
for {
if err := fd.Decode(&status); err != nil {
if err == io.EOF {
break
}
return errors.Wrap(err, "DOCKER PULL")
}
if status.Error != "" {
return errors.Wrap(errors.New(status.Error), "DOCKER PULL")
}
}
return nil
}
72 changes: 72 additions & 0 deletions cmd/tink-worker/internal/registry_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package internal

import (
"context"
"errors"
"io"
"io/ioutil"
"strings"
"testing"

"github.com/docker/docker/api/types"
"github.com/packethost/pkg/log"
"github.com/stretchr/testify/assert"
)

func setupTestLogger() log.Logger {
service := "github.com/tinkerbell/tink"
logger, err := log.Init(service)
if err != nil {
panic(err)
}
return logger
}

type imagePullerMock struct{}

func (d *imagePullerMock) ImagePull(ctx context.Context, str string, op types.ImagePullOptions) (io.ReadCloser, error) {
var s string
var err error
if str == "test/success" {
s = "{\"status\": \"hello\",\"error\":\"\"}{\"status\":\"world\",\"error\":\"\"}"
} else if str == "test/fail" {
s = "{\"error\": \"\"}"
err = errors.New("Tested, failure of the image pull")
} else if str == "test/fail_partial" {
s = "{\"status\": \"hello\",\"error\":\"\"}{\"status\":\"world\",\"error\":\"Tested, failure of No space left on device\"}"
}
stringReader := strings.NewReader(s)
stringReadCloser := ioutil.NopCloser(stringReader)
return stringReadCloser, err
}

func TestPullImageAnyFailure(t *testing.T) {
for _, test := range []struct {
testName string
err error
}{
{
testName: "success",
err: nil,
},
{
testName: "fail",
err: errors.New("Tested, failure of the image pull"),
},
{
testName: "fail_partial",
err: errors.New("Tested, failure of No space left on device"),
},
} {
t.Run(test.testName, func(t *testing.T) {
ctx := context.Background()
rcon := NewRegistryConnDetails("test", "testUser", "testPwd", setupTestLogger())
cli := &imagePullerMock{}
err := rcon.pullImage(ctx, cli, test.testName)
if test.err != nil {
assert.Error(t, err)
return
}
})
}
}

0 comments on commit 1387b5a

Please sign in to comment.