
This change adds 2 options for windows: --forward-healthcheck-vip: If true forward service VIP for health check port --root-hnsendpoint-name: The name of the hns endpoint name for root namespace attached to l2bridge, default is cbr0 When --forward-healthcheck-vip is set as true and winkernel is used, kube-proxy will add an hns load balancer to forward health check request that was sent to lb_vip:healthcheck_port to the node_ip:healthcheck_port. Without this forwarding, the health check from google load balancer will fail, and it will stop forwarding traffic to the windows node. This change fixes the following 2 cases for service: - `externalTrafficPolicy: Cluster` (default option): healthcheck_port is 10256 for all services. Without this fix, all traffic won't be directly forwarded to windows node. It will always go through a linux node and get forwarded to windows from there. - `externalTrafficPolicy: Local`: different healthcheck_port for each service that is configured as local. Without this fix, this feature won't work on windows node at all. This feature preserves client ip that tries to connect to their application running in windows pod. Change-Id: If4513e72900101ef70d86b91155e56a1f8c79719
312 lines
8.7 KiB
Go
312 lines
8.7 KiB
Go
//go:build windows
|
|
// +build windows
|
|
|
|
/*
|
|
Copyright 2018 The Kubernetes 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 winkernel
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
"github.com/Microsoft/hcsshim/hcn"
|
|
"k8s.io/klog/v2"
|
|
|
|
"strings"
|
|
)
|
|
|
|
type hnsV2 struct{}
|
|
|
|
var (
|
|
// LoadBalancerFlagsIPv6 enables IPV6.
|
|
LoadBalancerFlagsIPv6 hcn.LoadBalancerFlags = 2
|
|
)
|
|
|
|
func (hns hnsV2) getNetworkByName(name string) (*hnsNetworkInfo, error) {
|
|
hnsnetwork, err := hcn.GetNetworkByName(name)
|
|
if err != nil {
|
|
klog.ErrorS(err, "Error getting network by name")
|
|
return nil, err
|
|
}
|
|
|
|
var remoteSubnets []*remoteSubnetInfo
|
|
for _, policy := range hnsnetwork.Policies {
|
|
if policy.Type == hcn.RemoteSubnetRoute {
|
|
policySettings := hcn.RemoteSubnetRoutePolicySetting{}
|
|
err = json.Unmarshal(policy.Settings, &policySettings)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal Remote Subnet policy settings")
|
|
}
|
|
rs := &remoteSubnetInfo{
|
|
destinationPrefix: policySettings.DestinationPrefix,
|
|
isolationID: policySettings.IsolationId,
|
|
providerAddress: policySettings.ProviderAddress,
|
|
drMacAddress: policySettings.DistributedRouterMacAddress,
|
|
}
|
|
remoteSubnets = append(remoteSubnets, rs)
|
|
}
|
|
}
|
|
|
|
return &hnsNetworkInfo{
|
|
id: hnsnetwork.Id,
|
|
name: hnsnetwork.Name,
|
|
networkType: string(hnsnetwork.Type),
|
|
remoteSubnets: remoteSubnets,
|
|
}, nil
|
|
}
|
|
func (hns hnsV2) getEndpointByID(id string) (*endpointsInfo, error) {
|
|
hnsendpoint, err := hcn.GetEndpointByID(id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &endpointsInfo{ //TODO: fill out PA
|
|
ip: hnsendpoint.IpConfigurations[0].IpAddress,
|
|
isLocal: uint32(hnsendpoint.Flags&hcn.EndpointFlagsRemoteEndpoint) == 0, //TODO: Change isLocal to isRemote
|
|
macAddress: hnsendpoint.MacAddress,
|
|
hnsID: hnsendpoint.Id,
|
|
hns: hns,
|
|
}, nil
|
|
}
|
|
func (hns hnsV2) getEndpointByIpAddress(ip string, networkName string) (*endpointsInfo, error) {
|
|
hnsnetwork, err := hcn.GetNetworkByName(networkName)
|
|
if err != nil {
|
|
klog.ErrorS(err, "Error getting network by name")
|
|
return nil, err
|
|
}
|
|
|
|
endpoints, err := hcn.ListEndpoints()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to list endpoints: %w", err)
|
|
}
|
|
for _, endpoint := range endpoints {
|
|
equal := false
|
|
if endpoint.IpConfigurations != nil && len(endpoint.IpConfigurations) > 0 {
|
|
equal = endpoint.IpConfigurations[0].IpAddress == ip
|
|
|
|
if !equal && len(endpoint.IpConfigurations) > 1 {
|
|
equal = endpoint.IpConfigurations[1].IpAddress == ip
|
|
}
|
|
}
|
|
if equal && strings.EqualFold(endpoint.HostComputeNetwork, hnsnetwork.Id) {
|
|
return &endpointsInfo{
|
|
ip: ip,
|
|
isLocal: uint32(endpoint.Flags&hcn.EndpointFlagsRemoteEndpoint) == 0, //TODO: Change isLocal to isRemote
|
|
macAddress: endpoint.MacAddress,
|
|
hnsID: endpoint.Id,
|
|
hns: hns,
|
|
}, nil
|
|
}
|
|
}
|
|
|
|
return nil, fmt.Errorf("Endpoint %v not found on network %s", ip, networkName)
|
|
}
|
|
func (hns hnsV2) getEndpointByName(name string) (*endpointsInfo, error) {
|
|
hnsendpoint, err := hcn.GetEndpointByName(name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &endpointsInfo{ //TODO: fill out PA
|
|
ip: hnsendpoint.IpConfigurations[0].IpAddress,
|
|
isLocal: uint32(hnsendpoint.Flags&hcn.EndpointFlagsRemoteEndpoint) == 0, //TODO: Change isLocal to isRemote
|
|
macAddress: hnsendpoint.MacAddress,
|
|
hnsID: hnsendpoint.Id,
|
|
hns: hns,
|
|
}, nil
|
|
}
|
|
func (hns hnsV2) createEndpoint(ep *endpointsInfo, networkName string) (*endpointsInfo, error) {
|
|
hnsNetwork, err := hcn.GetNetworkByName(networkName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var flags hcn.EndpointFlags
|
|
if !ep.isLocal {
|
|
flags |= hcn.EndpointFlagsRemoteEndpoint
|
|
}
|
|
ipConfig := &hcn.IpConfig{
|
|
IpAddress: ep.ip,
|
|
}
|
|
hnsEndpoint := &hcn.HostComputeEndpoint{
|
|
IpConfigurations: []hcn.IpConfig{*ipConfig},
|
|
MacAddress: ep.macAddress,
|
|
Flags: flags,
|
|
SchemaVersion: hcn.SchemaVersion{
|
|
Major: 2,
|
|
Minor: 0,
|
|
},
|
|
}
|
|
|
|
var createdEndpoint *hcn.HostComputeEndpoint
|
|
if !ep.isLocal {
|
|
if len(ep.providerAddress) != 0 {
|
|
policySettings := hcn.ProviderAddressEndpointPolicySetting{
|
|
ProviderAddress: ep.providerAddress,
|
|
}
|
|
policySettingsJson, err := json.Marshal(policySettings)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("PA Policy creation failed: %v", err)
|
|
}
|
|
paPolicy := hcn.EndpointPolicy{
|
|
Type: hcn.NetworkProviderAddress,
|
|
Settings: policySettingsJson,
|
|
}
|
|
hnsEndpoint.Policies = append(hnsEndpoint.Policies, paPolicy)
|
|
}
|
|
createdEndpoint, err = hnsNetwork.CreateRemoteEndpoint(hnsEndpoint)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
createdEndpoint, err = hnsNetwork.CreateEndpoint(hnsEndpoint)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return &endpointsInfo{
|
|
ip: createdEndpoint.IpConfigurations[0].IpAddress,
|
|
isLocal: uint32(createdEndpoint.Flags&hcn.EndpointFlagsRemoteEndpoint) == 0,
|
|
macAddress: createdEndpoint.MacAddress,
|
|
hnsID: createdEndpoint.Id,
|
|
providerAddress: ep.providerAddress, //TODO get from createdEndpoint
|
|
hns: hns,
|
|
}, nil
|
|
}
|
|
func (hns hnsV2) deleteEndpoint(hnsID string) error {
|
|
hnsendpoint, err := hcn.GetEndpointByID(hnsID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = hnsendpoint.Delete()
|
|
if err == nil {
|
|
klog.V(3).InfoS("Remote endpoint resource deleted", "hnsID", hnsID)
|
|
}
|
|
return err
|
|
}
|
|
func (hns hnsV2) getLoadBalancer(endpoints []endpointsInfo, flags loadBalancerFlags, sourceVip string, vip string, protocol uint16, internalPort uint16, externalPort uint16) (*loadBalancerInfo, error) {
|
|
plists, err := hcn.ListLoadBalancers()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, plist := range plists {
|
|
if len(plist.HostComputeEndpoints) != len(endpoints) {
|
|
continue
|
|
}
|
|
// Validate if input meets any of the policy lists
|
|
lbPortMapping := plist.PortMappings[0]
|
|
if lbPortMapping.Protocol == uint32(protocol) && lbPortMapping.InternalPort == internalPort && lbPortMapping.ExternalPort == externalPort && (lbPortMapping.Flags&1 != 0) == flags.isILB {
|
|
if len(vip) > 0 {
|
|
if len(plist.FrontendVIPs) == 0 || plist.FrontendVIPs[0] != vip {
|
|
continue
|
|
}
|
|
} else if len(plist.FrontendVIPs) != 0 {
|
|
continue
|
|
}
|
|
klog.V(1).InfoS("Found existing Hns loadbalancer policy resource", "policies", plist)
|
|
return &loadBalancerInfo{
|
|
hnsID: plist.Id,
|
|
}, nil
|
|
}
|
|
}
|
|
|
|
var hnsEndpoints []hcn.HostComputeEndpoint
|
|
for _, ep := range endpoints {
|
|
endpoint, err := hcn.GetEndpointByID(ep.hnsID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
hnsEndpoints = append(hnsEndpoints, *endpoint)
|
|
}
|
|
|
|
vips := []string{}
|
|
if len(vip) > 0 {
|
|
vips = append(vips, vip)
|
|
}
|
|
|
|
lbPortMappingFlags := hcn.LoadBalancerPortMappingFlagsNone
|
|
if flags.isILB {
|
|
lbPortMappingFlags |= hcn.LoadBalancerPortMappingFlagsILB
|
|
}
|
|
if flags.useMUX {
|
|
lbPortMappingFlags |= hcn.LoadBalancerPortMappingFlagsUseMux
|
|
}
|
|
if flags.preserveDIP {
|
|
lbPortMappingFlags |= hcn.LoadBalancerPortMappingFlagsPreserveDIP
|
|
}
|
|
if flags.localRoutedVIP {
|
|
lbPortMappingFlags |= hcn.LoadBalancerPortMappingFlagsLocalRoutedVIP
|
|
}
|
|
|
|
lbFlags := hcn.LoadBalancerFlagsNone
|
|
if flags.isDSR {
|
|
lbFlags |= hcn.LoadBalancerFlagsDSR
|
|
}
|
|
|
|
if flags.isIPv6 {
|
|
lbFlags |= LoadBalancerFlagsIPv6
|
|
}
|
|
|
|
lbDistributionType := hcn.LoadBalancerDistributionNone
|
|
|
|
if flags.sessionAffinity {
|
|
lbDistributionType = hcn.LoadBalancerDistributionSourceIP
|
|
}
|
|
|
|
loadBalancer := &hcn.HostComputeLoadBalancer{
|
|
SourceVIP: sourceVip,
|
|
PortMappings: []hcn.LoadBalancerPortMapping{
|
|
{
|
|
Protocol: uint32(protocol),
|
|
InternalPort: internalPort,
|
|
ExternalPort: externalPort,
|
|
DistributionType: lbDistributionType,
|
|
Flags: lbPortMappingFlags,
|
|
},
|
|
},
|
|
FrontendVIPs: vips,
|
|
SchemaVersion: hcn.SchemaVersion{
|
|
Major: 2,
|
|
Minor: 0,
|
|
},
|
|
Flags: lbFlags,
|
|
}
|
|
|
|
for _, endpoint := range hnsEndpoints {
|
|
loadBalancer.HostComputeEndpoints = append(loadBalancer.HostComputeEndpoints, endpoint.Id)
|
|
}
|
|
|
|
lb, err := loadBalancer.Create()
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
klog.V(1).InfoS("Hns loadbalancer policy resource", "loadBalancer", lb)
|
|
return &loadBalancerInfo{
|
|
hnsID: lb.Id,
|
|
}, err
|
|
}
|
|
func (hns hnsV2) deleteLoadBalancer(hnsID string) error {
|
|
lb, err := hcn.GetLoadBalancerByID(hnsID)
|
|
if err != nil {
|
|
// Return silently
|
|
return nil
|
|
}
|
|
|
|
err = lb.Delete()
|
|
return err
|
|
}
|