Skip to content

Commit

Permalink
Merge pull request containernetworking#215 from nagiesek/addHcnSupport
Browse files Browse the repository at this point in the history
Windows: Adds support for Host Compute Calls for Win-Bridge
  • Loading branch information
dcbw committed Feb 27, 2019
2 parents 1865a07 + 57b42a7 commit a95d48b
Show file tree
Hide file tree
Showing 848 changed files with 283,233 additions and 818 deletions.
4 changes: 2 additions & 2 deletions Godeps/Godeps.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

219 changes: 217 additions & 2 deletions pkg/hns/endpoint_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
"strings"

"github.com/Microsoft/hcsshim"
"github.com/Microsoft/hcsshim/hcn"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/current"
"github.com/juju/errors"
)
Expand All @@ -28,6 +30,16 @@ const (
pauseContainerNetNS = "none"
)

type EndpointInfo struct {
EndpointName string
DnsSearch []string
NetworkName string
NetworkId string
Gateway net.IP
IpAddress net.IP
Nameservers []string
}

// GetSandboxContainerID returns the sandbox ID of this pod
func GetSandboxContainerID(containerID string, netNs string) string {
if len(netNs) != 0 && netNs != pauseContainerNetNS {
Expand All @@ -40,6 +52,111 @@ func GetSandboxContainerID(containerID string, netNs string) string {
return containerID
}

// short function so we know when to return "" for a string
func GetIpString(ip *net.IP) string {
if len(*ip) == 0 {
return ""
} else {
return ip.String()
}
}

func GenerateHnsEndpoint(epInfo *EndpointInfo, n *NetConf) (*hcsshim.HNSEndpoint, error) {
// run the IPAM plugin and get back the config to apply
hnsEndpoint, err := hcsshim.GetHNSEndpointByName(epInfo.EndpointName)
if err != nil && !hcsshim.IsNotExist(err) {
return nil, errors.Annotatef(err, "Attempt to get endpoint \"%v\" failed", epInfo.EndpointName)
}

if hnsEndpoint != nil {
if hnsEndpoint.VirtualNetwork != epInfo.NetworkId {
_, err = hnsEndpoint.Delete()
if err != nil {
return nil, errors.Annotatef(err, "Failed to delete endpoint %v", epInfo.EndpointName)
}
hnsEndpoint = nil
}
}

if hnsEndpoint == nil {
hnsEndpoint = &hcsshim.HNSEndpoint{
Name: epInfo.EndpointName,
VirtualNetwork: epInfo.NetworkId,
DNSServerList: strings.Join(epInfo.Nameservers, ","),
DNSSuffix: strings.Join(epInfo.DnsSearch, ","),
GatewayAddress: GetIpString(&epInfo.Gateway),
IPAddress: epInfo.IpAddress,
Policies: n.MarshalPolicies(),
}
}
return hnsEndpoint, nil
}

func GenerateHcnEndpoint(epInfo *EndpointInfo, n *NetConf) (*hcn.HostComputeEndpoint, error) {
// run the IPAM plugin and get back the config to apply
hcnEndpoint, err := hcn.GetEndpointByName(epInfo.EndpointName)
if err != nil && (err != hcn.EndpointNotFoundError{EndpointName: epInfo.EndpointName}) {
return nil, errors.Annotatef(err, "Attempt to get endpoint \"%v\" failed", epInfo.EndpointName)
}

if hcnEndpoint != nil {
// If the endpont already exists, then we should return error unless
// the endpoint is based on a different network then delete
// should that fail return error
if hcnEndpoint.HostComputeNetwork != epInfo.NetworkId {
err = hcnEndpoint.Delete()
if err != nil {
return nil, errors.Annotatef(err, "Failed to delete endpoint %v", epInfo.EndpointName)
hcnEndpoint = nil

}
} else {
return nil, fmt.Errorf("Endpoint \"%v\" already exits", epInfo.EndpointName)
}
}

if hcnEndpoint == nil {
routes := []hcn.Route{
{
NextHop: GetIpString(&epInfo.Gateway),
DestinationPrefix: func() string {
destinationPrefix := "0.0.0.0/0"
if ipv6 := epInfo.Gateway.To4(); ipv6 == nil {
destinationPrefix = "::/0"
}
return destinationPrefix
}(),
},
}

hcnDns := hcn.Dns{
Search: epInfo.DnsSearch,
ServerList: epInfo.Nameservers,
}

hcnIpConfig := hcn.IpConfig{
IpAddress: GetIpString(&epInfo.IpAddress),
}
ipConfigs := []hcn.IpConfig{hcnIpConfig}

hcnEndpoint = &hcn.HostComputeEndpoint{
SchemaVersion: hcn.Version{Major: 2},
Name: epInfo.EndpointName,
HostComputeNetwork: epInfo.NetworkId,
Dns: hcnDns,
Routes: routes,
IpConfigurations: ipConfigs,
Policies: func() []hcn.EndpointPolicy {
if n.HcnPolicyArgs == nil {
n.HcnPolicyArgs = []hcn.EndpointPolicy{}
}
return n.HcnPolicyArgs
}(),
}
}
return hcnEndpoint, nil
}

// ConstructEndpointName constructs enpointId which is used to identify an endpoint from HNS
// There is a special consideration for netNs name here, which is required for Windows Server 1709
// containerID is the Id of the container on which the endpoint is worked on
Expand Down Expand Up @@ -80,7 +197,16 @@ type EndpointMakerFunc func() (*hcsshim.HNSEndpoint, error)
// ProvisionEndpoint provisions an endpoint to a container specified by containerID.
// If an endpoint already exists, the endpoint is reused.
// This call is idempotent
func ProvisionEndpoint(epName string, expectedNetworkId string, containerID string, makeEndpoint EndpointMakerFunc) (*hcsshim.HNSEndpoint, error) {
func ProvisionEndpoint(epName string, expectedNetworkId string, containerID string, netns string, makeEndpoint EndpointMakerFunc) (*hcsshim.HNSEndpoint, error) {
// On the second add call we expect that the endpoint already exists. If it
// does not then we should return an error.
if netns != pauseContainerNetNS {
_, err := hcsshim.GetHNSEndpointByName(epName)
if err != nil {
return nil, errors.Annotatef(err, "failed to find HNSEndpoint %s", epName)
}
}

// check if endpoint already exists
createEndpoint := true
hnsEndpoint, err := hcsshim.GetHNSEndpointByName(epName)
Expand All @@ -107,16 +233,47 @@ func ProvisionEndpoint(epName string, expectedNetworkId string, containerID stri

// hot attach
if err := hcsshim.HotAttachEndpoint(containerID, hnsEndpoint.Id); err != nil {
if createEndpoint {
err := DeprovisionEndpoint(epName, netns, containerID)
if err != nil {
return nil, errors.Annotatef(err, "failed to Deprovsion after HotAttach failure")
}
}
if hcsshim.ErrComputeSystemDoesNotExist == err {
return hnsEndpoint, nil
}

return nil, err
}

return hnsEndpoint, nil
}

type HcnEndpointMakerFunc func() (*hcn.HostComputeEndpoint, error)

func AddHcnEndpoint(epName string, expectedNetworkId string, namespace string,
makeEndpoint HcnEndpointMakerFunc) (*hcn.HostComputeEndpoint, error) {

hcnEndpoint, err := makeEndpoint()
if err != nil {
return nil, errors.Annotate(err, "failed to make a new HNSEndpoint")
}

if hcnEndpoint, err = hcnEndpoint.Create(); err != nil {
return nil, errors.Annotate(err, "failed to create the new HNSEndpoint")
}

err = hcn.AddNamespaceEndpoint(namespace, hcnEndpoint.Id)
if err != nil {
err := RemoveHcnEndpoint(epName)
if err != nil {
return nil, errors.Annotatef(err, "failed to Remove Endpoint after AddNamespaceEndpoint failure")
}
return nil, errors.Annotatef(err, "Failed to Add endpoint to namespace")
}
return hcnEndpoint, nil

}

// ConstructResult constructs the CNI result for the endpoint
func ConstructResult(hnsNetwork *hcsshim.HNSNetwork, hnsEndpoint *hcsshim.HNSEndpoint) (*current.Result, error) {
resultInterface := &current.Interface{
Expand Down Expand Up @@ -147,6 +304,64 @@ func ConstructResult(hnsNetwork *hcsshim.HNSNetwork, hnsEndpoint *hcsshim.HNSEnd
result := &current.Result{}
result.Interfaces = []*current.Interface{resultInterface}
result.IPs = []*current.IPConfig{resultIPConfig}
result.DNS = types.DNS{
Search: strings.Split(hnsEndpoint.DNSSuffix, ","),
Nameservers: strings.Split(hnsEndpoint.DNSServerList, ","),
}

return result, nil
}

// This version follows the v2 workflow of removing the endpoint from the namespace and deleting it
func RemoveHcnEndpoint(epName string) error {
hcnEndpoint, err := hcn.GetEndpointByName(epName)
if err != nil {
_ = fmt.Errorf("[win-cni] Failed to find endpoint %v, err:%v", epName, err)
return err
}
if hcnEndpoint != nil {
err = hcnEndpoint.Delete()
if err != nil {
return fmt.Errorf("[win-cni] Failed to delete endpoint %v, err:%v", epName, err)
}
}
return nil
}

func ConstructHcnResult(hcnNetwork *hcn.HostComputeNetwork, hcnEndpoint *hcn.HostComputeEndpoint) (*current.Result, error) {
resultInterface := &current.Interface{
Name: hcnEndpoint.Name,
Mac: hcnEndpoint.MacAddress,
}
_, ipSubnet, err := net.ParseCIDR(hcnNetwork.Ipams[0].Subnets[0].IpAddressPrefix)
if err != nil {
return nil, err
}

var ipVersion string
ipAddress := net.ParseIP(hcnEndpoint.IpConfigurations[0].IpAddress)
if ipv4 := ipAddress.To4(); ipv4 != nil {
ipVersion = "4"
} else if ipv6 := ipAddress.To16(); ipv6 != nil {
ipVersion = "6"
} else {
return nil, fmt.Errorf("[win-cni] The IPAddress of hnsEndpoint isn't a valid ipv4 or ipv6 Address.")
}

resultIPConfig := &current.IPConfig{
Version: ipVersion,
Address: net.IPNet{
IP: ipAddress,
Mask: ipSubnet.Mask},
Gateway: net.ParseIP(hcnEndpoint.Routes[0].NextHop),
}
result := &current.Result{}
result.Interfaces = []*current.Interface{resultInterface}
result.IPs = []*current.IPConfig{resultIPConfig}
result.DNS = types.DNS{
Search: hcnEndpoint.Dns.Search,
Nameservers: hcnEndpoint.Dns.ServerList,
}

return result, nil
}
13 changes: 13 additions & 0 deletions pkg/hns/hns_suite_windows_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package hns_test

import (
"testing"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

func TestHns(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Hns Suite")
}
File renamed without changes.
10 changes: 5 additions & 5 deletions pkg/hns/netconf.go → pkg/hns/netconf_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,19 @@
package hns

import (
"encoding/json"
"strings"

"bytes"
"encoding/json"
"github.com/Microsoft/hcsshim/hcn"
"github.com/buger/jsonparser"
"github.com/containernetworking/cni/pkg/types"
"strings"
)

// NetConf is the CNI spec
type NetConf struct {
types.NetConf

Policies []policy `json:"policies,omitempty"`
HcnPolicyArgs []hcn.EndpointPolicy `json:"HcnPolicyArgs,omitempty"`
Policies []policy `json:"policies,omitempty"`
}

type policy struct {
Expand Down
File renamed without changes.
Loading

0 comments on commit a95d48b

Please sign in to comment.