Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Windows CNI for overlay (vxlan) and host-gw (l2bridge) modes #85

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
4 changes: 2 additions & 2 deletions .appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ environment:
install:
- echo %PATH%
- echo %GOPATH%
- set PATH=%GOPATH%\bin;c:\go\bin;%PATH%
- go version
- go env

- ps: $webClient = New-Object System.Net.WebClient; $InstallPath="c:" ; $webClient.DownloadFile("https://raw.githubusercontent.com/jhowardmsft/docker-tdmgcc/master/gcc.zip", "$InstallPath\gcc.zip"); Expand-Archive $InstallPath\gcc.zip -DestinationPath $InstallPath\gcc -Force; $webClient.DownloadFile("https://raw.githubusercontent.com/jhowardmsft/docker-tdmgcc/master/runtime.zip", "$InstallPath\runtime.zip"); Expand-Archive $InstallPath\runtime.zip -DestinationPath $InstallPath\gcc -Force; $webClient.DownloadFile("https://raw.githubusercontent.com/jhowardmsft/docker-tdmgcc/master/binutils.zip","$InstallPath\binutils.zip"); Expand-Archive $InstallPath\binutils.zip -DestinationPath $InstallPath\gcc -Force;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is gcc necessary?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dependency of hcsshim

go : # github.com/containernetworking/plugins/vendor/github.com/Microsoft/hcsshim
87
At line:7 char:5
88

  • go test -v $pkg
    

89

  • ~~~~~~~~~~~~~~~
    

90
+ CategoryInfo : NotSpecified: (# github.com/co...crosoft/hcsshim:String) [], RemoteException
91
+ FullyQualifiedErrorId : NativeCommandError
92

93
exec: "gcc": executable file not found in %PATH%
94
95
FAIL github.com/containernetworking/plugins/pkg/hns [build failed]
96
test failed

- set PATH=%GOPATH%\bin;c:\go\bin;c:\gcc\bin;%PATH%
build: off

test_script:
Expand Down
26 changes: 25 additions & 1 deletion Godeps/Godeps.json

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

4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ Some CNI network plugins, maintained by the containernetworking team. For more i
* `macvlan`: Creates a new MAC address, forwards all traffic to that to the container
* `ptp`: Creates a veth pair.
* `vlan`: Allocates a vlan device.

#### Windows: windows specific
* `l2bridge`: Creates a bridge, adds the host and the container to it.
* `overlay`: Creates an overlay interface to the container
### IPAM: IP address allocation
* `dhcp`: Runs a daemon on the host to make DHCP requests on behalf of the container
* `host-local`: maintains a local database of allocated IPs
Expand Down
14 changes: 11 additions & 3 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,20 @@ export GO="${GO:-go}"

mkdir -p "${PWD}/bin"

echo "Building plugins"
echo "Building plugins ${GOOS}"
PLUGINS="plugins/meta/* plugins/main/* plugins/ipam/* plugins/sample"
for d in $PLUGINS; do
if [ -d "$d" ]; then
plugin="$(basename "$d")"
echo " $plugin"
$GO build -o "${PWD}/bin/$plugin" "$@" "$REPO_PATH"/$d
if [ $plugin == "windows" ]
then
if [ "$GOARCH" == "amd64" ]
Copy link

@benmoss benmoss Jul 25, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GOARCH isn't always going to be in the environment, you should use go env GOARCH to get the current value in case its falling back to system defaults

then
GOOS=windows . $d/build.sh
fi
else
echo " $plugin"
$GO build -o "${PWD}/bin/$plugin" "$@" "$REPO_PATH"/$d
fi
fi
done
149 changes: 149 additions & 0 deletions pkg/hns/endpoint_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
// +build windows

// Copyright 2017 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package hns

import (
"fmt"
"log"
"net"
"strings"

"github.com/Microsoft/hcsshim"
"github.com/containernetworking/cni/pkg/types/current"
)

// 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
func ConstructEndpointName(containerID string, netNs string, networkName string) string {
if netNs != "" {
splits := strings.Split(netNs, ":")
if len(splits) == 2 {
containerID = splits[1]
}
}
epName := containerID + "_" + networkName
return epName
}

// DeprovisionEndpoint removes an endpoint from the container by sending a Detach request to HNS
// For shared endpoint, ContainerDetach is used
// for removing the endpoint completely, HotDetachEndpoint is used
func DeprovisionEndpoint(epName string, netns string, containerID string) error {
hnsEndpoint, err := hcsshim.GetHNSEndpointByName(epName)
if err != nil {
log.Printf("[win-cni] Failed to find endpoint %v, err:%v", epName, err)
return err
}

if netns != "none" {
// Shared endpoint removal. Do not remove the endpoint.
err := hnsEndpoint.ContainerDetach(containerID)
if err != nil {
log.Printf("[win-cni] Failed to detach the container endpoint %v, err:%v", epName, err)
}
return nil
}

err = hcsshim.HotDetachEndpoint(containerID, hnsEndpoint.Id)
if err != nil {
log.Printf("[win-cni] Failed to detach endpoint %v, err:%v", epName, err)
// Do not consider this as failure, else this would leak endpoints
}

_, err = hnsEndpoint.Delete()
if err != nil {
log.Printf("[win-cni] Failed to delete endpoint %v, err:%v", epName, err)
// Do not return error
}

return nil
}

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) {
// check if endpoint already exists
createEndpoint := true
hnsEndpoint, err := hcsshim.GetHNSEndpointByName(epName)
if hnsEndpoint != nil && hnsEndpoint.VirtualNetwork == expectedNetworkId {
log.Printf("[win-cni] Found existing endpoint %v", epName)
createEndpoint = false
}

if createEndpoint {
if hnsEndpoint != nil {
_, err = hnsEndpoint.Delete()
if err != nil {
log.Printf("[win-cni] Failed to delete stale endpoint %v, err:%v", epName, err)
}
}

if hnsEndpoint, err = makeEndpoint(); err != nil {
return nil, err
}

if hnsEndpoint, err = hnsEndpoint.Create(); err != nil {
return nil, err
}

}

// hot attach
if err = hcsshim.HotAttachEndpoint(containerID, hnsEndpoint.Id); err != nil {
return nil, err
}

return hnsEndpoint, nil
}

