mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #37209 from thaJeztah/bump_libnetwork
bump libnetwork to 3931ba4d815e385ab97093c64477b82f14dadefb
This commit is contained in:
commit
21291e5aef
12 changed files with 81 additions and 88 deletions
|
@ -3,7 +3,7 @@
|
||||||
# LIBNETWORK_COMMIT is used to build the docker-userland-proxy binary. When
|
# LIBNETWORK_COMMIT is used to build the docker-userland-proxy binary. When
|
||||||
# updating the binary version, consider updating github.com/docker/libnetwork
|
# updating the binary version, consider updating github.com/docker/libnetwork
|
||||||
# in vendor.conf accordingly
|
# in vendor.conf accordingly
|
||||||
LIBNETWORK_COMMIT=c15b372ef22125880d378167dde44f4b134e1a77
|
LIBNETWORK_COMMIT=3931ba4d815e385ab97093c64477b82f14dadefb
|
||||||
|
|
||||||
install_proxy() {
|
install_proxy() {
|
||||||
case "$1" in
|
case "$1" in
|
||||||
|
|
|
@ -35,7 +35,7 @@ github.com/opentracing/opentracing-go 1361b9cd60be79c4c3a7fa9841b3c132e40066a7
|
||||||
#get libnetwork packages
|
#get libnetwork packages
|
||||||
|
|
||||||
# When updating, also update LIBNETWORK_COMMIT in hack/dockerfile/install/proxy accordingly
|
# When updating, also update LIBNETWORK_COMMIT in hack/dockerfile/install/proxy accordingly
|
||||||
github.com/docker/libnetwork eb6b2a57955e5c149d47c3973573216e8f8baa09
|
github.com/docker/libnetwork 3931ba4d815e385ab97093c64477b82f14dadefb
|
||||||
github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9
|
github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9
|
||||||
github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
|
github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
|
||||||
github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
|
github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
|
||||||
|
|
5
vendor/github.com/docker/libnetwork/controller.go
generated
vendored
5
vendor/github.com/docker/libnetwork/controller.go
generated
vendored
|
@ -44,7 +44,6 @@ create network namespaces and allocate interfaces for containers to use.
|
||||||
package libnetwork
|
package libnetwork
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"container/heap"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -1085,7 +1084,7 @@ func (c *controller) NewSandbox(containerID string, options ...SandboxOption) (S
|
||||||
sb = &sandbox{
|
sb = &sandbox{
|
||||||
id: sandboxID,
|
id: sandboxID,
|
||||||
containerID: containerID,
|
containerID: containerID,
|
||||||
endpoints: epHeap{},
|
endpoints: []*endpoint{},
|
||||||
epPriority: map[string]int{},
|
epPriority: map[string]int{},
|
||||||
populatedEndpoints: map[string]struct{}{},
|
populatedEndpoints: map[string]struct{}{},
|
||||||
config: containerConfig{},
|
config: containerConfig{},
|
||||||
|
@ -1094,8 +1093,6 @@ func (c *controller) NewSandbox(containerID string, options ...SandboxOption) (S
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
heap.Init(&sb.endpoints)
|
|
||||||
|
|
||||||
sb.processOptions(options...)
|
sb.processOptions(options...)
|
||||||
|
|
||||||
c.Lock()
|
c.Lock()
|
||||||
|
|
4
vendor/github.com/docker/libnetwork/drivers/bridge/bridge.go
generated
vendored
4
vendor/github.com/docker/libnetwork/drivers/bridge/bridge.go
generated
vendored
|
@ -706,8 +706,8 @@ func (d *driver) createNetwork(config *networkConfiguration) error {
|
||||||
// Enable IPv6 Forwarding
|
// Enable IPv6 Forwarding
|
||||||
{enableIPv6Forwarding, setupIPv6Forwarding},
|
{enableIPv6Forwarding, setupIPv6Forwarding},
|
||||||
|
|
||||||
// Setup Loopback Adresses Routing
|
// Setup Loopback Addresses Routing
|
||||||
{!d.config.EnableUserlandProxy, setupLoopbackAdressesRouting},
|
{!d.config.EnableUserlandProxy, setupLoopbackAddressesRouting},
|
||||||
|
|
||||||
// Setup IPTables.
|
// Setup IPTables.
|
||||||
{d.config.EnableIPTables, network.setupIPTables},
|
{d.config.EnableIPTables, network.setupIPTables},
|
||||||
|
|
4
vendor/github.com/docker/libnetwork/drivers/bridge/setup_ipv4.go
generated
vendored
4
vendor/github.com/docker/libnetwork/drivers/bridge/setup_ipv4.go
generated
vendored
|
@ -64,13 +64,13 @@ func setupGatewayIPv4(config *networkConfiguration, i *bridgeInterface) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupLoopbackAdressesRouting(config *networkConfiguration, i *bridgeInterface) error {
|
func setupLoopbackAddressesRouting(config *networkConfiguration, i *bridgeInterface) error {
|
||||||
sysPath := filepath.Join("/proc/sys/net/ipv4/conf", config.BridgeName, "route_localnet")
|
sysPath := filepath.Join("/proc/sys/net/ipv4/conf", config.BridgeName, "route_localnet")
|
||||||
ipv4LoRoutingData, err := ioutil.ReadFile(sysPath)
|
ipv4LoRoutingData, err := ioutil.ReadFile(sysPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Cannot read IPv4 local routing setup: %v", err)
|
return fmt.Errorf("Cannot read IPv4 local routing setup: %v", err)
|
||||||
}
|
}
|
||||||
// Enable loopback adresses routing only if it isn't already enabled
|
// Enable loopback addresses routing only if it isn't already enabled
|
||||||
if ipv4LoRoutingData[0] != '1' {
|
if ipv4LoRoutingData[0] != '1' {
|
||||||
if err := ioutil.WriteFile(sysPath, []byte{'1', '\n'}, 0644); err != nil {
|
if err := ioutil.WriteFile(sysPath, []byte{'1', '\n'}, 0644); err != nil {
|
||||||
return fmt.Errorf("Unable to enable local routing for hairpin mode: %v", err)
|
return fmt.Errorf("Unable to enable local routing for hairpin mode: %v", err)
|
||||||
|
|
2
vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_setup.go
generated
vendored
2
vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_setup.go
generated
vendored
|
@ -150,7 +150,7 @@ func parseVlan(linkName string) (string, int, error) {
|
||||||
}
|
}
|
||||||
// Check if the interface exists
|
// Check if the interface exists
|
||||||
if !parentExists(parent) {
|
if !parentExists(parent) {
|
||||||
return "", 0, fmt.Errorf("-o parent interface does was not found on the host: %s", parent)
|
return "", 0, fmt.Errorf("-o parent interface was not found on the host: %s", parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
return parent, vidInt, nil
|
return parent, vidInt, nil
|
||||||
|
|
7
vendor/github.com/docker/libnetwork/endpoint.go
generated
vendored
7
vendor/github.com/docker/libnetwork/endpoint.go
generated
vendored
|
@ -1,7 +1,6 @@
|
||||||
package libnetwork
|
package libnetwork
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"container/heap"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
@ -514,9 +513,7 @@ func (ep *endpoint) sbJoin(sb *sandbox, options ...EndpointOption) (err error) {
|
||||||
// Current endpoint providing external connectivity for the sandbox
|
// Current endpoint providing external connectivity for the sandbox
|
||||||
extEp := sb.getGatewayEndpoint()
|
extEp := sb.getGatewayEndpoint()
|
||||||
|
|
||||||
sb.Lock()
|
sb.addEndpoint(ep)
|
||||||
heap.Push(&sb.endpoints, ep)
|
|
||||||
sb.Unlock()
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sb.removeEndpoint(ep)
|
sb.removeEndpoint(ep)
|
||||||
|
@ -755,11 +752,9 @@ func (ep *endpoint) sbLeave(sb *sandbox, force bool, options ...EndpointOption)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ep.svcID != "" {
|
|
||||||
if err := ep.deleteServiceInfoFromCluster(sb, true, "sbLeave"); err != nil {
|
if err := ep.deleteServiceInfoFromCluster(sb, true, "sbLeave"); err != nil {
|
||||||
logrus.Warnf("Failed to clean up service info on container %s disconnect: %v", ep.name, err)
|
logrus.Warnf("Failed to clean up service info on container %s disconnect: %v", ep.name, err)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if err := sb.clearNetworkResources(ep); err != nil {
|
if err := sb.clearNetworkResources(ep); err != nil {
|
||||||
logrus.Warnf("Failed to clean up network resources on container %s disconnect: %v", ep.name, err)
|
logrus.Warnf("Failed to clean up network resources on container %s disconnect: %v", ep.name, err)
|
||||||
|
|
2
vendor/github.com/docker/libnetwork/ipam/allocator.go
generated
vendored
2
vendor/github.com/docker/libnetwork/ipam/allocator.go
generated
vendored
|
@ -389,7 +389,7 @@ func (a *Allocator) getPredefinedPool(as string, ipV6 bool) (*net.IPNet, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
if as != localAddressSpace && as != globalAddressSpace {
|
if as != localAddressSpace && as != globalAddressSpace {
|
||||||
return nil, types.NotImplementedErrorf("no default pool availbale for non-default addresss spaces")
|
return nil, types.NotImplementedErrorf("no default pool available for non-default address spaces")
|
||||||
}
|
}
|
||||||
|
|
||||||
aSpace, err := a.getAddrSpace(as)
|
aSpace, err := a.getAddrSpace(as)
|
||||||
|
|
2
vendor/github.com/docker/libnetwork/resolver.go
generated
vendored
2
vendor/github.com/docker/libnetwork/resolver.go
generated
vendored
|
@ -280,7 +280,7 @@ func (r *resolver) handleIPQuery(name string, query *dns.Msg, ipType int) (*dns.
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *resolver) handlePTRQuery(ptr string, query *dns.Msg) (*dns.Msg, error) {
|
func (r *resolver) handlePTRQuery(ptr string, query *dns.Msg) (*dns.Msg, error) {
|
||||||
parts := []string{}
|
var parts []string
|
||||||
|
|
||||||
if strings.HasSuffix(ptr, ptrIPv4domain) {
|
if strings.HasSuffix(ptr, ptrIPv4domain) {
|
||||||
parts = strings.Split(ptr, ptrIPv4domain)
|
parts = strings.Split(ptr, ptrIPv4domain)
|
||||||
|
|
127
vendor/github.com/docker/libnetwork/sandbox.go
generated
vendored
127
vendor/github.com/docker/libnetwork/sandbox.go
generated
vendored
|
@ -1,10 +1,10 @@
|
||||||
package libnetwork
|
package libnetwork
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"container/heap"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
@ -63,8 +63,6 @@ func (sb *sandbox) processOptions(options ...SandboxOption) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type epHeap []*endpoint
|
|
||||||
|
|
||||||
type sandbox struct {
|
type sandbox struct {
|
||||||
id string
|
id string
|
||||||
containerID string
|
containerID string
|
||||||
|
@ -75,7 +73,7 @@ type sandbox struct {
|
||||||
resolver Resolver
|
resolver Resolver
|
||||||
resolverOnce sync.Once
|
resolverOnce sync.Once
|
||||||
refCnt int
|
refCnt int
|
||||||
endpoints epHeap
|
endpoints []*endpoint
|
||||||
epPriority map[string]int
|
epPriority map[string]int
|
||||||
populatedEndpoints map[string]struct{}
|
populatedEndpoints map[string]struct{}
|
||||||
joinLeaveDone chan struct{}
|
joinLeaveDone chan struct{}
|
||||||
|
@ -353,20 +351,36 @@ func (sb *sandbox) getConnectedEndpoints() []*endpoint {
|
||||||
defer sb.Unlock()
|
defer sb.Unlock()
|
||||||
|
|
||||||
eps := make([]*endpoint, len(sb.endpoints))
|
eps := make([]*endpoint, len(sb.endpoints))
|
||||||
for i, ep := range sb.endpoints {
|
copy(eps, sb.endpoints)
|
||||||
eps[i] = ep
|
|
||||||
}
|
|
||||||
|
|
||||||
return eps
|
return eps
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sb *sandbox) addEndpoint(ep *endpoint) {
|
||||||
|
sb.Lock()
|
||||||
|
defer sb.Unlock()
|
||||||
|
|
||||||
|
l := len(sb.endpoints)
|
||||||
|
i := sort.Search(l, func(j int) bool {
|
||||||
|
return ep.Less(sb.endpoints[j])
|
||||||
|
})
|
||||||
|
|
||||||
|
sb.endpoints = append(sb.endpoints, nil)
|
||||||
|
copy(sb.endpoints[i+1:], sb.endpoints[i:])
|
||||||
|
sb.endpoints[i] = ep
|
||||||
|
}
|
||||||
|
|
||||||
func (sb *sandbox) removeEndpoint(ep *endpoint) {
|
func (sb *sandbox) removeEndpoint(ep *endpoint) {
|
||||||
sb.Lock()
|
sb.Lock()
|
||||||
defer sb.Unlock()
|
defer sb.Unlock()
|
||||||
|
|
||||||
|
sb.removeEndpointRaw(ep)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sb *sandbox) removeEndpointRaw(ep *endpoint) {
|
||||||
for i, e := range sb.endpoints {
|
for i, e := range sb.endpoints {
|
||||||
if e == ep {
|
if e == ep {
|
||||||
heap.Remove(&sb.endpoints, i)
|
sb.endpoints = append(sb.endpoints[:i], sb.endpoints[i+1:]...)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -940,7 +954,7 @@ func (sb *sandbox) clearNetworkResources(origEp *endpoint) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
heap.Remove(&sb.endpoints, index)
|
sb.removeEndpointRaw(ep)
|
||||||
for _, e := range sb.endpoints {
|
for _, e := range sb.endpoints {
|
||||||
if len(e.Gateway()) > 0 {
|
if len(e.Gateway()) > 0 {
|
||||||
gwepAfter = e
|
gwepAfter = e
|
||||||
|
@ -1165,80 +1179,69 @@ func OptionIngress() SandboxOption {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (eh epHeap) Len() int { return len(eh) }
|
// <=> Returns true if a < b, false if a > b and advances to next level if a == b
|
||||||
|
// epi.prio <=> epj.prio # 2 < 1
|
||||||
func (eh epHeap) Less(i, j int) bool {
|
// epi.gw <=> epj.gw # non-gw < gw
|
||||||
|
// epi.internal <=> epj.internal # non-internal < internal
|
||||||
|
// epi.joininfo <=> epj.joininfo # ipv6 < ipv4
|
||||||
|
// epi.name <=> epj.name # bar < foo
|
||||||
|
func (epi *endpoint) Less(epj *endpoint) bool {
|
||||||
var (
|
var (
|
||||||
cip, cjp int
|
prioi, prioj int
|
||||||
ok bool
|
|
||||||
)
|
)
|
||||||
|
|
||||||
ci, _ := eh[i].getSandbox()
|
sbi, _ := epi.getSandbox()
|
||||||
cj, _ := eh[j].getSandbox()
|
sbj, _ := epj.getSandbox()
|
||||||
|
|
||||||
epi := eh[i]
|
// Prio defaults to 0
|
||||||
epj := eh[j]
|
if sbi != nil {
|
||||||
|
prioi = sbi.epPriority[epi.ID()]
|
||||||
if epi.endpointInGWNetwork() {
|
}
|
||||||
return false
|
if sbj != nil {
|
||||||
|
prioj = sbj.epPriority[epj.ID()]
|
||||||
}
|
}
|
||||||
|
|
||||||
if epj.endpointInGWNetwork() {
|
if prioi != prioj {
|
||||||
return true
|
return prioi > prioj
|
||||||
}
|
}
|
||||||
|
|
||||||
if epi.getNetwork().Internal() {
|
gwi := epi.endpointInGWNetwork()
|
||||||
return false
|
gwj := epj.endpointInGWNetwork()
|
||||||
|
if gwi != gwj {
|
||||||
|
return gwj
|
||||||
}
|
}
|
||||||
|
|
||||||
if epj.getNetwork().Internal() {
|
inti := epi.getNetwork().Internal()
|
||||||
return true
|
intj := epj.getNetwork().Internal()
|
||||||
|
if inti != intj {
|
||||||
|
return intj
|
||||||
}
|
}
|
||||||
|
|
||||||
if epi.joinInfo != nil && epj.joinInfo != nil {
|
jii := 0
|
||||||
if (epi.joinInfo.gw != nil && epi.joinInfo.gw6 != nil) &&
|
if epi.joinInfo != nil {
|
||||||
(epj.joinInfo.gw == nil || epj.joinInfo.gw6 == nil) {
|
if epi.joinInfo.gw != nil {
|
||||||
return true
|
jii = jii + 1
|
||||||
}
|
}
|
||||||
if (epj.joinInfo.gw != nil && epj.joinInfo.gw6 != nil) &&
|
if epi.joinInfo.gw6 != nil {
|
||||||
(epi.joinInfo.gw == nil || epi.joinInfo.gw6 == nil) {
|
jii = jii + 2
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ci != nil {
|
jij := 0
|
||||||
cip, ok = ci.epPriority[eh[i].ID()]
|
if epj.joinInfo != nil {
|
||||||
if !ok {
|
if epj.joinInfo.gw != nil {
|
||||||
cip = 0
|
jij = jij + 1
|
||||||
|
}
|
||||||
|
if epj.joinInfo.gw6 != nil {
|
||||||
|
jij = jij + 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if cj != nil {
|
if jii != jij {
|
||||||
cjp, ok = cj.epPriority[eh[j].ID()]
|
return jii > jij
|
||||||
if !ok {
|
|
||||||
cjp = 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if cip == cjp {
|
return epi.network.Name() < epj.network.Name()
|
||||||
return eh[i].network.Name() < eh[j].network.Name()
|
|
||||||
}
|
|
||||||
|
|
||||||
return cip > cjp
|
|
||||||
}
|
|
||||||
|
|
||||||
func (eh epHeap) Swap(i, j int) { eh[i], eh[j] = eh[j], eh[i] }
|
|
||||||
|
|
||||||
func (eh *epHeap) Push(x interface{}) {
|
|
||||||
*eh = append(*eh, x.(*endpoint))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (eh *epHeap) Pop() interface{} {
|
|
||||||
old := *eh
|
|
||||||
n := len(old)
|
|
||||||
x := old[n-1]
|
|
||||||
*eh = old[0 : n-1]
|
|
||||||
return x
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sb *sandbox) NdotsSet() bool {
|
func (sb *sandbox) NdotsSet() bool {
|
||||||
|
|
6
vendor/github.com/docker/libnetwork/sandbox_store.go
generated
vendored
6
vendor/github.com/docker/libnetwork/sandbox_store.go
generated
vendored
|
@ -1,7 +1,6 @@
|
||||||
package libnetwork
|
package libnetwork
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"container/heap"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
@ -215,7 +214,7 @@ func (c *controller) sandboxCleanup(activeSandboxes map[string]interface{}) {
|
||||||
id: sbs.ID,
|
id: sbs.ID,
|
||||||
controller: sbs.c,
|
controller: sbs.c,
|
||||||
containerID: sbs.Cid,
|
containerID: sbs.Cid,
|
||||||
endpoints: epHeap{},
|
endpoints: []*endpoint{},
|
||||||
populatedEndpoints: map[string]struct{}{},
|
populatedEndpoints: map[string]struct{}{},
|
||||||
dbIndex: sbs.dbIndex,
|
dbIndex: sbs.dbIndex,
|
||||||
isStub: true,
|
isStub: true,
|
||||||
|
@ -242,7 +241,6 @@ func (c *controller) sandboxCleanup(activeSandboxes map[string]interface{}) {
|
||||||
sb.processOptions(opts...)
|
sb.processOptions(opts...)
|
||||||
sb.restorePath()
|
sb.restorePath()
|
||||||
create = !sb.config.useDefaultSandBox
|
create = !sb.config.useDefaultSandBox
|
||||||
heap.Init(&sb.endpoints)
|
|
||||||
}
|
}
|
||||||
sb.osSbox, err = osl.NewSandbox(sb.Key(), create, isRestore)
|
sb.osSbox, err = osl.NewSandbox(sb.Key(), create, isRestore)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -272,7 +270,7 @@ func (c *controller) sandboxCleanup(activeSandboxes map[string]interface{}) {
|
||||||
logrus.Errorf("failed to restore endpoint %s in %s for container %s due to %v", eps.Eid, eps.Nid, sb.ContainerID(), err)
|
logrus.Errorf("failed to restore endpoint %s in %s for container %s due to %v", eps.Eid, eps.Nid, sb.ContainerID(), err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
heap.Push(&sb.endpoints, ep)
|
sb.addEndpoint(ep)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := activeSandboxes[sb.ID()]; !ok {
|
if _, ok := activeSandboxes[sb.ID()]; !ok {
|
||||||
|
|
2
vendor/github.com/docker/libnetwork/types/types.go
generated
vendored
2
vendor/github.com/docker/libnetwork/types/types.go
generated
vendored
|
@ -145,7 +145,7 @@ func (p *PortBinding) String() string {
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromString reads the TransportPort structure from string
|
// FromString reads the PortBinding structure from string
|
||||||
func (p *PortBinding) FromString(s string) error {
|
func (p *PortBinding) FromString(s string) error {
|
||||||
ps := strings.Split(s, "/")
|
ps := strings.Split(s, "/")
|
||||||
if len(ps) != 3 {
|
if len(ps) != 3 {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue