-
Notifications
You must be signed in to change notification settings - Fork 801
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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" ] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
then | ||
GOOS=windows . $d/build.sh | ||
fi | ||
else | ||
echo " $plugin" | ||
$GO build -o "${PWD}/bin/$plugin" "$@" "$REPO_PATH"/$d | ||
fi | ||
fi | ||
done |
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 := ¤t.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 := ¤t.IPConfig{ | ||
Version: ipVersion, | ||
Address: net.IPNet{ | ||
IP: hnsEndpoint.IPAddress, | ||
Mask: ipSubnet.Mask}, | ||
Gateway: net.ParseIP(hnsEndpoint.GatewayAddress), | ||
} | ||
result := ¤t.Result{} | ||
result.Interfaces = []*current.Interface{resultInterface} | ||
result.IPs = []*current.IPConfig{resultIPConfig} | ||
|
||
return result, nil | ||
} |
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 | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is
gcc
necessary?There was a problem hiding this comment.
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
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