Skip to content

Commit

Permalink
Merge pull request #3518 from chathuranga95/fix-re-created-routing-issue
Browse files Browse the repository at this point in the history
[Choreo] Fix intelligent routing availability after deleting and re-creating an API
  • Loading branch information
chathuranga95 authored May 14, 2024
2 parents 5ec1d7b + 0725795 commit aa68bcf
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 151 deletions.
150 changes: 19 additions & 131 deletions adapter/internal/discovery/xds/semantic_versioning.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,34 +36,14 @@ func GetVersionMatchRegex(version string) string {
func GetMajorMinorVersionRangeRegex(semVersion semantic_version.SemVersion) string {
majorVersion := strconv.Itoa(semVersion.Major)
minorVersion := strconv.Itoa(semVersion.Minor)
if semVersion.Patch == nil {
return "v" + majorVersion + "(?:\\." + minorVersion + ")?"
}
patchVersion := strconv.Itoa(*semVersion.Patch)
return "v" + majorVersion + "(?:\\." + minorVersion + "(?:\\." + patchVersion + ")?)?"
}

// GetMinorVersionRangeRegex generates minor version compatible range regex for the given version
func GetMinorVersionRangeRegex(semVersion semantic_version.SemVersion) string {
if semVersion.Patch == nil {
return GetVersionMatchRegex(semVersion.Version)
}
majorVersion := strconv.Itoa(semVersion.Major)
minorVersion := strconv.Itoa(semVersion.Minor)
patchVersion := strconv.Itoa(*semVersion.Patch)
return "v" + majorVersion + "\\." + minorVersion + "(?:\\." + patchVersion + ")?"
return "v" + majorVersion + "(?:\\." + minorVersion + ")?"
}

// GetMajorVersionRange generates major version range for the given version
func GetMajorVersionRange(semVersion semantic_version.SemVersion) string {
return "v" + strconv.Itoa(semVersion.Major)
}

// GetMinorVersionRange generates minor version range for the given version
func GetMinorVersionRange(semVersion semantic_version.SemVersion) string {
return "v" + strconv.Itoa(semVersion.Major) + "." + strconv.Itoa(semVersion.Minor)
}

func updateRoutingRulesOnAPIUpdate(organizationID, apiIdentifier, apiName, apiVersion, vHost string) {
apiSemVersion, err := semantic_version.ValidateAndGetVersionComponents(apiVersion, apiName)
// If the version validation is not success, we just proceed without intelligent version
Expand All @@ -73,39 +53,28 @@ func updateRoutingRulesOnAPIUpdate(organizationID, apiIdentifier, apiName, apiVe
}

apiRangeIdentifier := GenerateIdentifierForAPIWithoutVersion(vHost, apiName)
// Check the major and minor version ranges of the current API
// Check the major version range of the current API
existingMajorRangeLatestSemVersion, isMajorRangeRegexAvailable :=
orgIDLatestAPIVersionMap[organizationID][apiRangeIdentifier][GetMajorVersionRange(*apiSemVersion)]
existingMinorRangeLatestSemVersion, isMinorRangeRegexAvailable :=
orgIDLatestAPIVersionMap[organizationID][apiRangeIdentifier][GetMinorVersionRange(*apiSemVersion)]

// Check whether the current API is the latest version in the major and minor version ranges
// Check whether the current API is the latest version in the major version range
isLatestMajorVersion := !isMajorRangeRegexAvailable || existingMajorRangeLatestSemVersion.Compare(*apiSemVersion)
isLatestMinorVersion := !isMinorRangeRegexAvailable || existingMinorRangeLatestSemVersion.Compare(*apiSemVersion)

// Remove the existing regexes from the path specifier when latest major and/or minor version is available
if (isMajorRangeRegexAvailable || isMinorRangeRegexAvailable) && (isLatestMajorVersion || isLatestMinorVersion) {
// Remove the existing regexes from the path specifier when latest major version is available
if isMajorRangeRegexAvailable && isLatestMajorVersion {
// Organization's all apis
for apiUUID, swagger := range orgIDAPIMgwSwaggerMap[organizationID] {
// API's all versions in the same vHost
if swagger.GetTitle() == apiName && swagger.GetVHost() == vHost {
if (isMajorRangeRegexAvailable && swagger.GetVersion() == existingMajorRangeLatestSemVersion.Version) ||
(isMinorRangeRegexAvailable && swagger.GetVersion() == existingMinorRangeLatestSemVersion.Version) {
if swagger.GetVersion() == existingMajorRangeLatestSemVersion.Version {
for _, route := range orgIDOpenAPIRoutesMap[organizationID][apiUUID] {
regex := route.GetMatch().GetSafeRegex().GetRegex()
regexRewritePattern := route.GetRoute().GetRegexRewrite().GetPattern().GetRegex()
existingMinorRangeLatestVersionRegex := GetVersionMatchRegex(existingMinorRangeLatestSemVersion.Version)
existingMajorRangeLatestVersionRegex := GetVersionMatchRegex(existingMajorRangeLatestSemVersion.Version)
if isMinorRangeRegexAvailable && swagger.GetVersion() == existingMinorRangeLatestSemVersion.Version && isLatestMinorVersion {
regex = strings.Replace(regex, GetMinorVersionRangeRegex(existingMinorRangeLatestSemVersion), existingMinorRangeLatestVersionRegex, 1)
regex = strings.Replace(regex, GetMajorMinorVersionRangeRegex(existingMajorRangeLatestSemVersion), existingMajorRangeLatestVersionRegex, 1)
regexRewritePattern = strings.Replace(regexRewritePattern, GetMinorVersionRangeRegex(existingMinorRangeLatestSemVersion), existingMinorRangeLatestVersionRegex, 1)
regexRewritePattern = strings.Replace(regexRewritePattern, GetMajorMinorVersionRangeRegex(existingMajorRangeLatestSemVersion), existingMajorRangeLatestVersionRegex, 1)
}
if isMajorRangeRegexAvailable && swagger.GetVersion() == existingMajorRangeLatestSemVersion.Version && isLatestMajorVersion {
regex = strings.Replace(regex, GetMajorMinorVersionRangeRegex(existingMajorRangeLatestSemVersion), GetMinorVersionRangeRegex(existingMajorRangeLatestSemVersion), 1)
regexRewritePattern = strings.Replace(regexRewritePattern, GetMajorMinorVersionRangeRegex(existingMajorRangeLatestSemVersion), GetMinorVersionRangeRegex(existingMajorRangeLatestSemVersion), 1)
}

regex = strings.Replace(regex, GetMajorMinorVersionRangeRegex(existingMajorRangeLatestSemVersion), existingMajorRangeLatestVersionRegex, 1)
regexRewritePattern = strings.Replace(regexRewritePattern, GetMajorMinorVersionRangeRegex(existingMajorRangeLatestSemVersion), existingMajorRangeLatestVersionRegex, 1)

pathSpecifier := &routev3.RouteMatch_SafeRegex{
SafeRegex: &envoy_type_matcherv3.RegexMatcher{
Regex: regex,
Expand All @@ -121,35 +90,27 @@ func updateRoutingRulesOnAPIUpdate(organizationID, apiIdentifier, apiName, apiVe
}
}

if isLatestMajorVersion || isLatestMinorVersion {
// Update local memory map with the latest version ranges
if isLatestMajorVersion {
// Update local memory map with the latest version range
majorVersionRange := GetMajorVersionRange(*apiSemVersion)
minorVersionRange := GetMinorVersionRange(*apiSemVersion)
if _, orgExists := orgIDLatestAPIVersionMap[organizationID]; !orgExists {
orgIDLatestAPIVersionMap[organizationID] = make(map[string]map[string]semantic_version.SemVersion)
}
if _, apiRangeExists := orgIDLatestAPIVersionMap[organizationID][apiRangeIdentifier]; !apiRangeExists {
orgIDLatestAPIVersionMap[organizationID][apiRangeIdentifier] = make(map[string]semantic_version.SemVersion)
}
latestVersions := orgIDLatestAPIVersionMap[organizationID][apiRangeIdentifier]
latestVersions[minorVersionRange] = *apiSemVersion
if isLatestMajorVersion {
latestVersions[majorVersionRange] = *apiSemVersion
}
latestVersions[majorVersionRange] = *apiSemVersion

// Add the major and/or minor version range matching regexes to the path specifier when
// latest major and/or minor version is available
// Add the major version range matching regexes to the path specifier when latest major version is available
for _, route := range orgIDOpenAPIRoutesMap[organizationID][apiIdentifier] {
regex := route.GetMatch().GetSafeRegex().GetRegex()
regexRewritePattern := route.GetRoute().GetRegexRewrite().GetPattern().GetRegex()
apiVersionRegex := GetVersionMatchRegex(apiVersion)
if isLatestMajorVersion {
regex = strings.Replace(regex, apiVersionRegex, GetMajorMinorVersionRangeRegex(*apiSemVersion), 1)
regexRewritePattern = strings.Replace(regexRewritePattern, apiVersionRegex, GetMajorMinorVersionRangeRegex(*apiSemVersion), 1)
} else if isLatestMinorVersion {
regex = strings.Replace(regex, apiVersionRegex, GetMinorVersionRangeRegex(*apiSemVersion), 1)
regexRewritePattern = strings.Replace(regexRewritePattern, apiVersionRegex, GetMinorVersionRangeRegex(*apiSemVersion), 1)
}

regex = strings.Replace(regex, apiVersionRegex, GetMajorMinorVersionRangeRegex(*apiSemVersion), 1)
regexRewritePattern = strings.Replace(regexRewritePattern, apiVersionRegex, GetMajorMinorVersionRangeRegex(*apiSemVersion), 1)

pathSpecifier := &routev3.RouteMatch_SafeRegex{
SafeRegex: &envoy_type_matcherv3.RegexMatcher{
Regex: regex,
Expand Down Expand Up @@ -185,11 +146,10 @@ func updateRoutingRulesOnAPIDelete(organizationID, apiIdentifier string, api mgw
Version: "",
Major: deletingAPISemVersion.Major,
Minor: 0,
Patch: nil,
}
for currentAPIIdentifier, swagger := range orgIDAPIMgwSwaggerMap[organizationID] {
// Iterate all the API versions other than the deleting API itself
if swagger.GetTitle() == api.GetTitle() && currentAPIIdentifier != apiIdentifier {
if swagger.GetTitle() == api.GetTitle() && currentAPIIdentifier != apiIdentifier && swagger.GetVHost() == api.GetVHost() {
currentAPISemVersion, _ := semantic_version.ValidateAndGetVersionComponents(swagger.GetVersion(), swagger.GetTitle())
if currentAPISemVersion != nil {
if currentAPISemVersion.Major == deletingAPISemVersion.Major {
Expand All @@ -207,19 +167,6 @@ func updateRoutingRulesOnAPIDelete(organizationID, apiIdentifier string, api mgw
regex := route.GetMatch().GetSafeRegex().GetRegex()
regexRewritePattern := route.GetRoute().GetRegexRewrite().GetPattern().GetRegex()
newLatestMajorRangeAPIVersionRegex := GetVersionMatchRegex(newLatestMajorRangeAPI.Version)
// Remove any available minor version range regexes and apply the minor range regex
regex = strings.Replace(
regex,
GetMinorVersionRangeRegex(*newLatestMajorRangeAPI),
newLatestMajorRangeAPIVersionRegex,
1,
)
regexRewritePattern = strings.Replace(
regexRewritePattern,
GetMinorVersionRangeRegex(*newLatestMajorRangeAPI),
newLatestMajorRangeAPIVersionRegex,
1,
)
regex = strings.Replace(
regex,
newLatestMajorRangeAPIVersionRegex,
Expand Down Expand Up @@ -248,63 +195,4 @@ func updateRoutingRulesOnAPIDelete(organizationID, apiIdentifier string, api mgw
}
}
}
minorVersionRange := GetMinorVersionRange(*deletingAPISemVersion)
if deletingAPIsMinorRangeLatestAPI, ok := latestAPIVersionMap[minorVersionRange]; ok {
if deletingAPIsMinorRangeLatestAPI.Version == api.GetVersion() {
newLatestMinorRangeAPI := &semantic_version.SemVersion{
Version: "",
Major: deletingAPISemVersion.Major,
Minor: deletingAPISemVersion.Minor,
Patch: nil,
}
newLatestMinorRangeAPIIdentifier := ""
for currentAPIIdentifier, swagger := range orgIDAPIMgwSwaggerMap[organizationID] {
// Iterate all the API versions other than the deleting API itself
if swagger.GetTitle() == api.GetTitle() && currentAPIIdentifier != apiIdentifier {
currentAPISemVersion, _ := semantic_version.ValidateAndGetVersionComponents(swagger.GetVersion(), swagger.GetTitle())
if currentAPISemVersion != nil {
if currentAPISemVersion.Major == deletingAPISemVersion.Major &&
currentAPISemVersion.Minor == deletingAPISemVersion.Minor {
if newLatestMinorRangeAPI.Compare(*currentAPISemVersion) {
newLatestMinorRangeAPI = currentAPISemVersion
newLatestMinorRangeAPIIdentifier = currentAPIIdentifier
}
}
}
}
}
if newLatestMinorRangeAPIIdentifier != "" && newLatestMinorRangeAPIIdentifier != newLatestMajorRangeAPIIdentifier {
orgIDLatestAPIVersionMap[organizationID][apiRangeIdentifier][minorVersionRange] = *newLatestMinorRangeAPI
for _, route := range orgIDOpenAPIRoutesMap[organizationID][newLatestMinorRangeAPIIdentifier] {
regex := route.GetMatch().GetSafeRegex().GetRegex()
newLatestMinorRangeAPIVersionRegex := GetVersionMatchRegex(newLatestMinorRangeAPI.Version)
regex = strings.Replace(
regex,
newLatestMinorRangeAPIVersionRegex,
GetMinorVersionRangeRegex(*newLatestMinorRangeAPI),
1,
)
pathSpecifier := &routev3.RouteMatch_SafeRegex{
SafeRegex: &envoy_type_matcherv3.RegexMatcher{
Regex: regex,
},
}
regexRewritePattern := route.GetRoute().GetRegexRewrite().GetPattern().GetRegex()
regexRewritePattern = strings.Replace(
regexRewritePattern,
newLatestMinorRangeAPIVersionRegex,
GetMinorVersionRangeRegex(*newLatestMinorRangeAPI),
1,
)
route.Match.PathSpecifier = pathSpecifier
action := &routev3.Route_Route{}
action = route.Action.(*routev3.Route_Route)
action.Route.RegexRewrite.Pattern.Regex = regexRewritePattern
route.Action = action
}
} else {
delete(orgIDLatestAPIVersionMap[organizationID][apiRangeIdentifier], minorVersionRange)
}
}
}
}
20 changes: 0 additions & 20 deletions adapter/pkg/semanticversion/semantic_version.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ type SemVersion struct {
Version string
Major int
Minor int
Patch *int
}

// ValidateAndGetVersionComponents validates version string and extracts version components
Expand Down Expand Up @@ -65,20 +64,13 @@ func ValidateAndGetVersionComponents(version string, apiName string) (*SemVersio
Version: version,
Major: majorVersion,
Minor: minorVersion,
Patch: nil,
}, nil
}

patchVersion, patchVersionConvErr := strconv.Atoi(versionComponents[2])
if patchVersionConvErr != nil {
logger.LoggerSemanticVersion.Errorf(fmt.Sprintf("API patch version should be an integer in API: %v. API Version: %v", apiName, version), patchVersionConvErr)
return nil, errors.New("Invalid version format")
}
return &SemVersion{
Version: version,
Major: majorVersion,
Minor: minorVersion,
Patch: &patchVersion,
}, nil
}

Expand All @@ -94,18 +86,6 @@ func (baseVersion SemVersion) Compare(version SemVersion) bool {
return true
} else if baseVersion.Minor > version.Minor {
return false
} else {
if baseVersion.Patch != nil && version.Patch != nil {
if *baseVersion.Patch < *version.Patch {
return true
} else if *baseVersion.Patch > *version.Patch {
return false
}
} else if baseVersion.Patch == nil && version.Patch != nil {
return true
} else if baseVersion.Patch != nil && version.Patch == nil {
return false
}
}
}
return true
Expand Down

0 comments on commit aa68bcf

Please sign in to comment.