Skip to content

Commit

Permalink
feat(daemon): generate config file from Docker Engine API (#1130)
Browse files Browse the repository at this point in the history
* feat(daemon): generate config file from Docker API

* refactor: fix lint issues

* test: add GraphDriver
  • Loading branch information
knqyf263 committed Apr 20, 2023
1 parent d1c4e9f commit 9f68710
Show file tree
Hide file tree
Showing 3 changed files with 193 additions and 3 deletions.
151 changes: 149 additions & 2 deletions pkg/v1/daemon/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ import (
"context"
"io"
"sync"
"time"

api "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"

"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
Expand All @@ -30,7 +34,9 @@ type image struct {
ref name.Reference
opener *imageOpener
tarballImage v1.Image
computed bool
id *v1.Hash
configFile *v1.ConfigFile

once sync.Once
err error
Expand Down Expand Up @@ -121,6 +127,28 @@ func (i *image) initialize() error {
return i.err
}

func (i *image) compute() error {
// Don't re-compute if already computed.
if i.computed {
return nil
}

inspect, _, err := i.opener.client.ImageInspectWithRaw(i.opener.ctx, i.ref.String())
if err != nil {
return err
}

configFile, err := i.computeConfigFile(inspect)
if err != nil {
return err
}

i.configFile = configFile
i.computed = true

return nil
}

func (i *image) Layers() ([]v1.Layer, error) {
if err := i.initialize(); err != nil {
return nil, err
Expand Down Expand Up @@ -154,16 +182,19 @@ func (i *image) ConfigName() (v1.Hash, error) {
}

func (i *image) ConfigFile() (*v1.ConfigFile, error) {
if err := i.initialize(); err != nil {
if err := i.compute(); err != nil {
return nil, err
}
return i.tarballImage.ConfigFile()
return i.configFile.DeepCopy(), nil
}

func (i *image) RawConfigFile() ([]byte, error) {
if err := i.initialize(); err != nil {
return nil, err
}

// RawConfigFile cannot be generated from "docker inspect" because Docker Engine API returns serialized data,
// and formatting information of the raw config such as indent and prefix will be lost.
return i.tarballImage.RawConfigFile()
}

Expand Down Expand Up @@ -201,3 +232,119 @@ func (i *image) LayerByDiffID(h v1.Hash) (v1.Layer, error) {
}
return i.tarballImage.LayerByDiffID(h)
}

func (i *image) configHistory(author string) ([]v1.History, error) {
historyItems, err := i.opener.client.ImageHistory(i.opener.ctx, i.ref.String())
if err != nil {
return nil, err
}

history := make([]v1.History, len(historyItems))
for j, h := range historyItems {
history[j] = v1.History{
Author: author,
Created: v1.Time{
Time: time.Unix(h.Created, 0).UTC(),
},
CreatedBy: h.CreatedBy,
Comment: h.Comment,
EmptyLayer: h.Size == 0,
}
}
return history, nil
}

func (i *image) diffIDs(rootFS api.RootFS) ([]v1.Hash, error) {
diffIDs := make([]v1.Hash, len(rootFS.Layers))
for j, l := range rootFS.Layers {
h, err := v1.NewHash(l)
if err != nil {
return nil, err
}
diffIDs[j] = h
}
return diffIDs, nil
}

func (i *image) computeConfigFile(inspect api.ImageInspect) (*v1.ConfigFile, error) {
diffIDs, err := i.diffIDs(inspect.RootFS)
if err != nil {
return nil, err
}

history, err := i.configHistory(inspect.Author)
if err != nil {
return nil, err
}

created, err := time.Parse(time.RFC3339Nano, inspect.Created)
if err != nil {
return nil, err
}

return &v1.ConfigFile{
Architecture: inspect.Architecture,
Author: inspect.Author,
Container: inspect.Container,
Created: v1.Time{Time: created},
DockerVersion: inspect.DockerVersion,
History: history,
OS: inspect.Os,
RootFS: v1.RootFS{
Type: inspect.RootFS.Type,
DiffIDs: diffIDs,
},
Config: i.computeImageConfig(inspect.Config),
OSVersion: inspect.OsVersion,
}, nil
}

func (i *image) computeImageConfig(config *container.Config) v1.Config {
if config == nil {
return v1.Config{}
}

c := v1.Config{
AttachStderr: config.AttachStderr,
AttachStdin: config.AttachStdin,
AttachStdout: config.AttachStdout,
Cmd: config.Cmd,
Domainname: config.Domainname,
Entrypoint: config.Entrypoint,
Env: config.Env,
Hostname: config.Hostname,
Image: config.Image,
Labels: config.Labels,
OnBuild: config.OnBuild,
OpenStdin: config.OpenStdin,
StdinOnce: config.StdinOnce,
Tty: config.Tty,
User: config.User,
Volumes: config.Volumes,
WorkingDir: config.WorkingDir,
ArgsEscaped: config.ArgsEscaped,
NetworkDisabled: config.NetworkDisabled,
MacAddress: config.MacAddress,
StopSignal: config.StopSignal,
Shell: config.Shell,
}

if config.Healthcheck != nil {
c.Healthcheck = &v1.HealthConfig{
Test: config.Healthcheck.Test,
Interval: config.Healthcheck.Interval,
Timeout: config.Healthcheck.Timeout,
StartPeriod: config.Healthcheck.StartPeriod,
Retries: config.Healthcheck.Retries,
}
}

if len(config.ExposedPorts) > 0 {
c.ExposedPorts = map[string]struct{}{}
for port := range c.ExposedPorts {
c.ExposedPorts[port] = struct{}{}
}
}

return c
}
43 changes: 42 additions & 1 deletion pkg/v1/daemon/image_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ import (
"strings"
"testing"

"github.com/docker/docker/api/types/container"
api "github.com/docker/docker/api/types/image"

"github.com/docker/docker/api/types"
"github.com/google/go-containerregistry/internal/compare"
"github.com/google/go-containerregistry/pkg/name"
Expand Down Expand Up @@ -62,12 +65,49 @@ func (m *MockClient) ImageSave(_ context.Context, _ []string) (io.ReadCloser, er
return m.saveBody, m.saveErr
}

func (m *MockClient) ImageInspectWithRaw(context.Context, string) (types.ImageInspect, []byte, error) {
func (m *MockClient) ImageInspectWithRaw(_ context.Context, _ string) (types.ImageInspect, []byte, error) {
return types.ImageInspect{
ID: "sha256:6e0b05049ed9c17d02e1a55e80d6599dbfcce7f4f4b022e3c673e685789c470e",
RepoTags: []string{
"bazel/v1/tarball:test_image_1",
},
Created: "1970-01-01T00:00:00Z",
Author: "Bazel",
Architecture: "amd64",
Os: "linux",
Size: 8,
VirtualSize: 8,
Config: &container.Config{},
GraphDriver: types.GraphDriverData{
Data: map[string]string{
"MergedDir": "/var/lib/docker/overlay2/988ecd005d048fd47b241dd57687231859563ba65a1dfd01ae1771ebfc4cb7c5/merged",
"UpperDir": "/var/lib/docker/overlay2/988ecd005d048fd47b241dd57687231859563ba65a1dfd01ae1771ebfc4cb7c5/diff",
"WorkDir": "/var/lib/docker/overlay2/988ecd005d048fd47b241dd57687231859563ba65a1dfd01ae1771ebfc4cb7c5/work",
},
Name: "overlay2",
},
RootFS: types.RootFS{
Type: "layers",
Layers: []string{
"sha256:8897395fd26dc44ad0e2a834335b33198cb41ac4d98dfddf58eced3853fa7b17",
},
},
}, nil, nil
}

func (m *MockClient) ImageHistory(_ context.Context, _ string) ([]api.HistoryResponseItem, error) {
return []api.HistoryResponseItem{
{
CreatedBy: "bazel build ...",
ID: "sha256:6e0b05049ed9c17d02e1a55e80d6599dbfcce7f4f4b022e3c673e685789c470e",
Size: 8,
Tags: []string{
"bazel/v1/tarball:test_image_1",
},
},
}, nil
}

func TestImage(t *testing.T) {
for _, tc := range []struct {
name string
Expand Down Expand Up @@ -120,6 +160,7 @@ func TestImage(t *testing.T) {
}
return
}

err = compare.Images(img, dmn)
if err != nil {
if tc.wantErr == "" {
Expand Down
2 changes: 2 additions & 0 deletions pkg/v1/daemon/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"io"

"github.com/docker/docker/api/types"
api "github.com/docker/docker/api/types/image"
"github.com/docker/docker/client"
)

Expand Down Expand Up @@ -100,4 +101,5 @@ type Client interface {
ImageLoad(context.Context, io.Reader, bool) (types.ImageLoadResponse, error)
ImageTag(context.Context, string, string) error
ImageInspectWithRaw(context.Context, string) (types.ImageInspect, []byte, error)
ImageHistory(context.Context, string) ([]api.HistoryResponseItem, error)
}

0 comments on commit 9f68710

Please sign in to comment.