Repository: moby/moby
File: daemon/libnetwork/endpoint_info.go (Go)

Code before:
type EndpointInterface struct {
	mac                net.HardwareAddr
	addr               *net.IPNet
	addrv6             *net.IPNet
	llAddrs            []*net.IPNet
	srcName            string
	dstPrefix          string
	dstName            string // dstName is the name of the interface in the container namespace. It takes precedence over dstPrefix.
	routes             []*net.IPNet
	v4PoolID           string
	v6PoolID           string
	netnsPath          string
	createdInContainer bool
}

// endpointInterface is an intermediate struct used to marshal/unmarshal
// am [EndpointInterface] to JSON.
type endpointInterface struct {
	MAC                string         `json:"mac,omitzero"`
	Addr               netip.Prefix   `json:"addr,omitzero"`
	AddrV6             netip.Prefix   `json:"addrv6,omitzero"`
	LLAddrs            []netip.Prefix `json:"llAddrs,omitzero"`
	Routes             []netip.Prefix `json:"routes,omitzero"`
	SrcName            string         `json:"srcName,omitzero"`
	DstPrefix          string         `json:"dstPrefix,omitzero"`
	DstName            string         `json:"dstName,omitzero"`
	V4PoolID           string         `json:"v4PoolID,omitzero"`
	V6PoolID           string         `json:"v6PoolID,omitzero"`
	CreatedInContainer bool           `json:"createdInContainer,omitzero"`
}

func (epi *EndpointInterface) MarshalJSON() ([]byte, error) {
	mac := ""
	if epi.mac != nil {
		mac = epi.mac.String()
	}

	return json.Marshal(&endpointInterface{
		MAC:                mac,
		Addr:               toPrefix(epi.addr),
		AddrV6:             toPrefix(epi.addrv6),
		LLAddrs:            toPrefixes(epi.llAddrs),
		Routes:             toPrefixes(epi.routes),
		SrcName:            epi.srcName,
		DstPrefix:          epi.dstPrefix,
		DstName:            epi.dstName,
		V4PoolID:           epi.v4PoolID,
		V6PoolID:           epi.v6PoolID,
		CreatedInContainer: epi.createdInContainer,
	})
}

Review comment (bug):
Looks like `dstName` wasn't unmarshalled before - it comes from 56a7817b2d8e4e20d6aa81e5d43d072717c71519 ...

It's set by the driver option `com.docker.network.endpoint.ifname`, which is stored as a label in `EndpointSettings.DriverOpts` - so the interface name is reapplied whenever a container's started. On live-restore, when the endpoint will be loaded from store, the interface doesn't need to be re-created anyway. So I don't think there's a bug, but cc @akerouanton to double-check.

_After this change, no more issues like this - nice!_

Code after:
type EndpointInterface struct {
	mac                net.HardwareAddr
	addr               *net.IPNet
	addrv6             *net.IPNet
	llAddrs            []*net.IPNet
	srcName            string
	dstPrefix          string
	dstName            string // dstName is the name of the interface in the container namespace. It takes precedence over dstPrefix.
	routes             []*net.IPNet
	v4PoolID           string
	v6PoolID           string
	netnsPath          string
	createdInContainer bool
}

// endpointInterface is an intermediate struct used to marshal/unmarshal
// am [EndpointInterface] to JSON.
//
// TODO(thaJeztah): use "omitzero" for all fields once we no longer have to consider downgrades to < v29.0; see https://github.com/moby/moby/pull/51223#discussion_r2445826610
type endpointInterface struct {
	MAC                string         `json:"mac"`
	Addr               netip.Prefix   `json:"addr"`
	AddrV6             netip.Prefix   `json:"addrv6"`
	LLAddrs            []netip.Prefix `json:"llAddrs"`
	Routes             []netip.Prefix `json:"routes"`
	SrcName            string         `json:"srcName"`
	DstPrefix          string         `json:"dstPrefix"`
	DstName            string         `json:"dstName"`
	V4PoolID           string         `json:"v4PoolID"`
	V6PoolID           string         `json:"v6PoolID"`
	CreatedInContainer bool           `json:"createdInContainer"`
}

func (epi *EndpointInterface) MarshalJSON() ([]byte, error) {
	mac := ""
	if epi.mac != nil {
		mac = epi.mac.String()
	}

	return json.Marshal(&endpointInterface{
		MAC:                mac,
		Addr:               toPrefix(epi.addr),
		AddrV6:             toPrefix(epi.addrv6),
		LLAddrs:            toPrefixes(epi.llAddrs),
		Routes:             toPrefixes(epi.routes),
		SrcName:            epi.srcName,
		DstPrefix:          epi.dstPrefix,
		DstName:            epi.dstName,
		V4PoolID:           epi.v4PoolID,
		V6PoolID:           epi.v6PoolID,
		CreatedInContainer: epi.createdInContainer,