diff --git a/api/adc/types.go b/api/adc/types.go index 39ebbf336..1c561fd19 100644 --- a/api/adc/types.go +++ b/api/adc/types.go @@ -126,9 +126,9 @@ type Route struct { } type Timeout struct { - Connect float64 `json:"connect"` - Read float64 `json:"read"` - Send float64 `json:"send"` + Connect int `json:"connect"` + Read int `json:"read"` + Send int `json:"send"` } type StreamRoute struct { @@ -149,7 +149,7 @@ type Upstream struct { HashOn string `json:"hash_on,omitempty" yaml:"hash_on,omitempty"` Key string `json:"key,omitempty" yaml:"key,omitempty"` Nodes UpstreamNodes `json:"nodes" yaml:"nodes"` - PassHost *PassHost `json:"pass_host,omitempty" yaml:"pass_host,omitempty"` + PassHost string `json:"pass_host,omitempty" yaml:"pass_host,omitempty"` Retries *int64 `json:"retries,omitempty" yaml:"retries,omitempty"` RetryTimeout *float64 `json:"retry_timeout,omitempty" yaml:"retry_timeout,omitempty"` Scheme string `json:"scheme,omitempty" yaml:"scheme,omitempty"` @@ -202,14 +202,6 @@ const ( Trace Method = "TRACE" ) -type PassHost string - -const ( - Node PassHost = "node" - Pass PassHost = "pass" - Rewrite PassHost = "rewrite" -) - type Scheme string const ( diff --git a/api/v1alpha1/backendtrafficpolicy_types.go b/api/v1alpha1/backendtrafficpolicy_types.go index 3d438349d..9e2f1d756 100644 --- a/api/v1alpha1/backendtrafficpolicy_types.go +++ b/api/v1alpha1/backendtrafficpolicy_types.go @@ -44,11 +44,11 @@ type BackendTrafficPolicySpec struct { // // +kubebuilder:validation:Enum=pass;node;rewrite; // +kubebuilder:default=pass - PassHost string `json:"pass_host,omitempty" yaml:"pass_host,omitempty"` + PassHost string `json:"passHost,omitempty" yaml:"passHost,omitempty"` // Specifies the host of the Upstream request. This is only valid if - // the pass_host is set to rewrite - Host Hostname `json:"upstream_host,omitempty" yaml:"upstream_host,omitempty"` + // the passHost is set to rewrite + Host Hostname `json:"upstreamHost,omitempty" yaml:"upstreamHost,omitempty"` } // LoadBalancer describes the load balancing parameters. @@ -69,10 +69,16 @@ type LoadBalancer struct { type Timeout struct { // +kubebuilder:default="60s" + // +kubebuilder:validation:Pattern=`^[0-9]+s$` + // +kubebuilder:validation:Type=string Connect metav1.Duration `json:"connect,omitempty" yaml:"connect,omitempty"` // +kubebuilder:default="60s" + // +kubebuilder:validation:Pattern=`^[0-9]+s$` + // +kubebuilder:validation:Type=string Send metav1.Duration `json:"send,omitempty" yaml:"send,omitempty"` // +kubebuilder:default="60s" + // +kubebuilder:validation:Pattern=`^[0-9]+s$` + // +kubebuilder:validation:Type=string Read metav1.Duration `json:"read,omitempty" yaml:"read,omitempty"` } diff --git a/config/crd/bases/gateway.apisix.io_backendtrafficpolicies.yaml b/config/crd/bases/gateway.apisix.io_backendtrafficpolicies.yaml index 99e2fdea4..f26471b96 100644 --- a/config/crd/bases/gateway.apisix.io_backendtrafficpolicies.yaml +++ b/config/crd/bases/gateway.apisix.io_backendtrafficpolicies.yaml @@ -70,7 +70,7 @@ spec: type: object x-kubernetes-validations: - rule: '!(has(self.key) && self.type != ''chash'')' - pass_host: + passHost: default: pass description: |- Configures the host when the request is forwarded to the upstream. @@ -164,18 +164,21 @@ spec: properties: connect: default: 60s + pattern: ^[0-9]+s$ type: string read: default: 60s + pattern: ^[0-9]+s$ type: string send: default: 60s + pattern: ^[0-9]+s$ type: string type: object - upstream_host: + upstreamHost: description: |- Specifies the host of the Upstream request. This is only valid if - the pass_host is set to rewrite + the passHost is set to rewrite maxLength: 253 minLength: 1 pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ diff --git a/internal/controller/gateway_controller.go b/internal/controller/gateway_controller.go index cc67d2cd0..f6e157d24 100644 --- a/internal/controller/gateway_controller.go +++ b/internal/controller/gateway_controller.go @@ -75,6 +75,7 @@ func (r *GatewayReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct if err := r.Provider.Delete(ctx, gateway); err != nil { return ctrl.Result{}, err } + return ctrl.Result{}, nil } return ctrl.Result{}, err } @@ -234,6 +235,9 @@ func (r *GatewayReconciler) listGatewaysForGatewayProxy(ctx context.Context, obj recs := make([]reconcile.Request, 0, len(gatewayList.Items)) for _, gateway := range gatewayList.Items { + if !r.checkGatewayClass(&gateway) { + continue + } recs = append(recs, reconcile.Request{ NamespacedName: client.ObjectKey{ Namespace: gateway.GetNamespace(), @@ -244,7 +248,7 @@ func (r *GatewayReconciler) listGatewaysForGatewayProxy(ctx context.Context, obj return recs } -func (r *GatewayReconciler) listGatewaysForHTTPRoute(_ context.Context, obj client.Object) []reconcile.Request { +func (r *GatewayReconciler) listGatewaysForHTTPRoute(ctx context.Context, obj client.Object) []reconcile.Request { httpRoute, ok := obj.(*gatewayv1.HTTPRoute) if !ok { r.Log.Error( @@ -268,6 +272,18 @@ func (r *GatewayReconciler) listGatewaysForHTTPRoute(_ context.Context, obj clie gatewayNamespace = string(*parentRef.Namespace) } + gateway := new(gatewayv1.Gateway) + if err := r.Get(ctx, client.ObjectKey{ + Namespace: gatewayNamespace, + Name: string(parentRef.Name), + }, gateway); err != nil { + continue + } + + if !r.checkGatewayClass(gateway) { + continue + } + recs = append(recs, reconcile.Request{ NamespacedName: client.ObjectKey{ Namespace: gatewayNamespace, diff --git a/internal/controller/httproute_controller.go b/internal/controller/httproute_controller.go index fce1dc2d3..361735c45 100644 --- a/internal/controller/httproute_controller.go +++ b/internal/controller/httproute_controller.go @@ -18,6 +18,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" "github.com/api7/api7-ingress-controller/api/v1alpha1" @@ -33,10 +34,14 @@ type HTTPRouteReconciler struct { //nolint:revive Log logr.Logger Provider provider.Provider + + genericEvent chan event.GenericEvent } // SetupWithManager sets up the controller with the Manager. func (r *HTTPRouteReconciler) SetupWithManager(mgr ctrl.Manager) error { + r.genericEvent = make(chan event.GenericEvent, 100) + return ctrl.NewControllerManagedBy(mgr). For(&gatewayv1.HTTPRoute{}). WithEventFilter(predicate.GenerationChangedPredicate{}). @@ -65,9 +70,100 @@ func (r *HTTPRouteReconciler) SetupWithManager(mgr ctrl.Manager) error { }, ), ). + Watches(&v1alpha1.BackendTrafficPolicy{}, + handler.EnqueueRequestsFromMapFunc(r.listHTTPRoutesForBackendTrafficPolicy), + builder.WithPredicates( + predicate.Funcs{ + GenericFunc: func(e event.GenericEvent) bool { + return false + }, + DeleteFunc: func(e event.DeleteEvent) bool { + return true + }, + CreateFunc: func(e event.CreateEvent) bool { + return true + }, + UpdateFunc: func(e event.UpdateEvent) bool { + oldObj, ok := e.ObjectOld.(*v1alpha1.BackendTrafficPolicy) + newObj, ok2 := e.ObjectNew.(*v1alpha1.BackendTrafficPolicy) + if !ok || !ok2 { + return false + } + oldRefs := oldObj.Spec.TargetRefs + newRefs := newObj.Spec.TargetRefs + + oldRefMap := make(map[string]v1alpha1.BackendPolicyTargetReferenceWithSectionName) + for _, ref := range oldRefs { + key := fmt.Sprintf("%s/%s/%s", ref.Group, ref.Kind, ref.Name) + oldRefMap[key] = ref + } + + for _, ref := range newRefs { + key := fmt.Sprintf("%s/%s/%s", ref.Group, ref.Kind, ref.Name) + delete(oldRefMap, key) + } + if len(oldRefMap) > 0 { + targetRefs := make([]v1alpha1.BackendPolicyTargetReferenceWithSectionName, 0, len(oldRefs)) + for _, ref := range oldRefMap { + targetRefs = append(targetRefs, ref) + } + dump := oldObj.DeepCopy() + dump.Spec.TargetRefs = targetRefs + r.genericEvent <- event.GenericEvent{ + Object: dump, + } + } + return true + }, + }, + ), + ). + WatchesRawSource( + source.Channel( + r.genericEvent, + handler.EnqueueRequestsFromMapFunc(r.listHTTPRouteForGenericEvent), + ), + ). Complete(r) } +func (r *HTTPRouteReconciler) listHTTPRouteForGenericEvent(ctx context.Context, obj client.Object) []reconcile.Request { + var namespacedNameMap = make(map[types.NamespacedName]struct{}) + requests := []reconcile.Request{} + switch v := obj.(type) { + case *v1alpha1.BackendTrafficPolicy: + httprouteAll := []gatewayv1.HTTPRoute{} + for _, ref := range v.Spec.TargetRefs { + httprouteList := &gatewayv1.HTTPRouteList{} + if err := r.List(ctx, httprouteList, client.MatchingFields{ + indexer.ServiceIndexRef: indexer.GenIndexKey(v.GetNamespace(), string(ref.Name)), + }); err != nil { + r.Log.Error(err, "failed to list HTTPRoutes for BackendTrafficPolicy", "namespace", v.GetNamespace(), "ref", ref.Name) + return nil + } + httprouteAll = append(httprouteAll, httprouteList.Items...) + } + for _, hr := range httprouteAll { + key := types.NamespacedName{ + Namespace: hr.Namespace, + Name: hr.Name, + } + if _, ok := namespacedNameMap[key]; !ok { + namespacedNameMap[key] = struct{}{} + requests = append(requests, reconcile.Request{ + NamespacedName: client.ObjectKey{ + Namespace: hr.Namespace, + Name: hr.Name, + }, + }) + } + } + default: + r.Log.Error(fmt.Errorf("unexpected object type"), "failed to convert object to BackendTrafficPolicy") + } + return requests +} + func (r *HTTPRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { hr := new(gatewayv1.HTTPRoute) if err := r.Get(ctx, req.NamespacedName, hr); err != nil { @@ -114,6 +210,7 @@ func (r *HTTPRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( tctx := provider.NewDefaultTranslateContext(ctx) + tctx.RouteParentRefs = hr.Spec.ParentRefs rk := provider.ResourceKind{ Kind: hr.Kind, Namespace: hr.Namespace, @@ -138,6 +235,8 @@ func (r *HTTPRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( } } + ProcessBackendTrafficPolicy(r.Client, r.Log, tctx) + if err := r.Provider.Update(ctx, tctx, hr); err != nil { acceptStatus.status = false acceptStatus.msg = err.Error() @@ -161,6 +260,7 @@ func (r *HTTPRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( if err := r.Status().Update(ctx, hr); err != nil { return ctrl.Result{}, err } + UpdateStatus(r.Client, r.Log, tctx) return ctrl.Result{}, nil } @@ -220,6 +320,54 @@ func (r *HTTPRouteReconciler) listHTTPRoutesByExtensionRef(ctx context.Context, return requests } +func (r *HTTPRouteReconciler) listHTTPRoutesForBackendTrafficPolicy(ctx context.Context, obj client.Object) []reconcile.Request { + policy, ok := obj.(*v1alpha1.BackendTrafficPolicy) + if !ok { + r.Log.Error(fmt.Errorf("unexpected object type"), "failed to convert object to BackendTrafficPolicy") + return nil + } + + httprouteList := []gatewayv1.HTTPRoute{} + for _, targetRef := range policy.Spec.TargetRefs { + service := &corev1.Service{} + if err := r.Get(ctx, client.ObjectKey{ + Namespace: policy.Namespace, + Name: string(targetRef.Name), + }, service); err != nil { + if client.IgnoreNotFound(err) != nil { + r.Log.Error(err, "failed to get service", "namespace", policy.Namespace, "name", targetRef.Name) + } + continue + } + hrList := &gatewayv1.HTTPRouteList{} + if err := r.List(ctx, hrList, client.MatchingFields{ + indexer.ServiceIndexRef: indexer.GenIndexKey(policy.Namespace, string(targetRef.Name)), + }); err != nil { + r.Log.Error(err, "failed to list httproutes by service reference", "service", targetRef.Name) + return nil + } + httprouteList = append(httprouteList, hrList.Items...) + } + var namespacedNameMap = make(map[types.NamespacedName]struct{}) + requests := make([]reconcile.Request, 0, len(httprouteList)) + for _, hr := range httprouteList { + key := types.NamespacedName{ + Namespace: hr.Namespace, + Name: hr.Name, + } + if _, ok := namespacedNameMap[key]; !ok { + namespacedNameMap[key] = struct{}{} + requests = append(requests, reconcile.Request{ + NamespacedName: client.ObjectKey{ + Namespace: hr.Namespace, + Name: hr.Name, + }, + }) + } + } + return requests +} + func (r *HTTPRouteReconciler) listHTTPRoutesForGateway(ctx context.Context, obj client.Object) []reconcile.Request { gateway, ok := obj.(*gatewayv1.Gateway) if !ok { diff --git a/internal/controller/indexer/indexer.go b/internal/controller/indexer/indexer.go index 2709793d1..f245e2374 100644 --- a/internal/controller/indexer/indexer.go +++ b/internal/controller/indexer/indexer.go @@ -37,11 +37,9 @@ func SetupIndexer(mgr ctrl.Manager) error { if err := setupConsumerIndexer(mgr); err != nil { return err } - /* - if err := setupBackendTrafficPolicyIndexer(mgr); err != nil { - return err - } - */ + if err := setupBackendTrafficPolicyIndexer(mgr); err != nil { + return err + } return nil } @@ -183,7 +181,7 @@ func setupIngressIndexer(mgr ctrl.Manager) error { return nil } -func SetupBackendTrafficPolicyIndexer(mgr ctrl.Manager) error { +func setupBackendTrafficPolicyIndexer(mgr ctrl.Manager) error { if err := mgr.GetFieldIndexer().IndexField( context.Background(), &v1alpha1.BackendTrafficPolicy{}, diff --git a/internal/controller/policies.go b/internal/controller/policies.go index ee1770fa8..0ac10bfc9 100644 --- a/internal/controller/policies.go +++ b/internal/controller/policies.go @@ -3,6 +3,7 @@ package controller import ( "fmt" + "k8s.io/utils/ptr" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" @@ -11,6 +12,7 @@ import ( "github.com/api7/api7-ingress-controller/internal/controller/indexer" "github.com/api7/api7-ingress-controller/internal/provider" "github.com/go-logr/logr" + "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" @@ -26,10 +28,14 @@ func (p PolicyTargetKey) String() string { return p.NsName.String() + "/" + p.GroupKind.String() } -func ProcessBackendTrafficPolicy(c client.Client, +func ProcessBackendTrafficPolicy( + c client.Client, log logr.Logger, - tctx *provider.TranslateContext) { - conflicts := map[string]v1alpha1.BackendTrafficPolicy{} + tctx *provider.TranslateContext, +) { + conflicts := map[string]*v1alpha1.BackendTrafficPolicy{} + servicePortNameMap := map[string]bool{} + policyMap := map[types.NamespacedName]*v1alpha1.BackendTrafficPolicy{} for _, service := range tctx.Services { backendTrafficPolicyList := &v1alpha1.BackendTrafficPolicyList{} if err := c.List(tctx, backendTrafficPolicyList, @@ -43,58 +49,61 @@ func ProcessBackendTrafficPolicy(c client.Client, if len(backendTrafficPolicyList.Items) == 0 { continue } - - portNameExist := make(map[string]bool, len(service.Spec.Ports)) for _, port := range service.Spec.Ports { - portNameExist[port.Name] = true + key := fmt.Sprintf("%s/%s/%s", service.Namespace, service.Name, port.Name) + servicePortNameMap[key] = true + } + + for _, p := range backendTrafficPolicyList.Items { + policyMap[types.NamespacedName{ + Name: p.Name, + Namespace: p.Namespace, + }] = p.DeepCopy() } - for _, policy := range backendTrafficPolicyList.Items { - targetRefs := policy.Spec.TargetRefs - updated := false - for _, targetRef := range targetRefs { - sectionName := targetRef.SectionName - key := PolicyTargetKey{ - NsName: types.NamespacedName{Namespace: service.Namespace, Name: service.Name}, - GroupKind: schema.GroupKind{Group: "", Kind: "Service"}, - } - condition := NewPolicyCondition(policy.Generation, true, "Policy has been accepted") - if sectionName != nil && !portNameExist[string(*sectionName)] { - condition = NewPolicyCondition(policy.Generation, false, fmt.Sprintf("SectionName %s not found in Service %s/%s", *sectionName, service.Namespace, service.Name)) - processPolicyStatus(&policy, tctx, condition, &updated) - continue - } - if p, ok := conflicts[key.String()]; ok && (p.Name == policy.Name && p.Namespace == policy.Namespace) { - condition = NewPolicyConflictCondition(policy.Generation, fmt.Sprintf("Unable to target Service %s/%s, because it conflicts with another BackendTrafficPolicy", service.Namespace, service.Name)) - processPolicyStatus(&policy, tctx, condition, &updated) - continue - } - conflicts[key.String()] = policy - processPolicyStatus(&policy, tctx, condition, &updated) + } + + for _, p := range policyMap { + policy := p.DeepCopy() + targetRefs := policy.Spec.TargetRefs + updated := false + for _, targetRef := range targetRefs { + sectionName := targetRef.SectionName + key := PolicyTargetKey{ + NsName: types.NamespacedName{Namespace: p.GetNamespace(), Name: string(targetRef.Name)}, + GroupKind: schema.GroupKind{Group: "", Kind: "Service"}, } - if _, ok := tctx.BackendTrafficPolicies[types.NamespacedName{ - Name: policy.Name, - Namespace: policy.Namespace, - }]; ok { + condition := NewPolicyCondition(policy.Generation, true, "Policy has been accepted") + if sectionName != nil && !servicePortNameMap[fmt.Sprintf("%s/%s/%s", policy.Namespace, string(targetRef.Name), *sectionName)] { + condition = NewPolicyCondition(policy.Generation, false, fmt.Sprintf("No section name %s found in Service %s/%s", *sectionName, policy.Namespace, targetRef.Name)) + processPolicyStatus(policy, tctx, condition, &updated) continue } - - if updated { - tctx.StatusUpdaters = append(tctx.StatusUpdaters, policy.DeepCopy()) + if _, ok := conflicts[key.String()]; ok { + condition = NewPolicyConflictCondition(policy.Generation, fmt.Sprintf("Unable to target Service %s/%s, because it conflicts with another BackendTrafficPolicy", policy.Namespace, targetRef.Name)) + processPolicyStatus(policy, tctx, condition, &updated) + continue } - - tctx.BackendTrafficPolicies[types.NamespacedName{ - Name: policy.Name, - Namespace: policy.Namespace, - }] = policy.DeepCopy() + conflicts[key.String()] = policy + processPolicyStatus(policy, tctx, condition, &updated) + } + if updated { + tctx.StatusUpdaters = append(tctx.StatusUpdaters, policy.DeepCopy()) } } + for _, policy := range conflicts { + tctx.BackendTrafficPolicies[types.NamespacedName{ + Name: policy.Name, + Namespace: policy.Namespace, + }] = policy + } } func processPolicyStatus(policy *v1alpha1.BackendTrafficPolicy, tctx *provider.TranslateContext, condition metav1.Condition, - updated *bool) { - if ok := SetAncestors(&policy.Status, tctx.ParentRefs, condition); ok { + updated *bool, +) { + if ok := SetAncestors(&policy.Status, tctx.RouteParentRefs, condition); ok { *updated = true } } @@ -115,17 +124,21 @@ func SetAncestors(status *v1alpha1.PolicyStatus, parentRefs []gatewayv1.ParentRe } func SetAncestorStatus(status *v1alpha1.PolicyStatus, ancestorStatus gatewayv1alpha2.PolicyAncestorStatus) bool { + if len(ancestorStatus.Conditions) == 0 { + return false + } + condition := ancestorStatus.Conditions[0] for _, c := range status.Ancestors { - if c.AncestorRef == ancestorStatus.AncestorRef { - if len(c.Conditions) == 0 || len(ancestorStatus.Conditions) == 0 { - c.Conditions = ancestorStatus.Conditions - return true - } - if c.Conditions[0].ObservedGeneration < ancestorStatus.Conditions[0].ObservedGeneration { - c.Conditions = ancestorStatus.Conditions - return true + if c.AncestorRef.Name == ancestorStatus.AncestorRef.Name && + ptr.Equal(c.AncestorRef.Namespace, ancestorStatus.AncestorRef.Namespace) && + ptr.Equal(c.AncestorRef.Group, ancestorStatus.AncestorRef.Group) && + ptr.Equal(c.AncestorRef.Kind, ancestorStatus.AncestorRef.Kind) && + c.ControllerName == ancestorStatus.ControllerName { + if !VerifyConditions(&c.Conditions, condition) { + return false } - return false + meta.SetStatusCondition(&c.Conditions, condition) + return true } } status.Ancestors = append(status.Ancestors, ancestorStatus) diff --git a/internal/controller/status.go b/internal/controller/status.go index cae419995..05b0d5139 100644 --- a/internal/controller/status.go +++ b/internal/controller/status.go @@ -63,6 +63,7 @@ func NewPolicyCondition(observedGeneration int64, status bool, message string) m Status: conditionStatus, Message: message, ObservedGeneration: observedGeneration, + LastTransitionTime: metav1.Now(), } } @@ -73,6 +74,7 @@ func NewPolicyConflictCondition(observedGeneration int64, message string) metav1 Status: metav1.ConditionFalse, Message: message, ObservedGeneration: observedGeneration, + LastTransitionTime: metav1.Now(), } } @@ -82,9 +84,6 @@ func UpdateStatus( tctx *provider.TranslateContext, ) { for _, obj := range tctx.StatusUpdaters { - if err := c.Status().Update(tctx, obj); err != nil { - log.Error(err, "failed to update status", "object", obj) - continue - } + _ = c.Status().Update(tctx, obj) } } diff --git a/internal/provider/adc/translator/httproute.go b/internal/provider/adc/translator/httproute.go index 7691c0fb1..812f908ef 100644 --- a/internal/provider/adc/translator/httproute.go +++ b/internal/provider/adc/translator/httproute.go @@ -287,6 +287,7 @@ func (t *Translator) TranslateHTTPRoute(tctx *provider.TranslateContext, httpRou backend.Namespace = &namespace } upNodes := t.translateBackendRef(tctx, backend.BackendRef) + t.AttachBackendTrafficPolicyToUpstream(backend.BackendRef, tctx.BackendTrafficPolicies, upstream) upstream.Nodes = append(upstream.Nodes, upNodes...) } t.attachBackendTrafficPolicyToUpstream(nil, upstream) diff --git a/internal/provider/adc/translator/policies.go b/internal/provider/adc/translator/policies.go index 1ed0c0081..a71e76bd1 100644 --- a/internal/provider/adc/translator/policies.go +++ b/internal/provider/adc/translator/policies.go @@ -33,6 +33,7 @@ func (t *Translator) attachBackendTrafficPolicyToUpstream(policy *v1alpha1.Backe if policy == nil { return } + upstream.PassHost = policy.Spec.PassHost upstream.UpstreamHost = string(policy.Spec.Host) upstream.Scheme = policy.Spec.Scheme if policy.Spec.Retries != nil { @@ -41,9 +42,9 @@ func (t *Translator) attachBackendTrafficPolicyToUpstream(policy *v1alpha1.Backe } if policy.Spec.Timeout != nil { upstream.Timeout = &adctypes.Timeout{ - Connect: policy.Spec.Timeout.Connect.Duration.Seconds(), - Read: policy.Spec.Timeout.Read.Duration.Seconds(), - Send: policy.Spec.Timeout.Send.Duration.Seconds(), + Connect: int(policy.Spec.Timeout.Connect.Duration.Seconds()), + Read: int(policy.Spec.Timeout.Read.Duration.Seconds()), + Send: int(policy.Spec.Timeout.Send.Duration.Seconds()), } } if policy.Spec.LoadBalancer != nil { diff --git a/internal/provider/provider.go b/internal/provider/provider.go index d21f82118..4c1a54f9a 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -25,7 +25,7 @@ type ResourceKind struct { type TranslateContext struct { context.Context - ParentRefs []gatewayv1.ParentReference + RouteParentRefs []gatewayv1.ParentReference BackendRefs []gatewayv1.BackendRef GatewayTLSConfig []gatewayv1.GatewayTLSConfig Credentials []v1alpha1.Credential diff --git a/test/e2e/crds/backendtrafficpolicy.go b/test/e2e/crds/backendtrafficpolicy.go new file mode 100644 index 000000000..90c82bf07 --- /dev/null +++ b/test/e2e/crds/backendtrafficpolicy.go @@ -0,0 +1,144 @@ +package gatewayapi + +import ( + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/api7/api7-ingress-controller/test/e2e/scaffold" +) + +var _ = Describe("Test BackendTrafficPolicy", func() { + s := scaffold.NewDefaultScaffold() + + var defaultGatewayProxy = ` +apiVersion: gateway.apisix.io/v1alpha1 +kind: GatewayProxy +metadata: + name: api7-proxy-config +spec: + provider: + type: ControlPlane + controlPlane: + endpoints: + - %s + auth: + type: AdminKey + adminKey: + value: "%s" +` + + var defaultGatewayClass = ` +apiVersion: gateway.networking.k8s.io/v1 +kind: GatewayClass +metadata: + name: %s +spec: + controllerName: %s +` + + var defaultGateway = ` +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: api7ee +spec: + gatewayClassName: %s + listeners: + - name: http1 + protocol: HTTP + port: 80 + infrastructure: + parametersRef: + group: gateway.apisix.io + kind: GatewayProxy + name: api7-proxy-config +` + + var defaultHTTPRoute = ` +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: httpbin +spec: + parentRefs: + - name: api7ee + hostnames: + - "httpbin.org" + rules: + - matches: + - path: + type: Exact + value: /get + - path: + type: Exact + value: /headers + backendRefs: + - name: httpbin-service-e2e-test + port: 80 +` + Context("Rewrite Upstream Host", func() { + var createUpstreamHost = ` +apiVersion: gateway.apisix.io/v1alpha1 +kind: BackendTrafficPolicy +metadata: + name: httpbin +spec: + targetRefs: + - name: httpbin-service-e2e-test + kind: Service + group: "" + passHost: rewrite + upstreamHost: httpbin.example.com +` + + var updateUpstreamHost = ` +apiVersion: gateway.apisix.io/v1alpha1 +kind: BackendTrafficPolicy +metadata: + name: httpbin +spec: + targetRefs: + - name: httpbin-service-e2e-test + kind: Service + group: "" + passHost: rewrite + upstreamHost: httpbin.update.example.com +` + + BeforeEach(func() { + s.ApplyDefaultGatewayResource(defaultGatewayProxy, defaultGatewayClass, defaultGateway, defaultHTTPRoute) + }) + It("should rewrite upstream host", func() { + s.ResourceApplied("BackendTrafficPolicy", "httpbin", createUpstreamHost, 1) + s.NewAPISIXClient(). + GET("/headers"). + WithHost("httpbin.org"). + Expect(). + Status(200). + Body().Contains("httpbin.example.com") + + s.ResourceApplied("BackendTrafficPolicy", "httpbin", updateUpstreamHost, 2) + s.NewAPISIXClient(). + GET("/headers"). + WithHost("httpbin.org"). + Expect(). + Status(200). + Body().Contains("httpbin.update.example.com") + + err := s.DeleteResourceFromString(createUpstreamHost) + Expect(err).NotTo(HaveOccurred(), "deleting BackendTrafficPolicy") + time.Sleep(5 * time.Second) + + s.NewAPISIXClient(). + GET("/headers"). + WithHost("httpbin.org"). + Expect(). + Status(200). + Body(). + NotContains("httpbin.update.example.com"). + NotContains("httpbin.example.com") + }) + }) +}) diff --git a/test/e2e/crds/consumer.go b/test/e2e/crds/consumer.go index 0c9b3542e..c8c19f6ff 100644 --- a/test/e2e/crds/consumer.go +++ b/test/e2e/crds/consumer.go @@ -1,20 +1,18 @@ package gatewayapi import ( - "fmt" "time" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "github.com/api7/api7-ingress-controller/test/e2e/framework" "github.com/api7/api7-ingress-controller/test/e2e/scaffold" ) var _ = Describe("Test Consumer", func() { s := scaffold.NewDefaultScaffold() - var gatewayProxyYaml = ` + var defaultGatewayProxy = ` apiVersion: gateway.apisix.io/v1alpha1 kind: GatewayProxy metadata: @@ -98,46 +96,6 @@ spec: port: 80 ` - var beforeEachHTTP = func() { - By("create GatewayProxy") - gatewayProxy := fmt.Sprintf(gatewayProxyYaml, framework.DashboardTLSEndpoint, s.AdminKey()) - err := s.CreateResourceFromString(gatewayProxy) - Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy") - time.Sleep(5 * time.Second) - - By("create GatewayClass") - gatewayClassName := fmt.Sprintf("api7-%d", time.Now().Unix()) - gatewayString := fmt.Sprintf(defaultGatewayClass, gatewayClassName, s.GetControllerName()) - err = s.CreateResourceFromStringWithNamespace(gatewayString, "") - Expect(err).NotTo(HaveOccurred(), "creating GatewayClass") - time.Sleep(5 * time.Second) - - By("check GatewayClass condition") - gcyaml, err := s.GetResourceYaml("GatewayClass", gatewayClassName) - Expect(err).NotTo(HaveOccurred(), "getting GatewayClass yaml") - Expect(gcyaml).To(ContainSubstring(`status: "True"`), "checking GatewayClass condition status") - Expect(gcyaml).To( - ContainSubstring("message: the gatewayclass has been accepted by the api7-ingress-controller"), - "checking GatewayClass condition message", - ) - - By("create Gateway") - err = s.CreateResourceFromString(fmt.Sprintf(defaultGateway, gatewayClassName)) - Expect(err).NotTo(HaveOccurred(), "creating Gateway") - time.Sleep(5 * time.Second) - - By("check Gateway condition") - gwyaml, err := s.GetResourceYaml("Gateway", "api7ee") - Expect(err).NotTo(HaveOccurred(), "getting Gateway yaml") - Expect(gwyaml).To(ContainSubstring(`status: "True"`), "checking Gateway condition status") - Expect(gwyaml).To( - ContainSubstring("message: the gateway has been accepted by the api7-ingress-controller"), - "checking Gateway condition message", - ) - - s.ResourceApplied("httproute", "httpbin", defaultHTTPRoute, 1) - } - Context("Consumer plugins", func() { var limitCountConsumer = ` apiVersion: gateway.apisix.io/v1alpha1 @@ -176,7 +134,9 @@ spec: key: sample-key2 ` - BeforeEach(beforeEachHTTP) + BeforeEach(func() { + s.ApplyDefaultGatewayResource(defaultGatewayProxy, defaultGatewayClass, defaultGateway, defaultHTTPRoute) + }) It("limit-count plugin", func() { s.ResourceApplied("Consumer", "consumer-sample", limitCountConsumer, 1) @@ -257,7 +217,10 @@ spec: config: key: consumer-key ` - BeforeEach(beforeEachHTTP) + + BeforeEach(func() { + s.ApplyDefaultGatewayResource(defaultGatewayProxy, defaultGatewayClass, defaultGateway, defaultHTTPRoute) + }) It("Create/Update/Delete", func() { s.ResourceApplied("Consumer", "consumer-sample", defaultCredential, 1) @@ -368,8 +331,10 @@ spec: config: key: sample-key2 ` - BeforeEach(beforeEachHTTP) + BeforeEach(func() { + s.ApplyDefaultGatewayResource(defaultGatewayProxy, defaultGatewayClass, defaultGateway, defaultHTTPRoute) + }) It("Create/Update/Delete", func() { err := s.CreateResourceFromString(keyAuthSecret) Expect(err).NotTo(HaveOccurred(), "creating key-auth secret") diff --git a/test/e2e/scaffold/k8s.go b/test/e2e/scaffold/k8s.go index e6cb5a3ad..ea44e8dfa 100644 --- a/test/e2e/scaffold/k8s.go +++ b/test/e2e/scaffold/k8s.go @@ -24,6 +24,7 @@ import ( "github.com/api7/api7-ingress-controller/pkg/dashboard" apisix "github.com/api7/api7-ingress-controller/pkg/dashboard" + "github.com/api7/api7-ingress-controller/test/e2e/framework" "github.com/gruntwork-io/terratest/modules/k8s" "github.com/gruntwork-io/terratest/modules/retry" "github.com/gruntwork-io/terratest/modules/testing" @@ -242,3 +243,48 @@ func (s *Scaffold) ResourceApplied(resourType, resourceName, resourceRaw string, ) time.Sleep(1 * time.Second) } + +func (s *Scaffold) ApplyDefaultGatewayResource( + defaultGatewayProxy string, + defaultGatewayClass string, + defaultGateway string, + defaultHTTPRoute string, +) { + By("create GatewayProxy") + gatewayProxy := fmt.Sprintf(defaultGatewayProxy, framework.DashboardTLSEndpoint, s.AdminKey()) + err := s.CreateResourceFromString(gatewayProxy) + Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy") + time.Sleep(5 * time.Second) + + By("create GatewayClass") + gatewayClassName := fmt.Sprintf("api7-%d", time.Now().Unix()) + gatewayString := fmt.Sprintf(defaultGatewayClass, gatewayClassName, s.GetControllerName()) + err = s.CreateResourceFromStringWithNamespace(gatewayString, "") + Expect(err).NotTo(HaveOccurred(), "creating GatewayClass") + time.Sleep(5 * time.Second) + + By("check GatewayClass condition") + gcyaml, err := s.GetResourceYaml("GatewayClass", gatewayClassName) + Expect(err).NotTo(HaveOccurred(), "getting GatewayClass yaml") + Expect(gcyaml).To(ContainSubstring(`status: "True"`), "checking GatewayClass condition status") + Expect(gcyaml).To( + ContainSubstring("message: the gatewayclass has been accepted by the api7-ingress-controller"), + "checking GatewayClass condition message", + ) + + By("create Gateway") + err = s.CreateResourceFromString(fmt.Sprintf(defaultGateway, gatewayClassName)) + Expect(err).NotTo(HaveOccurred(), "creating Gateway") + time.Sleep(5 * time.Second) + + By("check Gateway condition") + gwyaml, err := s.GetResourceYaml("Gateway", "api7ee") + Expect(err).NotTo(HaveOccurred(), "getting Gateway yaml") + Expect(gwyaml).To(ContainSubstring(`status: "True"`), "checking Gateway condition status") + Expect(gwyaml).To( + ContainSubstring("message: the gateway has been accepted by the api7-ingress-controller"), + "checking Gateway condition message", + ) + + s.ResourceApplied("httproute", "httpbin", defaultHTTPRoute, 1) +}