mirror of
https://github.com/alibaba/higress.git
synced 2026-05-29 23:27:28 +08:00
Feat: upgrade istio from 1.19.5 to 1.27.1 (#3066)
This commit is contained in:
@@ -18,18 +18,16 @@ import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
k8s "sigs.k8s.io/gateway-api/apis/v1"
|
||||
|
||||
"istio.io/istio/pilot/pkg/features"
|
||||
"istio.io/istio/pilot/pkg/model/kstatus"
|
||||
"istio.io/istio/pkg/config"
|
||||
"istio.io/istio/pkg/config/schema/gvk"
|
||||
"istio.io/istio/pkg/kube/controllers"
|
||||
"istio.io/istio/pkg/maps"
|
||||
"istio.io/istio/pkg/ptr"
|
||||
"istio.io/istio/pkg/slices"
|
||||
"istio.io/istio/pkg/util/sets"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
k8s "sigs.k8s.io/gateway-api/apis/v1alpha2"
|
||||
k8sbeta "sigs.k8s.io/gateway-api/apis/v1beta1"
|
||||
|
||||
"github.com/alibaba/higress/v2/pkg/config/constants"
|
||||
)
|
||||
|
||||
// RouteParentResult holds the result of a route for a specific parent
|
||||
@@ -40,19 +38,31 @@ type RouteParentResult struct {
|
||||
DeniedReason *ParentError
|
||||
// RouteError, if present, indicates why the reference was not valid
|
||||
RouteError *ConfigError
|
||||
// WaypointError, if present, indicates why the reference was does not have a waypoint
|
||||
WaypointError *WaypointError
|
||||
}
|
||||
|
||||
func createRouteStatus(parentResults []RouteParentResult, obj config.Config, currentParents []k8s.RouteParentStatus) []k8s.RouteParentStatus {
|
||||
parents := make([]k8s.RouteParentStatus, 0, len(parentResults))
|
||||
// Fill in all the gateways that are already present but not owned by us. This is non-trivial as there may be multiple
|
||||
// gateway controllers that are exposing their status on the same route. We need to attempt to manage ours properly (including
|
||||
// removing gateway references when they are removed), without mangling other Controller's status.
|
||||
for _, r := range currentParents {
|
||||
if r.ControllerName != constants.ManagedGatewayController {
|
||||
// We don't own this status, so keep it around
|
||||
parents = append(parents, r)
|
||||
func createRouteStatus(
|
||||
parentResults []RouteParentResult,
|
||||
objectNamespace string,
|
||||
generation int64,
|
||||
currentParents []k8s.RouteParentStatus,
|
||||
) []k8s.RouteParentStatus {
|
||||
parents := slices.Clone(currentParents)
|
||||
parentIndexes := map[string]int{}
|
||||
for idx, p := range parents {
|
||||
// Only consider our own
|
||||
if p.ControllerName != k8s.GatewayController(features.ManagedGatewayController) {
|
||||
continue
|
||||
}
|
||||
rs := parentRefString(p.ParentRef, objectNamespace)
|
||||
if _, f := parentIndexes[rs]; f {
|
||||
log.Warnf("invalid HTTPRoute detected: duplicate parent: %v", rs)
|
||||
} else {
|
||||
parentIndexes[rs] = idx
|
||||
}
|
||||
}
|
||||
|
||||
// Collect all of our unique parent references. There may be multiple when we have a route without section name,
|
||||
// but reference a parent with multiple sections.
|
||||
// While we process these internally for-each sectionName, in the status we are just supposed to report one merged entry
|
||||
@@ -72,55 +82,63 @@ func createRouteStatus(parentResults []RouteParentResult, obj config.Config, cur
|
||||
seenReasons.Insert(ParentNoError)
|
||||
}
|
||||
}
|
||||
reasonRanking := []ParentErrorReason{
|
||||
// No errors is preferred
|
||||
ParentNoError,
|
||||
// All route level errors
|
||||
ParentErrorNotAllowed,
|
||||
ParentErrorNoHostname,
|
||||
ParentErrorParentRefConflict,
|
||||
// Failures to match the Port or SectionName. These are last so that if we bind to 1 listener we
|
||||
// just report errors for that 1 listener instead of for all sections we didn't bind to
|
||||
ParentErrorNotAccepted,
|
||||
|
||||
const (
|
||||
rankParentNoErrors = iota
|
||||
rankParentErrorNotAllowed
|
||||
rankParentErrorNoHostname
|
||||
rankParentErrorParentRefConflict
|
||||
rankParentErrorNotAccepted
|
||||
)
|
||||
|
||||
rankParentError := func(result RouteParentResult) int {
|
||||
if result.DeniedReason == nil {
|
||||
return rankParentNoErrors
|
||||
}
|
||||
switch result.DeniedReason.Reason {
|
||||
case ParentErrorNotAllowed:
|
||||
return rankParentErrorNotAllowed
|
||||
case ParentErrorNoHostname:
|
||||
return rankParentErrorNoHostname
|
||||
case ParentErrorParentRefConflict:
|
||||
return rankParentErrorParentRefConflict
|
||||
case ParentErrorNotAccepted:
|
||||
return rankParentErrorNotAccepted
|
||||
}
|
||||
return rankParentNoErrors
|
||||
}
|
||||
|
||||
// Next we want to collapse these. We need to report 1 type of error, or none.
|
||||
report := map[k8s.ParentReference]RouteParentResult{}
|
||||
for _, wantReason := range reasonRanking {
|
||||
if !seenReasons.Contains(wantReason) {
|
||||
for ref, results := range seen {
|
||||
if len(results) == 0 {
|
||||
continue
|
||||
}
|
||||
// We found our highest priority ranking, now we need to collapse this into a single message
|
||||
for k, refs := range seen {
|
||||
for _, ref := range refs {
|
||||
reason := ParentNoError
|
||||
if ref.DeniedReason != nil {
|
||||
reason = ref.DeniedReason.Reason
|
||||
}
|
||||
if wantReason != reason {
|
||||
// Skip this one, it is for a less relevant reason
|
||||
continue
|
||||
}
|
||||
exist, f := report[k]
|
||||
if f {
|
||||
if ref.DeniedReason != nil {
|
||||
if exist.DeniedReason != nil {
|
||||
// join the error
|
||||
exist.DeniedReason.Message += "; " + ref.DeniedReason.Message
|
||||
} else {
|
||||
exist.DeniedReason = ref.DeniedReason
|
||||
}
|
||||
}
|
||||
|
||||
toReport := results[0]
|
||||
mostSevereRankSeen := rankParentError(toReport)
|
||||
|
||||
for _, result := range results[1:] {
|
||||
resultRank := rankParentError(result)
|
||||
// lower number means more severe
|
||||
if resultRank < mostSevereRankSeen {
|
||||
mostSevereRankSeen = resultRank
|
||||
toReport = result
|
||||
} else if resultRank == mostSevereRankSeen {
|
||||
// join the error messages
|
||||
if toReport.DeniedReason == nil {
|
||||
toReport.DeniedReason = result.DeniedReason
|
||||
} else {
|
||||
exist = ref
|
||||
toReport.DeniedReason.Message += "; " + result.DeniedReason.Message
|
||||
}
|
||||
report[k] = exist
|
||||
}
|
||||
}
|
||||
// Once we find the best reason, do not consider any others
|
||||
break
|
||||
|
||||
report[ref] = toReport
|
||||
}
|
||||
|
||||
// Now we fill in all the parents we do own
|
||||
var toAppend []k8s.RouteParentStatus
|
||||
for k, gw := range report {
|
||||
msg := "Route was valid"
|
||||
if successCount[k] > 1 {
|
||||
@@ -151,31 +169,64 @@ func createRouteStatus(parentResults []RouteParentResult, obj config.Config, cur
|
||||
}
|
||||
}
|
||||
|
||||
// when ambient is enabled, report the waypoints resolved condition
|
||||
if features.EnableAmbient {
|
||||
cond := &condition{
|
||||
reason: string(RouteReasonResolvedWaypoints),
|
||||
message: "All waypoints resolved",
|
||||
}
|
||||
if gw.WaypointError != nil {
|
||||
cond.message = gw.WaypointError.Message
|
||||
}
|
||||
conds[string(RouteConditionResolvedWaypoints)] = cond
|
||||
}
|
||||
|
||||
myRef := parentRefString(gw.OriginalReference, objectNamespace)
|
||||
var currentConditions []metav1.Condition
|
||||
currentStatus := slices.FindFunc(currentParents, func(s k8sbeta.RouteParentStatus) bool {
|
||||
return parentRefString(s.ParentRef) == parentRefString(gw.OriginalReference)
|
||||
currentStatus := slices.FindFunc(currentParents, func(s k8s.RouteParentStatus) bool {
|
||||
return parentRefString(s.ParentRef, objectNamespace) == myRef &&
|
||||
s.ControllerName == k8s.GatewayController(features.ManagedGatewayController)
|
||||
})
|
||||
if currentStatus != nil {
|
||||
currentConditions = currentStatus.Conditions
|
||||
}
|
||||
parents = append(parents, k8s.RouteParentStatus{
|
||||
ns := k8s.RouteParentStatus{
|
||||
ParentRef: gw.OriginalReference,
|
||||
ControllerName: constants.ManagedGatewayController,
|
||||
Conditions: setConditions(obj.Generation, currentConditions, conds),
|
||||
})
|
||||
ControllerName: k8s.GatewayController(features.ManagedGatewayController),
|
||||
Conditions: setConditions(generation, currentConditions, conds),
|
||||
}
|
||||
// Parent ref already exists, insert in the same place
|
||||
if idx, f := parentIndexes[myRef]; f {
|
||||
parents[idx] = ns
|
||||
// Clear it out so we can detect which ones we need to delete later
|
||||
delete(parentIndexes, myRef)
|
||||
} else {
|
||||
// Else queue it up to append to the end. We don't append now since we will want to sort them.
|
||||
toAppend = append(toAppend, ns)
|
||||
}
|
||||
}
|
||||
// Ensure output is deterministic.
|
||||
// TODO: will we fight over other controllers doing similar (but not identical) ordering?
|
||||
sort.SliceStable(parents, func(i, j int) bool {
|
||||
return parentRefString(parents[i].ParentRef) > parentRefString(parents[j].ParentRef)
|
||||
sort.SliceStable(toAppend, func(i, j int) bool {
|
||||
return parentRefString(toAppend[i].ParentRef, objectNamespace) > parentRefString(toAppend[j].ParentRef, objectNamespace)
|
||||
})
|
||||
parents = append(parents, toAppend...)
|
||||
toDelete := sets.New(maps.Values(parentIndexes)...)
|
||||
parents = FilterInPlaceByIndex(parents, func(i int) bool {
|
||||
_, f := toDelete[i]
|
||||
return !f
|
||||
})
|
||||
|
||||
if parents == nil {
|
||||
return []k8s.RouteParentStatus{}
|
||||
}
|
||||
return parents
|
||||
}
|
||||
|
||||
type ParentErrorReason string
|
||||
|
||||
const (
|
||||
ParentErrorNotAccepted = ParentErrorReason(k8sbeta.RouteReasonNoMatchingParent)
|
||||
ParentErrorNotAccepted = ParentErrorReason(k8s.RouteReasonNoMatchingParent)
|
||||
ParentErrorNotAllowed = ParentErrorReason(k8s.RouteReasonNotAllowedByListeners)
|
||||
ParentErrorNoHostname = ParentErrorReason(k8s.RouteReasonNoMatchingListenerHostname)
|
||||
ParentErrorParentRefConflict = ParentErrorReason("ParentRefConflict")
|
||||
@@ -185,29 +236,41 @@ const (
|
||||
type ConfigErrorReason = string
|
||||
|
||||
const (
|
||||
// InvalidRefNotPermitted indicates a route was not permitted
|
||||
InvalidRefNotPermitted ConfigErrorReason = ConfigErrorReason(k8s.RouteReasonRefNotPermitted)
|
||||
// InvalidDestination indicates an issue with the destination
|
||||
InvalidDestination ConfigErrorReason = "InvalidDestination"
|
||||
InvalidAddress ConfigErrorReason = ConfigErrorReason(k8sbeta.GatewayReasonUnsupportedAddress)
|
||||
InvalidAddress ConfigErrorReason = ConfigErrorReason(k8s.GatewayReasonUnsupportedAddress)
|
||||
// InvalidDestinationPermit indicates a destination was not permitted
|
||||
InvalidDestinationPermit ConfigErrorReason = ConfigErrorReason(k8s.RouteReasonRefNotPermitted)
|
||||
// InvalidDestinationKind indicates an issue with the destination kind
|
||||
InvalidDestinationKind ConfigErrorReason = ConfigErrorReason(k8s.RouteReasonInvalidKind)
|
||||
// InvalidDestinationNotFound indicates a destination does not exist
|
||||
InvalidDestinationNotFound ConfigErrorReason = ConfigErrorReason(k8s.RouteReasonBackendNotFound)
|
||||
// InvalidParentRef indicates we could not refer to the parent we request
|
||||
InvalidParentRef ConfigErrorReason = "InvalidParentReference"
|
||||
// InvalidFilter indicates an issue with the filters
|
||||
InvalidFilter ConfigErrorReason = "InvalidFilter"
|
||||
// InvalidTLS indicates an issue with TLS settings
|
||||
InvalidTLS ConfigErrorReason = ConfigErrorReason(k8sbeta.ListenerReasonInvalidCertificateRef)
|
||||
InvalidTLS ConfigErrorReason = ConfigErrorReason(k8s.ListenerReasonInvalidCertificateRef)
|
||||
// InvalidListenerRefNotPermitted indicates a listener reference was not permitted
|
||||
InvalidListenerRefNotPermitted ConfigErrorReason = ConfigErrorReason(k8sbeta.ListenerReasonRefNotPermitted)
|
||||
InvalidListenerRefNotPermitted ConfigErrorReason = ConfigErrorReason(k8s.ListenerReasonRefNotPermitted)
|
||||
// InvalidConfiguration indicates a generic error for all other invalid configurations
|
||||
InvalidConfiguration ConfigErrorReason = "InvalidConfiguration"
|
||||
InvalidResources ConfigErrorReason = ConfigErrorReason(k8sbeta.GatewayReasonNoResources)
|
||||
DeprecateFieldUsage = "DeprecatedField"
|
||||
DeprecateFieldUsage ConfigErrorReason = "DeprecatedField"
|
||||
)
|
||||
|
||||
const (
|
||||
// This condition indicates whether a route's parent reference has
|
||||
// a waypoint configured by resolving the "istio.io/use-waypoint" label
|
||||
// on either the referenced parent or the parent's namespace.
|
||||
RouteConditionResolvedWaypoints k8s.RouteConditionType = "ResolvedWaypoints"
|
||||
RouteReasonResolvedWaypoints k8s.RouteConditionReason = "ResolvedWaypoints"
|
||||
)
|
||||
|
||||
type WaypointErrorReason string
|
||||
|
||||
const (
|
||||
WaypointErrorReasonMissingLabel = WaypointErrorReason("MissingUseWaypointLabel")
|
||||
WaypointErrorMsgMissingLabel = "istio.io/use-waypoint label missing from parent and parent namespace; in ambient mode, route will not be respected"
|
||||
WaypointErrorReasonNoMatchingParent = WaypointErrorReason("NoMatchingParent")
|
||||
WaypointErrorMsgNoMatchingParent = "parent not found"
|
||||
)
|
||||
|
||||
// ParentError represents that a parent could not be referenced
|
||||
@@ -222,6 +285,11 @@ type ConfigError struct {
|
||||
Message string
|
||||
}
|
||||
|
||||
type WaypointError struct {
|
||||
Reason WaypointErrorReason
|
||||
Message string
|
||||
}
|
||||
|
||||
type condition struct {
|
||||
// reason defines the reason to report on success. Ignored if error is set
|
||||
reason string
|
||||
@@ -282,59 +350,48 @@ func setConditions(generation int64, existingConditions []metav1.Condition, cond
|
||||
return existingConditions
|
||||
}
|
||||
|
||||
func reportListenerAttachedRoutes(index int, obj config.Config, i int32) {
|
||||
obj.Status.(*kstatus.WrappedStatus).Mutate(func(s config.Status) config.Status {
|
||||
gs := s.(*k8s.GatewayStatus)
|
||||
for index >= len(gs.Listeners) {
|
||||
gs.Listeners = append(gs.Listeners, k8s.ListenerStatus{})
|
||||
func reportListenerCondition(index int, l k8s.Listener, obj controllers.Object,
|
||||
statusListeners []k8s.ListenerStatus, conditions map[string]*condition,
|
||||
) []k8s.ListenerStatus {
|
||||
for index >= len(statusListeners) {
|
||||
statusListeners = append(statusListeners, k8s.ListenerStatus{})
|
||||
}
|
||||
cond := statusListeners[index].Conditions
|
||||
supported, valid := generateSupportedKinds(l)
|
||||
if !valid {
|
||||
conditions[string(k8s.ListenerConditionResolvedRefs)] = &condition{
|
||||
reason: string(k8s.ListenerReasonInvalidRouteKinds),
|
||||
status: metav1.ConditionFalse,
|
||||
message: "Invalid route kinds",
|
||||
}
|
||||
status := gs.Listeners[index]
|
||||
status.AttachedRoutes = i
|
||||
gs.Listeners[index] = status
|
||||
return gs
|
||||
})
|
||||
}
|
||||
|
||||
func reportListenerCondition(index int, l k8s.Listener, obj config.Config, conditions map[string]*condition) {
|
||||
obj.Status.(*kstatus.WrappedStatus).Mutate(func(s config.Status) config.Status {
|
||||
gs := s.(*k8s.GatewayStatus)
|
||||
for index >= len(gs.Listeners) {
|
||||
gs.Listeners = append(gs.Listeners, k8s.ListenerStatus{})
|
||||
}
|
||||
cond := gs.Listeners[index].Conditions
|
||||
supported, valid := generateSupportedKinds(l)
|
||||
if !valid {
|
||||
conditions[string(k8sbeta.ListenerConditionResolvedRefs)] = &condition{
|
||||
reason: string(k8sbeta.ListenerReasonInvalidRouteKinds),
|
||||
status: metav1.ConditionFalse,
|
||||
message: "Invalid route kinds",
|
||||
}
|
||||
}
|
||||
gs.Listeners[index] = k8s.ListenerStatus{
|
||||
Name: l.Name,
|
||||
AttachedRoutes: 0, // this will be reported later
|
||||
SupportedKinds: supported,
|
||||
Conditions: setConditions(obj.Generation, cond, conditions),
|
||||
}
|
||||
return gs
|
||||
})
|
||||
}
|
||||
statusListeners[index] = k8s.ListenerStatus{
|
||||
Name: l.Name,
|
||||
AttachedRoutes: 0, // this will be reported later
|
||||
SupportedKinds: supported,
|
||||
Conditions: setConditions(obj.GetGeneration(), cond, conditions),
|
||||
}
|
||||
return statusListeners
|
||||
}
|
||||
|
||||
func generateSupportedKinds(l k8s.Listener) ([]k8s.RouteGroupKind, bool) {
|
||||
supported := []k8s.RouteGroupKind{}
|
||||
switch l.Protocol {
|
||||
case k8sbeta.HTTPProtocolType, k8sbeta.HTTPSProtocolType:
|
||||
case k8s.HTTPProtocolType, k8s.HTTPSProtocolType:
|
||||
// Only terminate allowed, so its always HTTP
|
||||
supported = []k8s.RouteGroupKind{{Group: (*k8s.Group)(ptr.Of(gvk.HTTPRoute.Group)), Kind: k8s.Kind(gvk.HTTPRoute.Kind)}}
|
||||
case k8sbeta.TCPProtocolType:
|
||||
supported = []k8s.RouteGroupKind{{Group: (*k8s.Group)(ptr.Of(gvk.TCPRoute.Group)), Kind: k8s.Kind(gvk.TCPRoute.Kind)}}
|
||||
case k8sbeta.TLSProtocolType:
|
||||
if l.TLS != nil && l.TLS.Mode != nil && *l.TLS.Mode == k8sbeta.TLSModePassthrough {
|
||||
supported = []k8s.RouteGroupKind{{Group: (*k8s.Group)(ptr.Of(gvk.TLSRoute.Group)), Kind: k8s.Kind(gvk.TLSRoute.Kind)}}
|
||||
} else {
|
||||
supported = []k8s.RouteGroupKind{{Group: (*k8s.Group)(ptr.Of(gvk.TCPRoute.Group)), Kind: k8s.Kind(gvk.TCPRoute.Kind)}}
|
||||
supported = []k8s.RouteGroupKind{
|
||||
toRouteKind(gvk.HTTPRoute),
|
||||
toRouteKind(gvk.GRPCRoute),
|
||||
}
|
||||
// UDP route note support
|
||||
case k8s.TCPProtocolType:
|
||||
supported = []k8s.RouteGroupKind{toRouteKind(gvk.TCPRoute)}
|
||||
case k8s.TLSProtocolType:
|
||||
if l.TLS != nil && l.TLS.Mode != nil && *l.TLS.Mode == k8s.TLSModePassthrough {
|
||||
supported = []k8s.RouteGroupKind{toRouteKind(gvk.TLSRoute)}
|
||||
} else {
|
||||
supported = []k8s.RouteGroupKind{toRouteKind(gvk.TCPRoute)}
|
||||
}
|
||||
// UDP route not support
|
||||
}
|
||||
if l.AllowedRoutes != nil && len(l.AllowedRoutes.Kinds) > 0 {
|
||||
// We need to filter down to only ones we actually support
|
||||
@@ -352,11 +409,15 @@ func generateSupportedKinds(l k8s.Listener) ([]k8s.RouteGroupKind, bool) {
|
||||
return supported, true
|
||||
}
|
||||
|
||||
// This and the following function really belongs in some gateway-api lib
|
||||
func routeGroupKindEqual(rgk1, rgk2 k8s.RouteGroupKind) bool {
|
||||
return rgk1.Kind == rgk2.Kind && getGroup(rgk1) == getGroup(rgk2)
|
||||
}
|
||||
func FilterInPlaceByIndex[E any](s []E, keep func(int) bool) []E {
|
||||
i := 0
|
||||
for j := 0; j < len(s); j++ {
|
||||
if keep(j) {
|
||||
s[i] = s[j]
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
func getGroup(rgk k8s.RouteGroupKind) k8s.Group {
|
||||
return ptr.OrDefault(rgk.Group, k8s.Group(gvk.KubernetesGateway.Group))
|
||||
clear(s[i:]) // zero/nil out the obsolete elements, for GC
|
||||
return s[:i]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user