// ConstructResult constructs the CNI result for the endpoint
func ConstructResult(hnsNetwork *hcsshim.HNSNetwork, hnsEndpoint *hcsshim.HNSEndpoint) (*current.Result, error) {
resultInterface := &current.Interface{
Name: hnsEndpoint.Name,
Mac: hnsEndpoint.MacAddress,
}
_, ipSubnet, err := net.ParseCIDR(hnsNetwork.Subnets[0].AddressPrefix)
if err != nil {
return nil, err
}

var ipVersion string
if ipv4 := hnsEndpoint.IPAddress.To4(); ipv4 != nil {
ipVersion = "4"
} else if ipv6 := hnsEndpoint.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: hnsEndpoint.IPAddress,
Mask: ipSubnet.Mask},
Gateway: net.ParseIP(hnsEndpoint.GatewayAddress),
}
result := &current.Result{}
result.Interfaces = []*current.Interface{resultInterface}
result.IPs = []*current.IPConfig{resultIPConfig}

return result, nil
}
142 changes: 142 additions & 0 deletions pkg/hns/netconf.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// Copyright 2017 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package hns

import (
"encoding/json"
"github.com/containernetworking/cni/pkg/types"
"strings"
)

// NetConf is the CNI spec
type NetConf struct {
types.NetConf
AdditionalArgs []policyArgument `json:"AdditionalArgs,omitempty"`
}

type policyArgument struct {
Name string
Value map[string]interface{}
}

// MarshalPolicies converts the Endpoint policies in AdditionalArgs
// to HNS specific policies as Json raw bytes
func (n *NetConf) MarshalPolicies() []json.RawMessage {
if n.AdditionalArgs == nil {
n.AdditionalArgs = []policyArgument{}
}

var result []json.RawMessage
for _, policyArg := range n.AdditionalArgs {
if !strings.EqualFold(policyArg.Name, "EndpointPolicy") {
continue
}
if data, err := json.Marshal(policyArg.Value); err == nil {
result = append(result, data)
}
}

return result
}

// ApplyOutboundNatPolicy applies NAT Policy in VFP using HNS
// Simultaneously an exception is added for the network that has to be Nat'd
func (n *NetConf) ApplyOutboundNatPolicy(nwToNat string) {
if n.AdditionalArgs == nil {
n.AdditionalArgs = []policyArgument{}
}

for _, policy := range n.AdditionalArgs {
if !strings.EqualFold(policy.Name, "EndpointPolicy") {
continue
}

pv := policy.Value
if !hasKey(pv, "Type") {
continue
}

if !strings.EqualFold(pv["Type"].(string), "OutBoundNAT") {
continue
}

if !hasKey(pv, "ExceptionList") {
// add the exception since there weren't any
pv["ExceptionList"] = []interface{}{nwToNat}
return
}

nets := pv["ExceptionList"].([]interface{})
for _, net := range nets {
if net.(string) == nwToNat {
// found it - do nothing
return
}
}

// its not in the list of exceptions, add it and we're done
pv["ExceptionList"] = append(nets, nwToNat)
return
}

// didn't find the policy, add it
natEntry := policyArgument{
Name: "EndpointPolicy",
Value: map[string]interface{}{
"Type": "OutBoundNAT",
"ExceptionList": []interface{}{
nwToNat,
},
},
}

n.AdditionalArgs = append(n.AdditionalArgs, natEntry)
}

// ApplyDefaultPAPolicy is used to configure a endpoint PA policy in HNS
func (n *NetConf) ApplyDefaultPAPolicy(paAddress string) {
if n.AdditionalArgs == nil {
n.AdditionalArgs = []policyArgument{}
}

// if its already present, leave untouched
for _, policy := range n.AdditionalArgs {
if policy.Name == "EndpointPolicy" {
if hasKey(policy.Value, "PA") {
// found it, don't override
return
}
}
}

// did not find, add it now
paPolicyData := map[string]interface{}{
"Type": "PA",
"PA": paAddress,
}
paPolicy := &policyArgument{
Name: "EndpointPolicy",
Value: paPolicyData,
}

n.AdditionalArgs = append(n.AdditionalArgs, *paPolicy)

return
}

func hasKey(m map[string]interface{}, k string) bool {
_, ok := m[k]
return ok
}
Loading