diff --git a/pkg/hns/netconf_windows.go b/pkg/hns/netconf_windows.go index c0b0c316..01974333 100644 --- a/pkg/hns/netconf_windows.go +++ b/pkg/hns/netconf_windows.go @@ -20,10 +20,11 @@ import ( "fmt" "net" + "strings" + "github.com/Microsoft/hcsshim/hcn" "github.com/buger/jsonparser" "github.com/containernetworking/cni/pkg/types" - "strings" ) // NetConf is the CNI spec @@ -46,8 +47,16 @@ type RuntimeDNS struct { Search []string `json:"searches,omitempty"` } +type PortMapEntry struct { + HostPort int `json:"hostPort"` + ContainerPort int `json:"containerPort"` + Protocol string `json:"protocol"` + HostIP string `json:"hostIP,omitempty"` +} + type RuntimeConfig struct { - DNS RuntimeDNS `json:"dns"` + DNS RuntimeDNS `json:"dns"` + PortMaps []PortMapEntry `json:"portMappings,omitempty"` } type policy struct { @@ -207,3 +216,21 @@ func (n *NetConf) ApplyDefaultPAPolicy(paAddress string) { Value: []byte(`{"Type": "PA", "PA": "` + paAddress + `"}`), }) } + +// ApplyPortMappingPolicy is used to configure HostPort<>ContainerPort mapping in HNS +func (n *NetConf) ApplyPortMappingPolicy(portMappings []PortMapEntry) { + if portMappings == nil { + return + } + + if n.Policies == nil { + n.Policies = make([]policy, 0) + } + + for _, portMapping := range portMappings { + n.Policies = append(n.Policies, policy{ + Name: "EndpointPolicy", + Value: []byte(fmt.Sprintf(`{"Type": "NAT", "InternalPort": %d, "ExternalPort": %d, "Protocol": "%s"}`, portMapping.ContainerPort, portMapping.HostPort, portMapping.Protocol)), + }) + } +} diff --git a/pkg/hns/netconf_windows_test.go b/pkg/hns/netconf_windows_test.go index 0108056f..4bfa18af 100644 --- a/pkg/hns/netconf_windows_test.go +++ b/pkg/hns/netconf_windows_test.go @@ -128,6 +128,53 @@ var _ = Describe("HNS NetConf", func() { }) }) + Describe("ApplyPortMappingPolicy", func() { + Context("when portMappings not activated", func() { + It("does nothing", func() { + n := NetConf{} + n.ApplyPortMappingPolicy(nil) + Expect(n.Policies).Should(BeNil()) + + n.ApplyPortMappingPolicy([]PortMapEntry{}) + Expect(n.Policies).Should(HaveLen(0)) + }) + }) + + Context("when portMappings is activated", func() { + It("creates NAT policies", func() { + n := NetConf{} + n.ApplyPortMappingPolicy([]PortMapEntry{ + { + ContainerPort: 80, + HostPort: 8080, + Protocol: "TCP", + HostIP: "ignored", + }, + }) + + Expect(n.Policies).Should(HaveLen(1)) + + policy := n.Policies[0] + Expect(policy.Name).Should(Equal("EndpointPolicy")) + + value := make(map[string]interface{}) + json.Unmarshal(policy.Value, &value) + + Expect(value).Should(HaveKey("Type")) + Expect(value["Type"]).Should(Equal("NAT")) + + Expect(value).Should(HaveKey("InternalPort")) + Expect(value["InternalPort"]).Should(Equal(float64(80))) + + Expect(value).Should(HaveKey("ExternalPort")) + Expect(value["ExternalPort"]).Should(Equal(float64(8080))) + + Expect(value).Should(HaveKey("Protocol")) + Expect(value["Protocol"]).Should(Equal("TCP")) + }) + }) + }) + Describe("MarshalPolicies", func() { Context("when not set by user", func() { It("sets it by adding a policy", func() { diff --git a/plugins/main/windows/win-bridge/README.md b/plugins/main/windows/win-bridge/README.md index 0bd8fb2d..1ffd5f41 100644 --- a/plugins/main/windows/win-bridge/README.md +++ b/plugins/main/windows/win-bridge/README.md @@ -35,7 +35,8 @@ With win-bridge plugin, all containers (on the same host) are plugged into an L2 ], "loopbackDSR": true, "capabilities": { - "dns": true + "dns": true, + "portMappings": true } } ``` @@ -54,4 +55,5 @@ With win-bridge plugin, all containers (on the same host) are plugged into an L2 * `HcnPolicyArgs` (list, optional): List of hcn policies to be used (only used when ApiVersion is 2). * `loopbackDSR` (bool, optional): If true, will add a policy to allow the interface to support loopback direct server return. * `capabilities` (dictionary, optional): Runtime capabilities to enable. - * `dns` (boolean, optional): If true, will take the dns config supplied by the runtime and override other settings. \ No newline at end of file + * `dns` (boolean, optional): If true, will take the dns config supplied by the runtime and override other settings. + * `portMappings` (boolean, optional): If true, will handle HostPort<>ContainerPort mapping using NAT HNS Policies \ No newline at end of file diff --git a/plugins/main/windows/win-bridge/win-bridge_windows.go b/plugins/main/windows/win-bridge/win-bridge_windows.go index 5d7ce835..102543a4 100644 --- a/plugins/main/windows/win-bridge/win-bridge_windows.go +++ b/plugins/main/windows/win-bridge/win-bridge_windows.go @@ -86,6 +86,9 @@ func ProcessEndpointArgs(args *skel.CmdArgs, n *NetConf) (*hns.EndpointInfo, err n.ApplyOutboundNatPolicy(n.IPMasqNetwork) } + // Add HostPort mapping if any present + n.ApplyPortMappingPolicy(n.RuntimeConfig.PortMaps) + epInfo.DNS = n.GetDNS() return epInfo, nil @@ -107,7 +110,6 @@ func cmdHnsAdd(args *skel.CmdArgs, n *NetConf) (*current.Result, error) { } epName := hns.ConstructEndpointName(args.ContainerID, args.Netns, n.Name) - hnsEndpoint, err := hns.ProvisionEndpoint(epName, hnsNetwork.Id, args.ContainerID, args.Netns, func() (*hcsshim.HNSEndpoint, error) { epInfo, err := ProcessEndpointArgs(args, n) epInfo.NetworkId = hnsNetwork.Id @@ -130,7 +132,6 @@ func cmdHnsAdd(args *skel.CmdArgs, n *NetConf) (*current.Result, error) { } return result, nil - } func cmdHcnAdd(args *skel.CmdArgs, n *NetConf) (*current.Result, error) {