daemon: Daemon.getInspectData: move migration code to router

There also appeared to be duplication between daemon.getInspectData,
and the containerRouter.postContainersCreate methods, as both were
back-filling the field.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn
2025-10-15 17:30:11 +02:00
parent 91ce33d4b0
commit 79912d4c7f
6 changed files with 36 additions and 22 deletions

View File

@@ -43,7 +43,7 @@ type Backend interface {
ActivateContainerServiceBinding(containerName string) error ActivateContainerServiceBinding(containerName string) error
DeactivateContainerServiceBinding(containerName string) error DeactivateContainerServiceBinding(containerName string) error
UpdateContainerServiceConfig(containerName string, serviceConfig *clustertypes.ServiceConfig) error UpdateContainerServiceConfig(containerName string, serviceConfig *clustertypes.ServiceConfig) error
ContainerInspect(ctx context.Context, name string, options backend.ContainerInspectOptions) (*container.InspectResponse, error) ContainerInspect(ctx context.Context, name string, options backend.ContainerInspectOptions) (_ *container.InspectResponse, desiredMACAddress string, _ error)
ContainerWait(ctx context.Context, name string, condition container.WaitCondition) (<-chan containerpkg.StateStatus, error) ContainerWait(ctx context.Context, name string, condition container.WaitCondition) (<-chan containerpkg.StateStatus, error)
ContainerRm(name string, config *backend.ContainerRmConfig) error ContainerRm(name string, config *backend.ContainerRmConfig) error
ContainerKill(name string, sig string) error ContainerKill(name string, sig string) error

View File

@@ -375,7 +375,7 @@ func (c *containerAdapter) start(ctx context.Context) error {
} }
func (c *containerAdapter) inspect(ctx context.Context) (containertypes.InspectResponse, error) { func (c *containerAdapter) inspect(ctx context.Context) (containertypes.InspectResponse, error) {
cs, err := c.backend.ContainerInspect(ctx, c.container.name(), backend.ContainerInspectOptions{}) cs, _, err := c.backend.ContainerInspect(ctx, c.container.name(), backend.ContainerInspectOptions{})
if ctx.Err() != nil { if ctx.Err() != nil {
return containertypes.InspectResponse{}, ctx.Err() return containertypes.InspectResponse{}, ctx.Err()
} }

View File

@@ -19,18 +19,18 @@ import (
// ContainerInspect returns low-level information about a // ContainerInspect returns low-level information about a
// container. Returns an error if the container cannot be found, or if // container. Returns an error if the container cannot be found, or if
// there is an error getting the data. // there is an error getting the data.
func (daemon *Daemon) ContainerInspect(ctx context.Context, name string, options backend.ContainerInspectOptions) (*containertypes.InspectResponse, error) { func (daemon *Daemon) ContainerInspect(ctx context.Context, name string, options backend.ContainerInspectOptions) (_ *containertypes.InspectResponse, desiredMACAddress string, _ error) {
ctr, err := daemon.GetContainer(name) ctr, err := daemon.GetContainer(name)
if err != nil { if err != nil {
return nil, err return nil, "", err
} }
ctr.Lock() ctr.Lock()
base, err := daemon.getInspectData(&daemon.config().Config, ctr) base, desiredMACAddress, err := daemon.getInspectData(&daemon.config().Config, ctr)
if err != nil { if err != nil {
ctr.Unlock() ctr.Unlock()
return nil, err return nil, "", err
} }
// TODO(thaJeztah): do we need a deep copy here? Otherwise we could use maps.Clone (see https://github.com/moby/moby/commit/7917a36cc787ada58987320e67cc6d96858f3b55) // TODO(thaJeztah): do we need a deep copy here? Otherwise we could use maps.Clone (see https://github.com/moby/moby/commit/7917a36cc787ada58987320e67cc6d96858f3b55)
@@ -61,7 +61,7 @@ func (daemon *Daemon) ContainerInspect(ctx context.Context, name string, options
if options.Size { if options.Size {
sizeRw, sizeRootFs, err := daemon.imageService.GetContainerLayerSize(ctx, base.ID) sizeRw, sizeRootFs, err := daemon.imageService.GetContainerLayerSize(ctx, base.ID)
if err != nil { if err != nil {
return nil, err return nil, "", err
} }
base.SizeRw = &sizeRw base.SizeRw = &sizeRw
base.SizeRootFs = &sizeRootFs base.SizeRootFs = &sizeRootFs
@@ -80,10 +80,10 @@ func (daemon *Daemon) ContainerInspect(ctx context.Context, name string, options
base.NetworkSettings = networkSettings base.NetworkSettings = networkSettings
base.ImageManifestDescriptor = imageManifest base.ImageManifestDescriptor = imageManifest
return base, nil return base, desiredMACAddress, nil
} }
func (daemon *Daemon) getInspectData(daemonCfg *config.Config, ctr *container.Container) (*containertypes.InspectResponse, error) { func (daemon *Daemon) getInspectData(daemonCfg *config.Config, ctr *container.Container) (_ *containertypes.InspectResponse, desiredMACAddress string, _ error) {
// make a copy to play with // make a copy to play with
hostConfig := *ctr.HostConfig hostConfig := *ctr.HostConfig
@@ -101,10 +101,11 @@ func (daemon *Daemon) getInspectData(daemonCfg *config.Config, ctr *container.Co
// Config.MacAddress field for older API versions (< 1.44). We set it here // Config.MacAddress field for older API versions (< 1.44). We set it here
// unconditionally, to keep backward compatibility with clients that use // unconditionally, to keep backward compatibility with clients that use
// unversioned API endpoints. // unversioned API endpoints.
if ctr.Config != nil && ctr.Config.MacAddress == "" { //nolint:staticcheck // ignore SA1019: field is deprecated, but still used on API < v1.44. var macAddress string
if ctr.Config != nil {
if nwm := hostConfig.NetworkMode; nwm.IsBridge() || nwm.IsUserDefined() { if nwm := hostConfig.NetworkMode; nwm.IsBridge() || nwm.IsUserDefined() {
if epConf, ok := ctr.NetworkSettings.Networks[nwm.NetworkName()]; ok { if epConf, ok := ctr.NetworkSettings.Networks[nwm.NetworkName()]; ok {
ctr.Config.MacAddress = epConf.DesiredMacAddress //nolint:staticcheck // ignore SA1019: field is deprecated, but still used on API < v1.44. macAddress = epConf.DesiredMacAddress
} }
} }
} }
@@ -163,7 +164,7 @@ func (daemon *Daemon) getInspectData(daemonCfg *config.Config, ctr *container.Co
} }
// Additional information only applies to graphDrivers, so we're done. // Additional information only applies to graphDrivers, so we're done.
return inspectResponse, nil return inspectResponse, macAddress, nil
} }
inspectResponse.GraphDriver = &storage.DriverData{ inspectResponse.GraphDriver = &storage.DriverData{
@@ -171,9 +172,9 @@ func (daemon *Daemon) getInspectData(daemonCfg *config.Config, ctr *container.Co
} }
if ctr.RWLayer == nil { if ctr.RWLayer == nil {
if ctr.State.Dead { if ctr.State.Dead {
return inspectResponse, nil return inspectResponse, macAddress, nil
} }
return nil, errdefs.System(errors.New("RWLayer of container " + ctr.ID + " is unexpectedly nil")) return nil, "", errdefs.System(errors.New("RWLayer of container " + ctr.ID + " is unexpectedly nil"))
} }
graphDriverData, err := ctr.RWLayer.Metadata() graphDriverData, err := ctr.RWLayer.Metadata()
@@ -181,13 +182,13 @@ func (daemon *Daemon) getInspectData(daemonCfg *config.Config, ctr *container.Co
if ctr.State.Dead { if ctr.State.Dead {
// container is marked as Dead, and its graphDriver metadata may // container is marked as Dead, and its graphDriver metadata may
// have been removed; we can ignore errors. // have been removed; we can ignore errors.
return inspectResponse, nil return inspectResponse, macAddress, nil
} }
return nil, errdefs.System(err) return nil, "", errdefs.System(err)
} }
inspectResponse.GraphDriver.Data = graphDriverData inspectResponse.GraphDriver.Data = graphDriverData
return inspectResponse, nil return inspectResponse, macAddress, nil
} }
// ContainerExecInspect returns low-level information about the exec // ContainerExecInspect returns low-level information about the exec

View File

@@ -26,10 +26,10 @@ func TestGetInspectData(t *testing.T) {
cfg := &configStore{} cfg := &configStore{}
d.configStore.Store(cfg) d.configStore.Store(cfg)
_, err := d.getInspectData(&cfg.Config, c) _, _, err := d.getInspectData(&cfg.Config, c)
assert.Check(t, is.ErrorContains(err, "RWLayer of container inspect-me is unexpectedly nil")) assert.Check(t, is.ErrorContains(err, "RWLayer of container inspect-me is unexpectedly nil"))
c.State.Dead = true c.State.Dead = true
_, err = d.getInspectData(&cfg.Config, c) _, _, err = d.getInspectData(&cfg.Config, c)
assert.Check(t, err) assert.Check(t, err)
} }

View File

@@ -48,7 +48,7 @@ type stateBackend interface {
// monitorBackend includes functions to implement to provide containers monitoring functionality. // monitorBackend includes functions to implement to provide containers monitoring functionality.
type monitorBackend interface { type monitorBackend interface {
ContainerChanges(ctx context.Context, name string) ([]archive.Change, error) ContainerChanges(ctx context.Context, name string) ([]archive.Change, error)
ContainerInspect(ctx context.Context, name string, options backend.ContainerInspectOptions) (*container.InspectResponse, error) ContainerInspect(ctx context.Context, name string, options backend.ContainerInspectOptions) (_ *container.InspectResponse, desiredMACAddress string, _ error)
ContainerLogs(ctx context.Context, name string, config *backend.ContainerLogsOptions) (msgs <-chan *backend.LogMessage, tty bool, err error) ContainerLogs(ctx context.Context, name string, config *backend.ContainerLogsOptions) (msgs <-chan *backend.LogMessage, tty bool, err error)
ContainerStats(ctx context.Context, name string, config *backend.ContainerStatsConfig) error ContainerStats(ctx context.Context, name string, config *backend.ContainerStatsConfig) error
ContainerTop(name string, psArgs string) (*container.TopResponse, error) ContainerTop(name string, psArgs string) (*container.TopResponse, error)

View File

@@ -15,7 +15,7 @@ import (
// getContainersByName inspects container's configuration and serializes it as json. // getContainersByName inspects container's configuration and serializes it as json.
func (c *containerRouter) getContainersByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { func (c *containerRouter) getContainersByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
ctr, err := c.backend.ContainerInspect(ctx, vars["name"], backend.ContainerInspectOptions{ ctr, desiredMACAddress, err := c.backend.ContainerInspect(ctx, vars["name"], backend.ContainerInspectOptions{
Size: httputils.BoolValue(r, "size"), Size: httputils.BoolValue(r, "size"),
}) })
if err != nil { if err != nil {
@@ -39,7 +39,7 @@ func (c *containerRouter) getContainersByName(ctx context.Context, w http.Respon
if versions.LessThan(version, "1.52") { if versions.LessThan(version, "1.52") {
if bridgeNw := ctr.NetworkSettings.Networks["bridge"]; bridgeNw != nil { if bridgeNw := ctr.NetworkSettings.Networks["bridge"]; bridgeNw != nil {
// Old API versions showed the bridge's configuration as top-level // Old API versions showed the bridge's configuration as top-level
// fields in "NetworkConfig". // fields in "NetworkSettings".
// //
// This was deprecated in API v1.44, but kept in place until // This was deprecated in API v1.44, but kept in place until
// API v1.52, which removes this entirely. // API v1.52, which removes this entirely.
@@ -56,6 +56,19 @@ func (c *containerRouter) getContainersByName(ctx context.Context, w http.Respon
}, },
})) }))
} }
// Migrate the container's main / default network's MacAddress to
// the Config.MacAddress field for older API versions (< 1.44).
//
// This was deprecated in API v1.44, but kept in place until
// API v1.52, which removed this entirely.
if desiredMACAddress != "" {
wrapOpts = append(wrapOpts, compat.WithExtraFields(map[string]any{
"Config": map[string]any{
"MacAddress": desiredMACAddress,
},
}))
}
} }
return httputils.WriteJSON(w, http.StatusOK, compat.Wrap(ctr, wrapOpts...)) return httputils.WriteJSON(w, http.StatusOK, compat.Wrap(ctr, wrapOpts...))