2019-01-12 04:58:27 +00:00
package hcn
import (
"encoding/json"
2019-09-30 23:25:17 +00:00
"github.com/Microsoft/go-winio/pkg/guid"
2019-01-12 04:58:27 +00:00
"github.com/Microsoft/hcsshim/internal/interop"
"github.com/sirupsen/logrus"
)
// LoadBalancerPortMapping is associated with HostComputeLoadBalancer
type LoadBalancerPortMapping struct {
2020-08-10 17:43:49 +00:00
Protocol uint32 ` json:",omitempty" ` // EX: TCP = 6, UDP = 17
InternalPort uint16 ` json:",omitempty" `
ExternalPort uint16 ` json:",omitempty" `
DistributionType LoadBalancerDistribution ` json:",omitempty" ` // EX: Distribute per connection = 0, distribute traffic of the same protocol per client IP = 1, distribute per client IP = 2
Flags LoadBalancerPortMappingFlags ` json:",omitempty" `
2019-01-12 04:58:27 +00:00
}
// HostComputeLoadBalancer represents software load balancer.
type HostComputeLoadBalancer struct {
Id string ` json:"ID,omitempty" `
HostComputeEndpoints [ ] string ` json:",omitempty" `
SourceVIP string ` json:",omitempty" `
FrontendVIPs [ ] string ` json:",omitempty" `
PortMappings [ ] LoadBalancerPortMapping ` json:",omitempty" `
SchemaVersion SchemaVersion ` json:",omitempty" `
2019-07-10 00:29:38 +00:00
Flags LoadBalancerFlags ` json:",omitempty" ` // 0: None, 1: EnableDirectServerReturn
2019-01-12 04:58:27 +00:00
}
2019-07-10 00:29:38 +00:00
//LoadBalancerFlags modify settings for a loadbalancer.
type LoadBalancerFlags uint32
var (
// LoadBalancerFlagsNone is the default.
LoadBalancerFlagsNone LoadBalancerFlags = 0
// LoadBalancerFlagsDSR enables Direct Server Return (DSR)
LoadBalancerFlagsDSR LoadBalancerFlags = 1
2021-07-20 17:59:04 +00:00
LoadBalancerFlagsIPv6 LoadBalancerFlags = 2
2019-07-10 00:29:38 +00:00
)
// LoadBalancerPortMappingFlags are special settings on a loadbalancer.
type LoadBalancerPortMappingFlags uint32
var (
// LoadBalancerPortMappingFlagsNone is the default.
LoadBalancerPortMappingFlagsNone LoadBalancerPortMappingFlags
// LoadBalancerPortMappingFlagsILB enables internal loadbalancing.
LoadBalancerPortMappingFlagsILB LoadBalancerPortMappingFlags = 1
// LoadBalancerPortMappingFlagsLocalRoutedVIP enables VIP access from the host.
LoadBalancerPortMappingFlagsLocalRoutedVIP LoadBalancerPortMappingFlags = 2
// LoadBalancerPortMappingFlagsUseMux enables DSR for NodePort access of VIP.
LoadBalancerPortMappingFlagsUseMux LoadBalancerPortMappingFlags = 4
// LoadBalancerPortMappingFlagsPreserveDIP delivers packets with destination IP as the VIP.
LoadBalancerPortMappingFlagsPreserveDIP LoadBalancerPortMappingFlags = 8
)
2020-08-10 17:43:49 +00:00
// LoadBalancerDistribution specifies how the loadbalancer distributes traffic.
type LoadBalancerDistribution uint32
var (
// LoadBalancerDistributionNone is the default and loadbalances each connection to the same pod.
LoadBalancerDistributionNone LoadBalancerDistribution
// LoadBalancerDistributionSourceIPProtocol loadbalances all traffic of the same protocol from a client IP to the same pod.
LoadBalancerDistributionSourceIPProtocol LoadBalancerDistribution = 1
// LoadBalancerDistributionSourceIP loadbalances all traffic from a client IP to the same pod.
LoadBalancerDistributionSourceIP LoadBalancerDistribution = 2
)
2019-01-12 04:58:27 +00:00
func getLoadBalancer ( loadBalancerGuid guid . GUID , query string ) ( * HostComputeLoadBalancer , error ) {
// Open loadBalancer.
var (
loadBalancerHandle hcnLoadBalancer
resultBuffer * uint16
propertiesBuffer * uint16
)
hr := hcnOpenLoadBalancer ( & loadBalancerGuid , & loadBalancerHandle , & resultBuffer )
if err := checkForErrors ( "hcnOpenLoadBalancer" , hr , resultBuffer ) ; err != nil {
return nil , err
}
// Query loadBalancer.
hr = hcnQueryLoadBalancerProperties ( loadBalancerHandle , query , & propertiesBuffer , & resultBuffer )
if err := checkForErrors ( "hcnQueryLoadBalancerProperties" , hr , resultBuffer ) ; err != nil {
return nil , err
}
properties := interop . ConvertAndFreeCoTaskMemString ( propertiesBuffer )
// Close loadBalancer.
hr = hcnCloseLoadBalancer ( loadBalancerHandle )
if err := checkForErrors ( "hcnCloseLoadBalancer" , hr , nil ) ; err != nil {
return nil , err
}
// Convert output to HostComputeLoadBalancer
var outputLoadBalancer HostComputeLoadBalancer
if err := json . Unmarshal ( [ ] byte ( properties ) , & outputLoadBalancer ) ; err != nil {
return nil , err
}
return & outputLoadBalancer , nil
}
func enumerateLoadBalancers ( query string ) ( [ ] HostComputeLoadBalancer , error ) {
// Enumerate all LoadBalancer Guids
var (
resultBuffer * uint16
loadBalancerBuffer * uint16
)
hr := hcnEnumerateLoadBalancers ( query , & loadBalancerBuffer , & resultBuffer )
if err := checkForErrors ( "hcnEnumerateLoadBalancers" , hr , resultBuffer ) ; err != nil {
return nil , err
}
loadBalancers := interop . ConvertAndFreeCoTaskMemString ( loadBalancerBuffer )
var loadBalancerIds [ ] guid . GUID
if err := json . Unmarshal ( [ ] byte ( loadBalancers ) , & loadBalancerIds ) ; err != nil {
return nil , err
}
var outputLoadBalancers [ ] HostComputeLoadBalancer
for _ , loadBalancerGuid := range loadBalancerIds {
loadBalancer , err := getLoadBalancer ( loadBalancerGuid , query )
if err != nil {
return nil , err
}
outputLoadBalancers = append ( outputLoadBalancers , * loadBalancer )
}
return outputLoadBalancers , nil
}
func createLoadBalancer ( settings string ) ( * HostComputeLoadBalancer , error ) {
// Create new loadBalancer.
var (
loadBalancerHandle hcnLoadBalancer
resultBuffer * uint16
propertiesBuffer * uint16
)
loadBalancerGuid := guid . GUID { }
hr := hcnCreateLoadBalancer ( & loadBalancerGuid , settings , & loadBalancerHandle , & resultBuffer )
if err := checkForErrors ( "hcnCreateLoadBalancer" , hr , resultBuffer ) ; err != nil {
return nil , err
}
// Query loadBalancer.
hcnQuery := defaultQuery ( )
query , err := json . Marshal ( hcnQuery )
if err != nil {
return nil , err
}
hr = hcnQueryLoadBalancerProperties ( loadBalancerHandle , string ( query ) , & propertiesBuffer , & resultBuffer )
if err := checkForErrors ( "hcnQueryLoadBalancerProperties" , hr , resultBuffer ) ; err != nil {
return nil , err
}
properties := interop . ConvertAndFreeCoTaskMemString ( propertiesBuffer )
// Close loadBalancer.
hr = hcnCloseLoadBalancer ( loadBalancerHandle )
if err := checkForErrors ( "hcnCloseLoadBalancer" , hr , nil ) ; err != nil {
return nil , err
}
// Convert output to HostComputeLoadBalancer
var outputLoadBalancer HostComputeLoadBalancer
if err := json . Unmarshal ( [ ] byte ( properties ) , & outputLoadBalancer ) ; err != nil {
return nil , err
}
return & outputLoadBalancer , nil
}
func deleteLoadBalancer ( loadBalancerId string ) error {
2019-09-30 23:25:17 +00:00
loadBalancerGuid , err := guid . FromString ( loadBalancerId )
if err != nil {
return errInvalidLoadBalancerID
}
2019-01-12 04:58:27 +00:00
var resultBuffer * uint16
hr := hcnDeleteLoadBalancer ( & loadBalancerGuid , & resultBuffer )
if err := checkForErrors ( "hcnDeleteLoadBalancer" , hr , resultBuffer ) ; err != nil {
return err
}
return nil
}
// ListLoadBalancers makes a call to list all available loadBalancers.
func ListLoadBalancers ( ) ( [ ] HostComputeLoadBalancer , error ) {
hcnQuery := defaultQuery ( )
loadBalancers , err := ListLoadBalancersQuery ( hcnQuery )
if err != nil {
return nil , err
}
return loadBalancers , nil
}
// ListLoadBalancersQuery makes a call to query the list of available loadBalancers.
func ListLoadBalancersQuery ( query HostComputeQuery ) ( [ ] HostComputeLoadBalancer , error ) {
queryJson , err := json . Marshal ( query )
if err != nil {
return nil , err
}
loadBalancers , err := enumerateLoadBalancers ( string ( queryJson ) )
if err != nil {
return nil , err
}
return loadBalancers , nil
}
// GetLoadBalancerByID returns the LoadBalancer specified by Id.
func GetLoadBalancerByID ( loadBalancerId string ) ( * HostComputeLoadBalancer , error ) {
hcnQuery := defaultQuery ( )
mapA := map [ string ] string { "ID" : loadBalancerId }
filter , err := json . Marshal ( mapA )
if err != nil {
return nil , err
}
hcnQuery . Filter = string ( filter )
loadBalancers , err := ListLoadBalancersQuery ( hcnQuery )
if err != nil {
return nil , err
}
if len ( loadBalancers ) == 0 {
return nil , LoadBalancerNotFoundError { LoadBalancerId : loadBalancerId }
}
return & loadBalancers [ 0 ] , err
}
// Create LoadBalancer.
func ( loadBalancer * HostComputeLoadBalancer ) Create ( ) ( * HostComputeLoadBalancer , error ) {
logrus . Debugf ( "hcn::HostComputeLoadBalancer::Create id=%s" , loadBalancer . Id )
jsonString , err := json . Marshal ( loadBalancer )
if err != nil {
return nil , err
}
logrus . Debugf ( "hcn::HostComputeLoadBalancer::Create JSON: %s" , jsonString )
loadBalancer , hcnErr := createLoadBalancer ( string ( jsonString ) )
if hcnErr != nil {
return nil , hcnErr
}
return loadBalancer , nil
}
// Delete LoadBalancer.
2019-07-10 00:29:38 +00:00
func ( loadBalancer * HostComputeLoadBalancer ) Delete ( ) error {
2019-01-12 04:58:27 +00:00
logrus . Debugf ( "hcn::HostComputeLoadBalancer::Delete id=%s" , loadBalancer . Id )
if err := deleteLoadBalancer ( loadBalancer . Id ) ; err != nil {
2019-07-10 00:29:38 +00:00
return err
2019-01-12 04:58:27 +00:00
}
2019-07-10 00:29:38 +00:00
return nil
2019-01-12 04:58:27 +00:00
}
// AddEndpoint add an endpoint to a LoadBalancer
func ( loadBalancer * HostComputeLoadBalancer ) AddEndpoint ( endpoint * HostComputeEndpoint ) ( * HostComputeLoadBalancer , error ) {
logrus . Debugf ( "hcn::HostComputeLoadBalancer::AddEndpoint loadBalancer=%s endpoint=%s" , loadBalancer . Id , endpoint . Id )
2019-07-10 00:29:38 +00:00
err := loadBalancer . Delete ( )
2019-01-12 04:58:27 +00:00
if err != nil {
return nil , err
}
// Add Endpoint to the Existing List
loadBalancer . HostComputeEndpoints = append ( loadBalancer . HostComputeEndpoints , endpoint . Id )
return loadBalancer . Create ( )
}
// RemoveEndpoint removes an endpoint from a LoadBalancer
func ( loadBalancer * HostComputeLoadBalancer ) RemoveEndpoint ( endpoint * HostComputeEndpoint ) ( * HostComputeLoadBalancer , error ) {
logrus . Debugf ( "hcn::HostComputeLoadBalancer::RemoveEndpoint loadBalancer=%s endpoint=%s" , loadBalancer . Id , endpoint . Id )
2019-07-10 00:29:38 +00:00
err := loadBalancer . Delete ( )
2019-01-12 04:58:27 +00:00
if err != nil {
return nil , err
}
// Create a list of all the endpoints besides the one being removed
var endpoints [ ] string
for _ , endpointReference := range loadBalancer . HostComputeEndpoints {
if endpointReference == endpoint . Id {
continue
}
endpoints = append ( endpoints , endpointReference )
}
loadBalancer . HostComputeEndpoints = endpoints
return loadBalancer . Create ( )
}
// AddLoadBalancer for the specified endpoints
2019-07-10 00:29:38 +00:00
func AddLoadBalancer ( endpoints [ ] HostComputeEndpoint , flags LoadBalancerFlags , portMappingFlags LoadBalancerPortMappingFlags , sourceVIP string , frontendVIPs [ ] string , protocol uint16 , internalPort uint16 , externalPort uint16 ) ( * HostComputeLoadBalancer , error ) {
logrus . Debugf ( "hcn::HostComputeLoadBalancer::AddLoadBalancer endpointId=%v, LoadBalancerFlags=%v, LoadBalancerPortMappingFlags=%v, sourceVIP=%s, frontendVIPs=%v, protocol=%v, internalPort=%v, externalPort=%v" , endpoints , flags , portMappingFlags , sourceVIP , frontendVIPs , protocol , internalPort , externalPort )
2019-01-12 04:58:27 +00:00
loadBalancer := & HostComputeLoadBalancer {
SourceVIP : sourceVIP ,
PortMappings : [ ] LoadBalancerPortMapping {
{
Protocol : uint32 ( protocol ) ,
InternalPort : internalPort ,
ExternalPort : externalPort ,
Flags : portMappingFlags ,
} ,
} ,
FrontendVIPs : frontendVIPs ,
SchemaVersion : SchemaVersion {
Major : 2 ,
Minor : 0 ,
} ,
2019-07-10 00:29:38 +00:00
Flags : flags ,
2019-01-12 04:58:27 +00:00
}
for _ , endpoint := range endpoints {
loadBalancer . HostComputeEndpoints = append ( loadBalancer . HostComputeEndpoints , endpoint . Id )
}
return loadBalancer . Create ( )
}