-
Notifications
You must be signed in to change notification settings - Fork 4.4k
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
xds: version sniff envoy and switch regular expressions from 'regex' to 'safe_regex' on newer envoy versions #8222
Changes from all commits
f6e7ca7
b670a98
f846883
b20de68
75954da
716e0d6
7160479
c546485
797539e
de4b5ee
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package xds | ||
|
||
import ( | ||
"fmt" | ||
"regexp" | ||
|
||
envoycore "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" | ||
"github.com/hashicorp/go-version" | ||
) | ||
|
||
var ( | ||
// minSafeRegexVersion reflects the minimum version where we could use safe_regex instead of regex | ||
// | ||
// NOTE: the first version that no longer supported the old style was 1.13.0 | ||
minSafeRegexVersion = version.Must(version.NewVersion("1.11.2")) | ||
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. Conveniently for 1.8.x all of the officially supported versions are covered by this pattern. 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. I could have elected to avoid doing any sniffing at all here, but upcoming work would require us to add it right back in again anyway. 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. One thing we could start doing after this lands is to put in guardrails when an older or newer envoy dials in to a consul agent. Maybe not going so far as to reject the connection, but we could start logging a "this envoy is unsupported" warning message for stuff outside of our support matrix. |
||
) | ||
|
||
type supportedProxyFeatures struct { | ||
RouterMatchSafeRegex bool // use safe_regex instead of regex in http.router rules | ||
} | ||
|
||
func determineSupportedProxyFeatures(node *envoycore.Node) supportedProxyFeatures { | ||
version := determineEnvoyVersionFromNode(node) | ||
if version == nil { | ||
return supportedProxyFeatures{} | ||
} | ||
|
||
return supportedProxyFeatures{ | ||
RouterMatchSafeRegex: !version.LessThan(minSafeRegexVersion), | ||
} | ||
} | ||
|
||
// example: 1580db37e9a97c37e410bad0e1507ae1a0fd9e77/1.12.4/Clean/RELEASE/BoringSSL | ||
var buildVersionPattern = regexp.MustCompile(`^[a-f0-9]{40}/([^/]+)/Clean/RELEASE/BoringSSL$`) | ||
|
||
func determineEnvoyVersionFromNode(node *envoycore.Node) *version.Version { | ||
if node == nil { | ||
return nil | ||
} | ||
|
||
if node.UserAgentVersionType == nil { | ||
if node.BuildVersion == "" { | ||
return nil | ||
} | ||
|
||
// Must be an older pre-1.13 envoy | ||
m := buildVersionPattern.FindStringSubmatch(node.BuildVersion) | ||
if m == nil { | ||
return nil | ||
} | ||
|
||
return version.Must(version.NewVersion(m[1])) | ||
} | ||
|
||
if node.UserAgentName != "envoy" { | ||
return nil | ||
} | ||
|
||
bv, ok := node.UserAgentVersionType.(*envoycore.Node_UserAgentBuildVersion) | ||
if !ok { | ||
// NOTE: we could sniff for *envoycore.Node_UserAgentVersion and do more regex but official builds don't have this problem. | ||
return nil | ||
} | ||
if bv.UserAgentBuildVersion == nil { | ||
return nil | ||
} | ||
v := bv.UserAgentBuildVersion.Version | ||
|
||
return version.Must(version.NewVersion( | ||
fmt.Sprintf("%d.%d.%d", | ||
v.GetMajorNumber(), | ||
v.GetMinorNumber(), | ||
v.GetPatch(), | ||
), | ||
)) | ||
} | ||
|
||
func determineSupportedProxyFeaturesFromString(vs string) supportedProxyFeatures { | ||
version := version.Must(version.NewVersion(vs)) | ||
return supportedProxyFeatures{ | ||
RouterMatchSafeRegex: !version.LessThan(minSafeRegexVersion), | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package xds | ||
|
||
import ( | ||
"testing" | ||
|
||
envoycore "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" | ||
envoytype "github.com/envoyproxy/go-control-plane/envoy/type" | ||
"github.com/hashicorp/go-version" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestDetermineEnvoyVersionFromNode(t *testing.T) { | ||
cases := map[string]struct { | ||
node *envoycore.Node | ||
expect *version.Version | ||
}{ | ||
"empty": { | ||
node: &envoycore.Node{}, | ||
expect: nil, | ||
}, | ||
"only build version": { | ||
node: &envoycore.Node{ | ||
BuildVersion: "1580db37e9a97c37e410bad0e1507ae1a0fd9e77/1.9.0/Clean/RELEASE/BoringSSL", | ||
}, | ||
expect: version.Must(version.NewVersion("1.9.0")), | ||
}, | ||
"user agent build version but no user agent": { | ||
node: &envoycore.Node{ | ||
UserAgentName: "", | ||
UserAgentVersionType: &envoycore.Node_UserAgentBuildVersion{ | ||
UserAgentBuildVersion: &envoycore.BuildVersion{ | ||
Version: &envoytype.SemanticVersion{ | ||
MajorNumber: 1, | ||
MinorNumber: 14, | ||
Patch: 4, | ||
}, | ||
}, | ||
}, | ||
}, | ||
expect: nil, | ||
}, | ||
"user agent build version with user agent": { | ||
node: &envoycore.Node{ | ||
UserAgentName: "envoy", | ||
UserAgentVersionType: &envoycore.Node_UserAgentBuildVersion{ | ||
UserAgentBuildVersion: &envoycore.BuildVersion{ | ||
Version: &envoytype.SemanticVersion{ | ||
MajorNumber: 1, | ||
MinorNumber: 14, | ||
Patch: 4, | ||
}, | ||
}, | ||
}, | ||
}, | ||
expect: version.Must(version.NewVersion("1.14.4")), | ||
}, | ||
} | ||
|
||
for name, tc := range cases { | ||
tc := tc | ||
t.Run(name, func(t *testing.T) { | ||
got := determineEnvoyVersionFromNode(tc.node) | ||
if tc.expect != nil { | ||
require.Equal(t, tc.expect, got) | ||
} else { | ||
require.Nil(t, got) | ||
} | ||
}) | ||
} | ||
} |
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.
Reminder to move these to somewhere that an agent API can also pull them
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.
I figure the moving can happen in the other PR that surfaces the feature.