detached e057b28

feat(docker): implement GetImageID and populate ImageID in ListContainers and InspectContainer

commit
e057b287afbc13dcdd44c0aa07e4309b740fee4f
author
bnema · · 2026-02-09 20:41 UTC
parent
24d79bf
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
2 files · +92 -6
M internal/adapters/out/docker/runtime.go +26 -6
@@ -305,12 +305,13 @@ func (r *Runtime) ListContainers(ctx context.Context, all bool) ([]*domain.Conta
305305 }
306306
307307 result = append(result, &domain.Container{
308- ID: c.ID,
309- Image: c.Image,
310- Name: name,
311- Status: c.State, // Use State (e.g., "running") not Status (e.g., "Up 2 days")
312- Ports: ports,
313- Labels: c.Labels,
308+ ID: c.ID,
309+ Image: c.Image,
310+ ImageID: c.ImageID,
311+ Name: name,
312+ Status: c.State, // Use State (e.g., "running") not Status (e.g., "Up 2 days")
313+ Ports: ports,
314+ Labels: c.Labels,
314315 })
315316 }
316317
@@ -352,6 +353,7 @@ func (r *Runtime) InspectContainer(ctx context.Context, containerID string) (*do
352353 return &domain.Container{
353354 ID: resp.ID,
354355 Image: resp.Config.Image,
356+ ImageID: resp.Image,
355357 Name: name,
356358 Status: resp.State.Status,
357359 ExitCode: resp.State.ExitCode,
@@ -1103,6 +1105,24 @@ func (r *Runtime) GetImageLabels(ctx context.Context, imageRef string) (map[stri
11031105 return labels, nil
11041106 }
11051107
1108+// GetImageID returns the unique image ID (sha256 digest) for the given image reference.
1109+func (r *Runtime) GetImageID(ctx context.Context, imageRef string) (string, error) {
1110+ ctx = zerowrap.CtxWithFields(ctx, map[string]any{
1111+ zerowrap.FieldLayer: "adapter",
1112+ zerowrap.FieldAdapter: "docker",
1113+ zerowrap.FieldAction: "GetImageID",
1114+ "image": imageRef,
1115+ })
1116+ log := zerowrap.FromCtx(ctx)
1117+
1118+ imageInspect, err := r.client.ImageInspect(ctx, imageRef)
1119+ if err != nil {
1120+ return "", log.WrapErr(err, "failed to inspect image")
1121+ }
1122+
1123+ return imageInspect.ID, nil
1124+}
1125+
11061126 // CreateNetwork creates a new Docker network.
11071127 func (r *Runtime) CreateNetwork(ctx context.Context, name string, options map[string]string) error {
11081128 ctx = zerowrap.CtxWithFields(ctx, map[string]any{
@@ -305,12 +305,13 @@ func (r *Runtime) ListContainers(ctx context.Context, all bool) ([]*domain.Conta
305 }305 }
306 306
307 result = append(result, &domain.Container{307 result = append(result, &domain.Container{
308- ID: c.ID,308+ ID: c.ID,
309- Image: c.Image,309+ Image: c.Image,
310- Name: name,310+ ImageID: c.ImageID,
311- Status: c.State, // Use State (e.g., "running") not Status (e.g., "Up 2 days")311+ Name: name,
312- Ports: ports,312+ Status: c.State, // Use State (e.g., "running") not Status (e.g., "Up 2 days")
313- Labels: c.Labels,313+ Ports: ports,
314+ Labels: c.Labels,
314 })315 })
315 }316 }
316 317
@@ -352,6 +353,7 @@ func (r *Runtime) InspectContainer(ctx context.Context, containerID string) (*do
352 return &domain.Container{353 return &domain.Container{
353 ID: resp.ID,354 ID: resp.ID,
354 Image: resp.Config.Image,355 Image: resp.Config.Image,
356+ ImageID: resp.Image,
355 Name: name,357 Name: name,
356 Status: resp.State.Status,358 Status: resp.State.Status,
357 ExitCode: resp.State.ExitCode,359 ExitCode: resp.State.ExitCode,
@@ -1103,6 +1105,24 @@ func (r *Runtime) GetImageLabels(ctx context.Context, imageRef string) (map[stri
1103 return labels, nil1105 return labels, nil
1104 }1106 }
1105 1107
1108+// GetImageID returns the unique image ID (sha256 digest) for the given image reference.
1109+func (r *Runtime) GetImageID(ctx context.Context, imageRef string) (string, error) {
1110+ ctx = zerowrap.CtxWithFields(ctx, map[string]any{
1111+ zerowrap.FieldLayer: "adapter",
1112+ zerowrap.FieldAdapter: "docker",
1113+ zerowrap.FieldAction: "GetImageID",
1114+ "image": imageRef,
1115+ })
1116+ log := zerowrap.FromCtx(ctx)
1117+
1118+ imageInspect, err := r.client.ImageInspect(ctx, imageRef)
1119+ if err != nil {
1120+ return "", log.WrapErr(err, "failed to inspect image")
1121+ }
1122+
1123+ return imageInspect.ID, nil
1124+}
1125+
1106 // CreateNetwork creates a new Docker network.1126 // CreateNetwork creates a new Docker network.
1107 func (r *Runtime) CreateNetwork(ctx context.Context, name string, options map[string]string) error {1127 func (r *Runtime) CreateNetwork(ctx context.Context, name string, options map[string]string) error {
1108 ctx = zerowrap.CtxWithFields(ctx, map[string]any{1128 ctx = zerowrap.CtxWithFields(ctx, map[string]any{
M internal/boundaries/out/mocks/mock_container_runtime.go +66 -0
@@ -994,6 +994,72 @@ func (_c *MockContainerRuntime_GetImageExposedPorts_Call) RunAndReturn(run func(
994994 return _c
995995 }
996996
997+// GetImageID provides a mock function for the type MockContainerRuntime
998+func (_mock *MockContainerRuntime) GetImageID(ctx context.Context, imageRef string) (string, error) {
999+ ret := _mock.Called(ctx, imageRef)
1000+
1001+ if len(ret) == 0 {
1002+ panic("no return value specified for GetImageID")
1003+ }
1004+
1005+ var r0 string
1006+ var r1 error
1007+ if returnFunc, ok := ret.Get(0).(func(context.Context, string) (string, error)); ok {
1008+ return returnFunc(ctx, imageRef)
1009+ }
1010+ if returnFunc, ok := ret.Get(0).(func(context.Context, string) string); ok {
1011+ r0 = returnFunc(ctx, imageRef)
1012+ } else {
1013+ r0 = ret.Get(0).(string)
1014+ }
1015+ if returnFunc, ok := ret.Get(1).(func(context.Context, string) error); ok {
1016+ r1 = returnFunc(ctx, imageRef)
1017+ } else {
1018+ r1 = ret.Error(1)
1019+ }
1020+ return r0, r1
1021+}
1022+
1023+// MockContainerRuntime_GetImageID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetImageID'
1024+type MockContainerRuntime_GetImageID_Call struct {
1025+ *mock.Call
1026+}
1027+
1028+// GetImageID is a helper method to define mock.On call
1029+// - ctx context.Context
1030+// - imageRef string
1031+func (_e *MockContainerRuntime_Expecter) GetImageID(ctx interface{}, imageRef interface{}) *MockContainerRuntime_GetImageID_Call {
1032+ return &MockContainerRuntime_GetImageID_Call{Call: _e.mock.On("GetImageID", ctx, imageRef)}
1033+}
1034+
1035+func (_c *MockContainerRuntime_GetImageID_Call) Run(run func(ctx context.Context, imageRef string)) *MockContainerRuntime_GetImageID_Call {
1036+ _c.Call.Run(func(args mock.Arguments) {
1037+ var arg0 context.Context
1038+ if args[0] != nil {
1039+ arg0 = args[0].(context.Context)
1040+ }
1041+ var arg1 string
1042+ if args[1] != nil {
1043+ arg1 = args[1].(string)
1044+ }
1045+ run(
1046+ arg0,
1047+ arg1,
1048+ )
1049+ })
1050+ return _c
1051+}
1052+
1053+func (_c *MockContainerRuntime_GetImageID_Call) Return(s string, err error) *MockContainerRuntime_GetImageID_Call {
1054+ _c.Call.Return(s, err)
1055+ return _c
1056+}
1057+
1058+func (_c *MockContainerRuntime_GetImageID_Call) RunAndReturn(run func(ctx context.Context, imageRef string) (string, error)) *MockContainerRuntime_GetImageID_Call {
1059+ _c.Call.Return(run)
1060+ return _c
1061+}
1062+
9971063 // GetImageLabels provides a mock function for the type MockContainerRuntime
9981064 func (_mock *MockContainerRuntime) GetImageLabels(ctx context.Context, imageRef string) (map[string]string, error) {
9991065 ret := _mock.Called(ctx, imageRef)
@@ -994,6 +994,72 @@ func (_c *MockContainerRuntime_GetImageExposedPorts_Call) RunAndReturn(run func(
994 return _c994 return _c
995 }995 }
996 996
997+// GetImageID provides a mock function for the type MockContainerRuntime
998+func (_mock *MockContainerRuntime) GetImageID(ctx context.Context, imageRef string) (string, error) {
999+ ret := _mock.Called(ctx, imageRef)
1000+
1001+ if len(ret) == 0 {
1002+ panic("no return value specified for GetImageID")
1003+ }
1004+
1005+ var r0 string
1006+ var r1 error
1007+ if returnFunc, ok := ret.Get(0).(func(context.Context, string) (string, error)); ok {
1008+ return returnFunc(ctx, imageRef)
1009+ }
1010+ if returnFunc, ok := ret.Get(0).(func(context.Context, string) string); ok {
1011+ r0 = returnFunc(ctx, imageRef)
1012+ } else {
1013+ r0 = ret.Get(0).(string)
1014+ }
1015+ if returnFunc, ok := ret.Get(1).(func(context.Context, string) error); ok {
1016+ r1 = returnFunc(ctx, imageRef)
1017+ } else {
1018+ r1 = ret.Error(1)
1019+ }
1020+ return r0, r1
1021+}
1022+
1023+// MockContainerRuntime_GetImageID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetImageID'
1024+type MockContainerRuntime_GetImageID_Call struct {
1025+ *mock.Call
1026+}
1027+
1028+// GetImageID is a helper method to define mock.On call
1029+// - ctx context.Context
1030+// - imageRef string
1031+func (_e *MockContainerRuntime_Expecter) GetImageID(ctx interface{}, imageRef interface{}) *MockContainerRuntime_GetImageID_Call {
1032+ return &MockContainerRuntime_GetImageID_Call{Call: _e.mock.On("GetImageID", ctx, imageRef)}
1033+}
1034+
1035+func (_c *MockContainerRuntime_GetImageID_Call) Run(run func(ctx context.Context, imageRef string)) *MockContainerRuntime_GetImageID_Call {
1036+ _c.Call.Run(func(args mock.Arguments) {
1037+ var arg0 context.Context
1038+ if args[0] != nil {
1039+ arg0 = args[0].(context.Context)
1040+ }
1041+ var arg1 string
1042+ if args[1] != nil {
1043+ arg1 = args[1].(string)
1044+ }
1045+ run(
1046+ arg0,
1047+ arg1,
1048+ )
1049+ })
1050+ return _c
1051+}
1052+
1053+func (_c *MockContainerRuntime_GetImageID_Call) Return(s string, err error) *MockContainerRuntime_GetImageID_Call {
1054+ _c.Call.Return(s, err)
1055+ return _c
1056+}
1057+
1058+func (_c *MockContainerRuntime_GetImageID_Call) RunAndReturn(run func(ctx context.Context, imageRef string) (string, error)) *MockContainerRuntime_GetImageID_Call {
1059+ _c.Call.Return(run)
1060+ return _c
1061+}
1062+
997 // GetImageLabels provides a mock function for the type MockContainerRuntime1063 // GetImageLabels provides a mock function for the type MockContainerRuntime
998 func (_mock *MockContainerRuntime) GetImageLabels(ctx context.Context, imageRef string) (map[string]string, error) {1064 func (_mock *MockContainerRuntime) GetImageLabels(ctx context.Context, imageRef string) (map[string]string, error) {
999 ret := _mock.Called(ctx, imageRef)1065 ret := _mock.Called(ctx, imageRef)

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close