Skip to content

Commit

Permalink
Add GetCachedSupportedFeatures method to hcn package
Browse files Browse the repository at this point in the history
To avoid a breaking change on GetSupportedFeatures introduce a new
GetCachedSupportedFeatures method. This method does the feature check
and version parsing once and then assigns a global with the information.
This can be used to optimize for situations where many uses of the
hcn.IsXSupported methods are going to be used (kube-proxy for example).

Signed-off-by: Daniel Canter <dcanter@microsoft.com>
  • Loading branch information
dcantah committed Aug 19, 2021
1 parent 2b5a08d commit 112b5e7
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 28 deletions.
76 changes: 60 additions & 16 deletions hcn/hcn.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,10 @@ func platformDoesNotSupportError(featureName string) error {

// V2ApiSupported returns an error if the HCN version does not support the V2 Apis.
func V2ApiSupported() error {
supported := GetSupportedFeatures()
supported, err := GetCachedSupportedFeatures()
if err != nil {
return err
}
if supported.Api.V2 {
return nil
}
Expand All @@ -143,7 +146,10 @@ func V2SchemaVersion() SchemaVersion {

// RemoteSubnetSupported returns an error if the HCN version does not support Remote Subnet policies.
func RemoteSubnetSupported() error {
supported := GetSupportedFeatures()
supported, err := GetCachedSupportedFeatures()
if err != nil {
return err
}
if supported.RemoteSubnet {
return nil
}
Expand All @@ -152,7 +158,10 @@ func RemoteSubnetSupported() error {

// HostRouteSupported returns an error if the HCN version does not support Host Route policies.
func HostRouteSupported() error {
supported := GetSupportedFeatures()
supported, err := GetCachedSupportedFeatures()
if err != nil {
return err
}
if supported.HostRoute {
return nil
}
Expand All @@ -161,7 +170,10 @@ func HostRouteSupported() error {

// DSRSupported returns an error if the HCN version does not support Direct Server Return.
func DSRSupported() error {
supported := GetSupportedFeatures()
supported, err := GetCachedSupportedFeatures()
if err != nil {
return err
}
if supported.DSR {
return nil
}
Expand All @@ -170,7 +182,10 @@ func DSRSupported() error {

// Slash32EndpointPrefixesSupported returns an error if the HCN version does not support configuring endpoints with /32 prefixes.
func Slash32EndpointPrefixesSupported() error {
supported := GetSupportedFeatures()
supported, err := GetCachedSupportedFeatures()
if err != nil {
return err
}
if supported.Slash32EndpointPrefixes {
return nil
}
Expand All @@ -179,7 +194,10 @@ func Slash32EndpointPrefixesSupported() error {

// AclSupportForProtocol252Supported returns an error if the HCN version does not support HNS ACL Policies to support protocol 252 for VXLAN.
func AclSupportForProtocol252Supported() error {
supported := GetSupportedFeatures()
supported, err := GetCachedSupportedFeatures()
if err != nil {
return err
}
if supported.AclSupportForProtocol252 {
return nil
}
Expand All @@ -188,7 +206,10 @@ func AclSupportForProtocol252Supported() error {

// SessionAffinitySupported returns an error if the HCN version does not support Session Affinity.
func SessionAffinitySupported() error {
supported := GetSupportedFeatures()
supported, err := GetCachedSupportedFeatures()
if err != nil {
return err
}
if supported.SessionAffinity {
return nil
}
Expand All @@ -197,7 +218,10 @@ func SessionAffinitySupported() error {

// IPv6DualStackSupported returns an error if the HCN version does not support IPv6DualStack.
func IPv6DualStackSupported() error {
supported := GetSupportedFeatures()
supported, err := GetCachedSupportedFeatures()
if err != nil {
return err
}
if supported.IPv6DualStack {
return nil
}
Expand All @@ -206,7 +230,10 @@ func IPv6DualStackSupported() error {

//L4proxySupported returns an error if the HCN verison does not support L4Proxy
func L4proxyPolicySupported() error {
supported := GetSupportedFeatures()
supported, err := GetCachedSupportedFeatures()
if err != nil {
return err
}
if supported.L4Proxy {
return nil
}
Expand All @@ -215,7 +242,10 @@ func L4proxyPolicySupported() error {

// L4WfpProxySupported returns an error if the HCN verison does not support L4WfpProxy
func L4WfpProxyPolicySupported() error {
supported := GetSupportedFeatures()
supported, err := GetCachedSupportedFeatures()
if err != nil {
return err
}
if supported.L4WfpProxy {
return nil
}
Expand All @@ -224,7 +254,10 @@ func L4WfpProxyPolicySupported() error {

// SetPolicySupported returns an error if the HCN version does not support SetPolicy.
func SetPolicySupported() error {
supported := GetSupportedFeatures()
supported, err := GetCachedSupportedFeatures()
if err != nil {
return err
}
if supported.SetPolicy {
return nil
}
Expand All @@ -233,7 +266,10 @@ func SetPolicySupported() error {

// VxlanPortSupported returns an error if the HCN version does not support configuring the VXLAN TCP port.
func VxlanPortSupported() error {
supported := GetSupportedFeatures()
supported, err := GetCachedSupportedFeatures()
if err != nil {
return err
}
if supported.VxlanPort {
return nil
}
Expand All @@ -242,7 +278,10 @@ func VxlanPortSupported() error {

// TierAclPolicySupported returns an error if the HCN version does not support configuring the TierAcl.
func TierAclPolicySupported() error {
supported := GetSupportedFeatures()
supported, err := GetCachedSupportedFeatures()
if err != nil {
return err
}
if supported.TierAcl {
return nil
}
Expand All @@ -251,7 +290,10 @@ func TierAclPolicySupported() error {

// NetworkACLPolicySupported returns an error if the HCN version does not support NetworkACLPolicy
func NetworkACLPolicySupported() error {
supported := GetSupportedFeatures()
supported, err := GetCachedSupportedFeatures()
if err != nil {
return err
}
if supported.NetworkACL {
return nil
}
Expand All @@ -260,14 +302,16 @@ func NetworkACLPolicySupported() error {

// NestedIpSetSupported returns an error if the HCN version does not support NestedIpSet
func NestedIpSetSupported() error {
supported := GetSupportedFeatures()
supported, err := GetCachedSupportedFeatures()
if err != nil {
return err
}
if supported.NestedIpSet {
return nil
}
return platformDoesNotSupportError("NestedIpSet")
}


// RequestType are the different operations performed to settings.
// Used to update the settings of Endpoint/Namespace objects.
type RequestType string
Expand Down
77 changes: 65 additions & 12 deletions hcn/hcnsupport.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,17 @@ import (
"fmt"
"sync"

"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)

var versionOnce sync.Once
var (
// featuresOnce handles assigning the supported features and printing the supported info to stdout only once to avoid unnecessary work
// multiple times.
featuresOnce sync.Once
versionErr error
supportedFeatures SupportedFeatures
)

// SupportedFeatures are the features provided by the Service.
type SupportedFeatures struct {
Expand Down Expand Up @@ -43,7 +50,59 @@ type ApiSupport struct {
V2 bool `json:"V2"`
}

// GetSupportedFeatures returns the features supported by the Service.
// GetCachedSupportedFeatures returns the features supported by the Service and an error if the query failed. If this has been called
// before it will return the supported features and error received from the first call. This can be used to optimize if many calls to the
// various hcn.IsXSupported methods need to be made.
func GetCachedSupportedFeatures() (SupportedFeatures, error) {
// Only query the HCN version and features supported once, instead of everytime this is invoked. The logs are useful to
// debug incidents where there's confusion on if a feature is supported on the host machine. The sync.Once helps to avoid redundant
// spam of these anytime a check needs to be made for if an HCN feature is supported. This is a common occurrence in kube-proxy
// for example.
featuresOnce.Do(func() {
globals, err := GetGlobals()
if err != nil {
// It's expected if this fails once, it should always fail. It should fail on pre 1803 builds for example.
versionErr = errors.Wrap(err, "failed to query HCN version number: this is expected on pre 1803 builds.")
} else {
supportedFeatures.Acl = AclFeatures{
AclAddressLists: isFeatureSupported(globals.Version, HNSVersion1803),
AclNoHostRulePriority: isFeatureSupported(globals.Version, HNSVersion1803),
AclPortRanges: isFeatureSupported(globals.Version, HNSVersion1803),
AclRuleId: isFeatureSupported(globals.Version, HNSVersion1803),
}

supportedFeatures.Api = ApiSupport{
V2: isFeatureSupported(globals.Version, V2ApiSupport),
V1: true, // HNSCall is still available.
}

supportedFeatures.RemoteSubnet = isFeatureSupported(globals.Version, RemoteSubnetVersion)
supportedFeatures.HostRoute = isFeatureSupported(globals.Version, HostRouteVersion)
supportedFeatures.DSR = isFeatureSupported(globals.Version, DSRVersion)
supportedFeatures.Slash32EndpointPrefixes = isFeatureSupported(globals.Version, Slash32EndpointPrefixesVersion)
supportedFeatures.AclSupportForProtocol252 = isFeatureSupported(globals.Version, AclSupportForProtocol252Version)
supportedFeatures.SessionAffinity = isFeatureSupported(globals.Version, SessionAffinityVersion)
supportedFeatures.IPv6DualStack = isFeatureSupported(globals.Version, IPv6DualStackVersion)
supportedFeatures.SetPolicy = isFeatureSupported(globals.Version, SetPolicyVersion)
supportedFeatures.VxlanPort = isFeatureSupported(globals.Version, VxlanPortVersion)
supportedFeatures.L4Proxy = isFeatureSupported(globals.Version, L4ProxyPolicyVersion)
supportedFeatures.L4WfpProxy = isFeatureSupported(globals.Version, L4WfpProxyPolicyVersion)
supportedFeatures.TierAcl = isFeatureSupported(globals.Version, TierAclPolicyVersion)
supportedFeatures.NetworkACL = isFeatureSupported(globals.Version, NetworkACLPolicyVersion)
supportedFeatures.NestedIpSet = isFeatureSupported(globals.Version, NestedIpSetVersion)

logrus.WithFields(logrus.Fields{
"version": fmt.Sprintf("%+v", globals.Version),
"supportedFeatures": fmt.Sprintf("%+v", supportedFeatures),
}).Info("HCN feature check")
}
})

return supportedFeatures, versionErr
}

// GetSupportedFeatures returns the features supported by the Service. Prefer `GetCachedSupportedFeatures` as this method will query hns and validate
// every feature is supported on every invocation.
func GetSupportedFeatures() SupportedFeatures {
var features SupportedFeatures

Expand Down Expand Up @@ -81,16 +140,10 @@ func GetSupportedFeatures() SupportedFeatures {
features.NetworkACL = isFeatureSupported(globals.Version, NetworkACLPolicyVersion)
features.NestedIpSet = isFeatureSupported(globals.Version, NestedIpSetVersion)

// Only print the HCN version and features supported once, instead of everytime this is invoked. These logs are useful to
// debug incidents where there's confusion on if a feature is supported on the host machine. The sync.Once helps to avoid redundant
// spam of these anytime a check needs to be made for if an HCN feature is supported. This is a common occurrence in kubeproxy
// for example.
versionOnce.Do(func() {
logrus.WithFields(logrus.Fields{
"version": fmt.Sprintf("%+v", globals.Version),
"supportedFeatures": fmt.Sprintf("%+v", features),
}).Info("HCN feature check")
})
logrus.WithFields(logrus.Fields{
"version": fmt.Sprintf("%+v", globals.Version),
"supportedFeatures": fmt.Sprintf("%+v", features),
}).Info("HCN feature check")

return features
}
Expand Down

0 comments on commit 112b5e7

Please sign in to comment.