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

Code before:
	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,
	})
}

func (epi *EndpointInterface) UnmarshalJSON(b []byte) error {
	var epiTmp endpointInterface

Review comment (bug):
With `omitzero` fields missing in the store, an older daemon trying to load this will crash in `UnmarshalJSON` - for example `epi.srcName = epMap["srcName"].(string)`.

But I think `EndpointSettings` might only be read from store on live-restore (when a container's stopped, its endpoints are deleted), and we don't claim live-restore works over a major version change.

Even so, I think it'd be best to remove `omitzero` from `SrcName`, `DstPrefix`, `V4PoolD` and `V6PoolID`?

Code after:
	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,
	})
}