forked from knative-extensions/net-gateway-api
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Three-Phase Endpoint Probing (knative-extensions#672)
* Use the net-gateway-api's status prober * add helpers for test fixtures * sort headers based on name * test case - new backend triggers endpoint probes * test case - steady state endpoints are not ready * test case - transition to new backends * test case - route probing succeeds - ingress is ready * test case - no backend transition when hash is different * test-case - update endpoint probes when ingress has new backends * Update the status prober to work with dynamic paths Because probes on the HTTPRoute now have unique paths we no longer use a vanilla prober from knative/networking. The changes make the prober less coupled to Ingress and it requires consumers to build up a list of backends URLs to probe. The probe target lister now merges backends and URLs with their corresponding proxy pod IPs and ports. The alternative would be to look up HTTPRoutes in the informer cache but theres no guarantee this is up to date. * re-enable update tests, run codegen * Add a third phase to the probing We can't just drop the probing rules and move the backends into the main rules. This still results in 503s with Contour. Instead we now move the newer backends into the main rules and then once that 'succeeds' we drop the probes. * test case - add steady state reconcile while we are transition probing * test case - handle backend updates * fix boilerplate * Add test coverage for resources functions
- Loading branch information
Showing
13 changed files
with
2,261 additions
and
644 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,232 @@ | ||
/* | ||
Copyright 2021 The Knative Authors | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
package ingress | ||
|
||
import ( | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/utils/ptr" | ||
"knative.dev/networking/pkg/apis/networking" | ||
"knative.dev/networking/pkg/http/header" | ||
gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" | ||
gatewayapi "sigs.k8s.io/gateway-api/apis/v1beta1" | ||
) | ||
|
||
type RuleBuilder interface { | ||
Build() gatewayapi.HTTPRouteRule | ||
} | ||
|
||
type HTTPRoute struct { | ||
Namespace string | ||
Name string | ||
Hostnames []string | ||
Hostname string | ||
Rules []RuleBuilder | ||
StatusConditions []metav1.Condition | ||
} | ||
|
||
func (r HTTPRoute) Build() *gatewayapi.HTTPRoute { | ||
hostnames := r.Hostnames | ||
|
||
if len(hostnames) == 0 && r.Hostname == "" { | ||
hostnames = []string{"example.com"} | ||
} | ||
|
||
if r.Hostname != "" { | ||
hostnames = append(hostnames, r.Hostname) | ||
} | ||
|
||
route := gatewayapi.HTTPRoute{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: r.Name, | ||
Namespace: r.Namespace, | ||
Annotations: map[string]string{ | ||
networking.IngressClassAnnotationKey: gatewayAPIIngressClassName, | ||
}, | ||
Labels: map[string]string{ | ||
networking.VisibilityLabelKey: "", | ||
}, | ||
OwnerReferences: []metav1.OwnerReference{{ | ||
APIVersion: "networking.internal.knative.dev/v1alpha1", | ||
Kind: "Ingress", | ||
Name: "name", | ||
Controller: ptr.To(true), | ||
BlockOwnerDeletion: ptr.To(true), | ||
}}, | ||
}, | ||
Spec: gatewayapi.HTTPRouteSpec{ | ||
CommonRouteSpec: gatewayapi.CommonRouteSpec{ | ||
ParentRefs: []gatewayapi.ParentReference{{ | ||
Group: ptr.To[gatewayapi.Group]("gateway.networking.k8s.io"), | ||
Kind: ptr.To[gatewayapi.Kind]("Gateway"), | ||
Namespace: ptr.To[gatewayapi.Namespace]("istio-system"), | ||
Name: "istio-gateway", | ||
}}, | ||
}, | ||
}, | ||
} | ||
|
||
for _, hostname := range hostnames { | ||
route.Spec.Hostnames = append( | ||
route.Spec.Hostnames, | ||
gatewayapi.Hostname(hostname), | ||
) | ||
} | ||
|
||
if route.Status.Parents == nil { | ||
route.Status.Parents = []gatewayapi.RouteParentStatus{{}} | ||
} | ||
|
||
route.Status.RouteStatus.Parents[0].Conditions = append( | ||
route.Status.RouteStatus.Parents[0].Conditions, | ||
r.StatusConditions..., | ||
) | ||
|
||
for _, rule := range r.Rules { | ||
route.Spec.Rules = append(route.Spec.Rules, rule.Build()) | ||
} | ||
|
||
return &route | ||
} | ||
|
||
type EndpointProbeRule struct { | ||
Namespace string | ||
Name string | ||
Hash string | ||
Path string | ||
Port int | ||
Headers []string | ||
} | ||
|
||
func (p EndpointProbeRule) Build() gatewayapi.HTTPRouteRule { | ||
path := p.Path | ||
if path == "" { | ||
path = "/" | ||
} | ||
rule := gatewayapi.HTTPRouteRule{ | ||
Matches: []gatewayapi.HTTPRouteMatch{{ | ||
Path: &gatewayapi.HTTPPathMatch{ | ||
Type: ptr.To(gatewayapiv1.PathMatchPathPrefix), | ||
Value: ptr.To(path), | ||
}, | ||
Headers: []gatewayapi.HTTPHeaderMatch{{ | ||
Type: ptr.To(gatewayapiv1.HeaderMatchExact), | ||
Name: header.HashKey, | ||
Value: header.HashValueOverride, | ||
}}, | ||
}}, | ||
Filters: []gatewayapi.HTTPRouteFilter{{ | ||
Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, | ||
RequestHeaderModifier: &gatewayapi.HTTPHeaderFilter{ | ||
Set: []gatewayapi.HTTPHeader{{ | ||
Name: header.HashKey, | ||
Value: p.Hash, | ||
}}, | ||
}, | ||
}}, | ||
BackendRefs: []gatewayapi.HTTPBackendRef{{ | ||
Filters: []gatewayapiv1.HTTPRouteFilter{{ | ||
Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, | ||
RequestHeaderModifier: &gatewayapi.HTTPHeaderFilter{ | ||
Set: []gatewayapi.HTTPHeader{{ | ||
Name: "K-Serving-Namespace", | ||
Value: p.Namespace, | ||
}, { | ||
Name: "K-Serving-Revision", | ||
Value: p.Name, | ||
}}, | ||
}, | ||
}}, | ||
BackendRef: gatewayapi.BackendRef{ | ||
Weight: ptr.To[int32](100), | ||
BackendObjectReference: gatewayapiv1.BackendObjectReference{ | ||
Group: ptr.To[gatewayapi.Group](""), | ||
Kind: ptr.To[gatewayapi.Kind]("Service"), | ||
Name: gatewayapi.ObjectName(p.Name), | ||
Port: ptr.To[gatewayapi.PortNumber](gatewayapi.PortNumber(p.Port)), | ||
}, | ||
}, | ||
}}, | ||
} | ||
|
||
for i := 0; i < len(p.Headers); i += 2 { | ||
k, v := p.Headers[i], p.Headers[i+1] | ||
rule.BackendRefs[0].Filters[0].RequestHeaderModifier.Set = append( | ||
rule.BackendRefs[0].Filters[0].RequestHeaderModifier.Set, | ||
gatewayapi.HTTPHeader{Name: gatewayapiv1.HTTPHeaderName(k), Value: v}, | ||
) | ||
} | ||
|
||
return rule | ||
} | ||
|
||
type NormalRule struct { | ||
Namespace string | ||
Name string | ||
Path string | ||
Port int | ||
Headers []string | ||
Weight int | ||
} | ||
|
||
func (p NormalRule) Build() gatewayapi.HTTPRouteRule { | ||
path := p.Path | ||
if path == "" { | ||
path = "/" | ||
} | ||
rule := gatewayapi.HTTPRouteRule{ | ||
Matches: []gatewayapi.HTTPRouteMatch{{ | ||
Path: &gatewayapi.HTTPPathMatch{ | ||
Type: ptr.To(gatewayapiv1.PathMatchPathPrefix), | ||
Value: ptr.To(path), | ||
}, | ||
}}, | ||
BackendRefs: []gatewayapi.HTTPBackendRef{{ | ||
Filters: []gatewayapiv1.HTTPRouteFilter{{ | ||
Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, | ||
RequestHeaderModifier: &gatewayapi.HTTPHeaderFilter{ | ||
Set: []gatewayapi.HTTPHeader{{ | ||
Name: "K-Serving-Namespace", | ||
Value: p.Namespace, | ||
}, { | ||
Name: "K-Serving-Revision", | ||
Value: p.Name, | ||
}}, | ||
}, | ||
}}, | ||
BackendRef: gatewayapi.BackendRef{ | ||
BackendObjectReference: gatewayapiv1.BackendObjectReference{ | ||
Group: ptr.To[gatewayapi.Group](""), | ||
Kind: ptr.To[gatewayapi.Kind]("Service"), | ||
Name: gatewayapi.ObjectName(p.Name), | ||
Port: ptr.To[gatewayapi.PortNumber](gatewayapi.PortNumber(p.Port)), | ||
}, | ||
Weight: ptr.To[int32](int32(p.Weight)), | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
for i := 0; i < len(p.Headers); i += 2 { | ||
k, v := p.Headers[i], p.Headers[i+1] | ||
rule.BackendRefs[0].Filters[0].RequestHeaderModifier.Set = append( | ||
rule.BackendRefs[0].Filters[0].RequestHeaderModifier.Set, | ||
gatewayapi.HTTPHeader{Name: gatewayapiv1.HTTPHeaderName(k), Value: v}, | ||
) | ||
} | ||
|
||
return rule | ||
} |
Oops, something went wrong.