Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 3 additions & 11 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ var verbose = flag.Bool("v", false, "Print all details about the image")
var filter = flag.Bool("filter", true, "Filters filenames that create noise such as"+
" node_modules. Check ignore.go file for more details")
var extractLayers = flag.Bool("x", false, "Save layers to current directory")
var specificVersion = flag.String("sV", "", "Set the docker client ID to a specific version -sV=1.47")
var re *regexp.Regexp

type Manifest struct {
Expand Down Expand Up @@ -66,6 +65,7 @@ type dockerHist struct {
type DockerClient interface {
ImageInspectWithRaw(ctx context.Context, imageID string) (image.InspectResponse, []byte, error)
ImageSave(ctx context.Context, imageIDs []string, options ...client.ImageSaveOption) (io.ReadCloser, error)
ImagePull(ctx context.Context, refStr string, options image.PullOptions) (io.ReadCloser, error)
Close() error
}

Expand Down Expand Up @@ -172,13 +172,9 @@ func printConfigInfo(config *ImageConfig) {
func analyze(cli DockerClient, imageID string) {
info, _, err := cli.ImageInspectWithRaw(context.Background(), imageID)
if err != nil {
out, err := cli.ImageSave(context.Background(), []string{imageID})
out, err := cli.ImagePull(context.Background(), imageID, image.PullOptions{})
if err != nil {
color.Red(err.Error())
if strings.Contains(err.Error(), "Maximum supported API version is") {
version := strings.Split(err.Error(), "Maximum supported API version is ")[1]
color.Yellow("Use the -sV flag to change your client version:\n./whaler -sV=%s %s", version, imageID)
}
return
}
defer out.Close()
Expand Down Expand Up @@ -824,11 +820,7 @@ func main() {
}

// Existing Docker client logic
if len(*specificVersion) > 0 {
cli, err = client.NewClientWithOpts(client.WithVersion(*specificVersion))
} else {
cli, err = client.NewClientWithOpts()
}
cli, err = client.NewClientWithOpts(client.WithAPIVersionNegotiation())
if err != nil {
color.Red(err.Error())
return
Expand Down
38 changes: 28 additions & 10 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import (
type MockDockerClient struct {
*client.Client
imageInspectFunc func(ctx context.Context, imageID string) (image.InspectResponse, []byte, error)
imageSaveFunc func(ctx context.Context, imageIDs []string) (io.ReadCloser, error)
imageSaveFunc func(ctx context.Context, imageIDs []string, options ...client.ImageSaveOption) (io.ReadCloser, error)
imagePullFunc func(ctx context.Context, refStr string, options image.PullOptions) (io.ReadCloser, error)
}

func (m *MockDockerClient) ImageInspectWithRaw(ctx context.Context, imageID string) (image.InspectResponse, []byte, error) {
Expand All @@ -28,9 +29,17 @@ func (m *MockDockerClient) ImageInspectWithRaw(ctx context.Context, imageID stri
return image.InspectResponse{}, nil, nil
}

func (m *MockDockerClient) ImagePull(ctx context.Context, refStr string, options image.PullOptions) (io.ReadCloser, error) {
if m.imagePullFunc != nil {
return m.imagePullFunc(ctx, refStr, options)
}
return nil, nil
}


func (m *MockDockerClient) ImageSave(ctx context.Context, imageIDs []string, options ...client.ImageSaveOption) (io.ReadCloser, error) {
if m.imageSaveFunc != nil {
return m.imageSaveFunc(ctx, imageIDs)
return m.imageSaveFunc(ctx, imageIDs, options...)
}
return nil, nil
}
Expand Down Expand Up @@ -81,11 +90,13 @@ func TestExtractImageLayers(t *testing.T) {
// Create a mock Docker client
mockClient := &MockDockerClient{
Client: &client.Client{},
imageSaveFunc: func(ctx context.Context, imageIDs []string) (io.ReadCloser, error) {
imageSaveFunc: func(ctx context.Context, imageIDs []string, options ...client.ImageSaveOption) (io.ReadCloser, error) {
// Create a tar file in memory with our test data
pr, pw := io.Pipe()
go func() {
defer pw.Close()
tw := tar.NewWriter(pw)
defer tw.Close()

// Write a layer file
layerContent := "test layer content"
Expand All @@ -94,18 +105,25 @@ func TestExtractImageLayers(t *testing.T) {
Size: int64(len(layerContent)),
Mode: 0600,
}
tw.WriteHeader(header)
tw.Write([]byte(layerContent))
if err := tw.WriteHeader(header); err != nil {
return
}
if _, err := tw.Write([]byte(layerContent)); err != nil {
return
}

tw.Close()
pw.Close()
}()
return pr, nil
},
}

imageStream, err := mockClient.ImageSave(context.Background(), []string{"test-image"})
if err != nil {
t.Fatal(err)
}

// Test the extractImageLayers function
err = extractImageLayers(mockClient, "test-image", []dockerHist{
err = extractImageLayers(imageStream, "test-image", []dockerHist{
{
CreatedBy: "ADD file:123 /app",
LayerID: "layer1",
Expand Down Expand Up @@ -138,7 +156,7 @@ func TestAnalyzeImageFilesystem(t *testing.T) {

mockClient := &MockDockerClient{
Client: &client.Client{},
imageSaveFunc: func(ctx context.Context, imageIDs []string) (io.ReadCloser, error) {
imageSaveFunc: func(ctx context.Context, imageIDs []string, options ...client.ImageSaveOption) (io.ReadCloser, error) {
// Create a tar file in memory with our test data
pr, pw := io.Pipe()
go func() {
Expand Down Expand Up @@ -180,7 +198,7 @@ func TestAnalyzeImageFilesystem(t *testing.T) {
},
}

err := analyzeImageFilesystem(mockClient, "test-image")
_, err := analyzeImageFilesystem(mockClient, "test-image")
if err != nil {
t.Errorf("analyzeImageFilesystem failed: %v", err)
}
Expand Down
Binary file modified whaler
Binary file not shown.