diff --git a/api/v1alpha1/backendtrafficpolicy_types.go b/api/v1alpha1/backendtrafficpolicy_types.go index 2bdf5b553..3d438349d 100644 --- a/api/v1alpha1/backendtrafficpolicy_types.go +++ b/api/v1alpha1/backendtrafficpolicy_types.go @@ -2,8 +2,6 @@ package v1alpha1 import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" ) // +kubebuilder:object:root=true @@ -12,8 +10,8 @@ type BackendTrafficPolicy struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec BackendTrafficPolicySpec `json:"spec,omitempty"` - Status gatewayv1alpha2.PolicyStatus `json:"status,omitempty"` + Spec BackendTrafficPolicySpec `json:"spec,omitempty"` + Status PolicyStatus `json:"status,omitempty"` } type BackendTrafficPolicySpec struct { @@ -21,13 +19,9 @@ type BackendTrafficPolicySpec struct { // Currently, Backends (i.e. Service, ServiceImport, or any // implementation-specific backendRef) are the only valid API // target references. - // +listType=map - // +listMapKey=group - // +listMapKey=kind - // +listMapKey=name // +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MaxItems=16 - TargetRefs []gatewayv1alpha2.LocalPolicyTargetReferenceWithSectionName `json:"targetRefs"` + TargetRefs []BackendPolicyTargetReferenceWithSectionName `json:"targetRefs"` // LoadBalancer represents the load balancer configuration for Kubernetes Service. // The default strategy is round robin. LoadBalancer *LoadBalancer `json:"loadbalancer,omitempty" yaml:"loadbalancer,omitempty"` @@ -74,9 +68,12 @@ type LoadBalancer struct { } type Timeout struct { + // +kubebuilder:default="60s" Connect metav1.Duration `json:"connect,omitempty" yaml:"connect,omitempty"` - Send metav1.Duration `json:"send,omitempty" yaml:"send,omitempty"` - Read metav1.Duration `json:"read,omitempty" yaml:"read,omitempty"` + // +kubebuilder:default="60s" + Send metav1.Duration `json:"send,omitempty" yaml:"send,omitempty"` + // +kubebuilder:default="60s" + Read metav1.Duration `json:"read,omitempty" yaml:"read,omitempty"` } // +kubebuilder:object:root=true diff --git a/api/v1alpha1/policies_type.go b/api/v1alpha1/policies_type.go new file mode 100644 index 000000000..527226e4f --- /dev/null +++ b/api/v1alpha1/policies_type.go @@ -0,0 +1,8 @@ +package v1alpha1 + +import gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + +type PolicyStatus gatewayv1alpha2.PolicyStatus + +// +kubebuilder:validation:XValidation:rule="self.kind == 'Service' && self.group == \"\"" +type BackendPolicyTargetReferenceWithSectionName gatewayv1alpha2.LocalPolicyTargetReferenceWithSectionName diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 04ff10e12..8f4a5b117 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -25,6 +25,7 @@ import ( "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" + apisv1 "sigs.k8s.io/gateway-api/apis/v1" "sigs.k8s.io/gateway-api/apis/v1alpha2" ) @@ -68,6 +69,27 @@ func (in *AdminKeyValueFrom) DeepCopy() *AdminKeyValueFrom { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackendPolicyTargetReferenceWithSectionName) DeepCopyInto(out *BackendPolicyTargetReferenceWithSectionName) { + *out = *in + out.LocalPolicyTargetReference = in.LocalPolicyTargetReference + if in.SectionName != nil { + in, out := &in.SectionName, &out.SectionName + *out = new(apisv1.SectionName) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackendPolicyTargetReferenceWithSectionName. +func (in *BackendPolicyTargetReferenceWithSectionName) DeepCopy() *BackendPolicyTargetReferenceWithSectionName { + if in == nil { + return nil + } + out := new(BackendPolicyTargetReferenceWithSectionName) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *BackendTrafficPolicy) DeepCopyInto(out *BackendTrafficPolicy) { *out = *in @@ -132,7 +154,7 @@ func (in *BackendTrafficPolicySpec) DeepCopyInto(out *BackendTrafficPolicySpec) *out = *in if in.TargetRefs != nil { in, out := &in.TargetRefs, &out.TargetRefs - *out = make([]v1alpha2.LocalPolicyTargetReferenceWithSectionName, len(*in)) + *out = make([]BackendPolicyTargetReferenceWithSectionName, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -688,6 +710,28 @@ func (in *PluginConfigSpec) DeepCopy() *PluginConfigSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PolicyStatus) DeepCopyInto(out *PolicyStatus) { + *out = *in + if in.Ancestors != nil { + in, out := &in.Ancestors, &out.Ancestors + *out = make([]v1alpha2.PolicyAncestorStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PolicyStatus. +func (in *PolicyStatus) DeepCopy() *PolicyStatus { + if in == nil { + return nil + } + out := new(PolicyStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SecretKeySelector) DeepCopyInto(out *SecretKeySelector) { *out = *in diff --git a/config/crd/bases/gateway.apisix.io_backendtrafficpolicies.yaml b/config/crd/bases/gateway.apisix.io_backendtrafficpolicies.yaml index 3e325bf7f..99e2fdea4 100644 --- a/config/crd/bases/gateway.apisix.io_backendtrafficpolicies.yaml +++ b/config/crd/bases/gateway.apisix.io_backendtrafficpolicies.yaml @@ -153,23 +153,23 @@ spec: - kind - name type: object + x-kubernetes-validations: + - rule: self.kind == 'Service' && self.group == "" maxItems: 16 minItems: 1 type: array - x-kubernetes-list-map-keys: - - group - - kind - - name - x-kubernetes-list-type: map timeout: description: Timeout settings for the read, send and connect to the upstream. properties: connect: + default: 60s type: string read: + default: 60s type: string send: + default: 60s type: string type: object upstream_host: diff --git a/internal/controller/consumer_controller.go b/internal/controller/consumer_controller.go index 1d5054aca..2ef6e30e2 100644 --- a/internal/controller/consumer_controller.go +++ b/internal/controller/consumer_controller.go @@ -136,7 +136,7 @@ func (r *ConsumerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c } var statusErr error - tctx := provider.NewDefaultTranslateContext() + tctx := provider.NewDefaultTranslateContext(ctx) if err := r.processSpec(ctx, tctx, consumer); err != nil { r.Log.Error(err, "failed to process consumer spec", "consumer", consumer) diff --git a/internal/controller/httproute_controller.go b/internal/controller/httproute_controller.go index afe21a884..bc33dd79b 100644 --- a/internal/controller/httproute_controller.go +++ b/internal/controller/httproute_controller.go @@ -112,7 +112,7 @@ func (r *HTTPRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( return ctrl.Result{}, nil } - tctx := provider.NewDefaultTranslateContext() + tctx := provider.NewDefaultTranslateContext(ctx) if err := r.processHTTPRoute(tctx, hr); err != nil { acceptStatus.status = false @@ -249,7 +249,7 @@ func (r *HTTPRouteReconciler) processHTTPRouteBackendRefs(tctx *provider.Transla } var service corev1.Service - if err := r.Get(context.TODO(), client.ObjectKey{ + if err := r.Get(tctx, client.ObjectKey{ Namespace: namespace, Name: name, }, &service); err != nil { @@ -268,9 +268,13 @@ func (r *HTTPRouteReconciler) processHTTPRouteBackendRefs(tctx *provider.Transla terr = fmt.Errorf("port %d not found in service %s", *backend.Port, name) continue } + tctx.Services[client.ObjectKey{ + Namespace: namespace, + Name: name, + }] = &service endpointSliceList := new(discoveryv1.EndpointSliceList) - if err := r.List(context.TODO(), endpointSliceList, + if err := r.List(tctx, endpointSliceList, client.InNamespace(namespace), client.MatchingLabels{ discoveryv1.LabelServiceName: name, diff --git a/internal/controller/indexer/indexer.go b/internal/controller/indexer/indexer.go index 5b20ea121..2709793d1 100644 --- a/internal/controller/indexer/indexer.go +++ b/internal/controller/indexer/indexer.go @@ -5,6 +5,8 @@ import ( "github.com/api7/api7-ingress-controller/api/v1alpha1" networkingv1 "k8s.io/api/networking/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" @@ -19,6 +21,7 @@ const ( SecretIndexRef = "secretRefs" IngressClassRef = "ingressClassRef" ConsumerGatewayRef = "consumerGatewayRef" + PolicyTargetRefs = "targetRefs" ) func SetupIndexer(mgr ctrl.Manager) error { @@ -34,6 +37,11 @@ func SetupIndexer(mgr ctrl.Manager) error { if err := setupConsumerIndexer(mgr); err != nil { return err } + /* + if err := setupBackendTrafficPolicyIndexer(mgr); err != nil { + return err + } + */ return nil } @@ -175,6 +183,18 @@ func setupIngressIndexer(mgr ctrl.Manager) error { return nil } +func SetupBackendTrafficPolicyIndexer(mgr ctrl.Manager) error { + if err := mgr.GetFieldIndexer().IndexField( + context.Background(), + &v1alpha1.BackendTrafficPolicy{}, + PolicyTargetRefs, + BackendTrafficPolicyIndexFunc, + ); err != nil { + return err + } + return nil +} + func IngressClassIndexFunc(rawObj client.Object) []string { ingressClass := rawObj.(*networkingv1.IngressClass) if ingressClass.Spec.Controller == "" { @@ -227,6 +247,18 @@ func IngressSecretIndexFunc(rawObj client.Object) []string { return secrets } +func GenIndexKeyWithGK(group, kind, namespace, name string) string { + gvk := schema.GroupKind{ + Group: group, + Kind: kind, + } + nsName := types.NamespacedName{ + Namespace: namespace, + Name: name, + } + return gvk.String() + "/" + nsName.String() +} + func GenIndexKey(namespace, name string) string { return client.ObjectKey{ Namespace: namespace, @@ -292,3 +324,19 @@ func GatewayParametersRefIndexFunc(rawObj client.Object) []string { } return nil } + +func BackendTrafficPolicyIndexFunc(rawObj client.Object) []string { + btp := rawObj.(*v1alpha1.BackendTrafficPolicy) + keys := make([]string, 0, len(btp.Spec.TargetRefs)) + for _, ref := range btp.Spec.TargetRefs { + keys = append(keys, + GenIndexKeyWithGK( + string(ref.Group), + string(ref.Kind), + btp.GetNamespace(), + string(ref.Name), + ), + ) + } + return keys +} diff --git a/internal/controller/ingress_controller.go b/internal/controller/ingress_controller.go index 2e1636eed..ac22a0ea7 100644 --- a/internal/controller/ingress_controller.go +++ b/internal/controller/ingress_controller.go @@ -93,16 +93,16 @@ func (r *IngressReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct r.Log.Info("reconciling ingress", "ingress", ingress.Name) // create a translate context - tctx := provider.NewDefaultTranslateContext() + tctx := provider.NewDefaultTranslateContext(ctx) // process TLS configuration - if err := r.processTLS(ctx, tctx, ingress); err != nil { + if err := r.processTLS(tctx, ingress); err != nil { r.Log.Error(err, "failed to process TLS configuration", "ingress", ingress.Name) return ctrl.Result{}, err } // process backend services - if err := r.processBackends(ctx, tctx, ingress); err != nil { + if err := r.processBackends(tctx, ingress); err != nil { r.Log.Error(err, "failed to process backend services", "ingress", ingress.Name) return ctrl.Result{}, err } @@ -295,14 +295,14 @@ func (r *IngressReconciler) listIngressesBySecret(ctx context.Context, obj clien } // processTLS process the TLS configuration of the ingress -func (r *IngressReconciler) processTLS(ctx context.Context, tctx *provider.TranslateContext, ingress *networkingv1.Ingress) error { +func (r *IngressReconciler) processTLS(tctx *provider.TranslateContext, ingress *networkingv1.Ingress) error { for _, tls := range ingress.Spec.TLS { if tls.SecretName == "" { continue } secret := corev1.Secret{} - if err := r.Get(ctx, client.ObjectKey{ + if err := r.Get(tctx, client.ObjectKey{ Namespace: ingress.Namespace, Name: tls.SecretName, }, &secret); err != nil { @@ -323,7 +323,7 @@ func (r *IngressReconciler) processTLS(ctx context.Context, tctx *provider.Trans } // processBackends process the backend services of the ingress -func (r *IngressReconciler) processBackends(ctx context.Context, tctx *provider.TranslateContext, ingress *networkingv1.Ingress) error { +func (r *IngressReconciler) processBackends(tctx *provider.TranslateContext, ingress *networkingv1.Ingress) error { var terr error // process all the backend services in the rules @@ -336,7 +336,7 @@ func (r *IngressReconciler) processBackends(ctx context.Context, tctx *provider. continue } service := path.Backend.Service - if err := r.processBackendService(ctx, tctx, ingress.Namespace, service); err != nil { + if err := r.processBackendService(tctx, ingress.Namespace, service); err != nil { terr = err } } @@ -345,10 +345,10 @@ func (r *IngressReconciler) processBackends(ctx context.Context, tctx *provider. } // processBackendService process a single backend service -func (r *IngressReconciler) processBackendService(ctx context.Context, tctx *provider.TranslateContext, namespace string, backendService *networkingv1.IngressServiceBackend) error { +func (r *IngressReconciler) processBackendService(tctx *provider.TranslateContext, namespace string, backendService *networkingv1.IngressServiceBackend) error { // get the service var service corev1.Service - if err := r.Get(ctx, client.ObjectKey{ + if err := r.Get(tctx, client.ObjectKey{ Namespace: namespace, Name: backendService.Name, }, &service); err != nil { @@ -385,7 +385,7 @@ func (r *IngressReconciler) processBackendService(ctx context.Context, tctx *pro // get the endpoint slices endpointSliceList := &discoveryv1.EndpointSliceList{} - if err := r.List(ctx, endpointSliceList, + if err := r.List(tctx, endpointSliceList, client.InNamespace(namespace), client.MatchingLabels{ discoveryv1.LabelServiceName: backendService.Name, diff --git a/internal/controller/policies.go b/internal/controller/policies.go new file mode 100644 index 000000000..ee1770fa8 --- /dev/null +++ b/internal/controller/policies.go @@ -0,0 +1,133 @@ +package controller + +import ( + "fmt" + + gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" + gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + + "github.com/api7/api7-ingress-controller/api/v1alpha1" + "github.com/api7/api7-ingress-controller/internal/controller/config" + "github.com/api7/api7-ingress-controller/internal/controller/indexer" + "github.com/api7/api7-ingress-controller/internal/provider" + "github.com/go-logr/logr" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +type PolicyTargetKey struct { + NsName types.NamespacedName + GroupKind schema.GroupKind +} + +func (p PolicyTargetKey) String() string { + return p.NsName.String() + "/" + p.GroupKind.String() +} + +func ProcessBackendTrafficPolicy(c client.Client, + log logr.Logger, + tctx *provider.TranslateContext) { + conflicts := map[string]v1alpha1.BackendTrafficPolicy{} + for _, service := range tctx.Services { + backendTrafficPolicyList := &v1alpha1.BackendTrafficPolicyList{} + if err := c.List(tctx, backendTrafficPolicyList, + client.MatchingFields{ + indexer.PolicyTargetRefs: indexer.GenIndexKeyWithGK("", "Service", service.Namespace, service.Name), + }, + ); err != nil { + log.Error(err, "failed to list BackendTrafficPolicy for Service") + continue + } + 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 + } + 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) + } + if _, ok := tctx.BackendTrafficPolicies[types.NamespacedName{ + Name: policy.Name, + Namespace: policy.Namespace, + }]; ok { + continue + } + + if updated { + tctx.StatusUpdaters = append(tctx.StatusUpdaters, policy.DeepCopy()) + } + + tctx.BackendTrafficPolicies[types.NamespacedName{ + Name: policy.Name, + Namespace: policy.Namespace, + }] = policy.DeepCopy() + } + } +} + +func processPolicyStatus(policy *v1alpha1.BackendTrafficPolicy, + tctx *provider.TranslateContext, + condition metav1.Condition, + updated *bool) { + if ok := SetAncestors(&policy.Status, tctx.ParentRefs, condition); ok { + *updated = true + } +} + +func SetAncestors(status *v1alpha1.PolicyStatus, parentRefs []gatewayv1.ParentReference, condition metav1.Condition) bool { + updated := false + for _, parent := range parentRefs { + ancestorStatus := gatewayv1alpha2.PolicyAncestorStatus{ + AncestorRef: parent, + Conditions: []metav1.Condition{condition}, + ControllerName: gatewayv1alpha2.GatewayController(config.ControllerConfig.ControllerName), + } + if SetAncestorStatus(status, ancestorStatus) { + updated = true + } + } + return updated +} + +func SetAncestorStatus(status *v1alpha1.PolicyStatus, ancestorStatus gatewayv1alpha2.PolicyAncestorStatus) bool { + 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 + } + return false + } + } + status.Ancestors = append(status.Ancestors, ancestorStatus) + return true +} diff --git a/internal/controller/status.go b/internal/controller/status.go index d60e2616b..cae419995 100644 --- a/internal/controller/status.go +++ b/internal/controller/status.go @@ -1,8 +1,12 @@ package controller import ( + "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" + "sigs.k8s.io/controller-runtime/pkg/client" + gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" ) const ( @@ -44,3 +48,43 @@ func VerifyConditions(conditions *[]metav1.Condition, newCondition metav1.Condit } return true } + +func NewPolicyCondition(observedGeneration int64, status bool, message string) metav1.Condition { + conditionStatus := metav1.ConditionTrue + reason := string(gatewayv1alpha2.PolicyReasonAccepted) + if !status { + conditionStatus = metav1.ConditionFalse + reason = string(gatewayv1alpha2.PolicyReasonInvalid) + } + + return metav1.Condition{ + Type: string(gatewayv1alpha2.PolicyConditionAccepted), + Reason: reason, + Status: conditionStatus, + Message: message, + ObservedGeneration: observedGeneration, + } +} + +func NewPolicyConflictCondition(observedGeneration int64, message string) metav1.Condition { + return metav1.Condition{ + Type: string(gatewayv1alpha2.PolicyConditionAccepted), + Reason: string(gatewayv1alpha2.PolicyReasonConflicted), + Status: metav1.ConditionFalse, + Message: message, + ObservedGeneration: observedGeneration, + } +} + +func UpdateStatus( + c client.Client, + log logr.Logger, + 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 + } + } +} diff --git a/internal/provider/adc/translator/httproute.go b/internal/provider/adc/translator/httproute.go index 8dea67a97..37dc9687a 100644 --- a/internal/provider/adc/translator/httproute.go +++ b/internal/provider/adc/translator/httproute.go @@ -290,15 +290,7 @@ func (t *Translator) TranslateHTTPRoute(tctx *provider.TranslateContext, httpRou upNodes := t.translateBackendRef(tctx, backend.BackendRef) upstream.Nodes = append(upstream.Nodes, upNodes...) } - if len(upstream.Nodes) == 0 { - upstream.Nodes = adctypes.UpstreamNodes{ - { - Host: "0.0.0.0", - Port: 80, - Weight: 1, - }, - } - } + t.attachBackendTrafficPolicyToUpstream(nil, upstream) // todo: support multiple backends service := adctypes.NewDefaultService() diff --git a/internal/provider/adc/translator/ingress.go b/internal/provider/adc/translator/ingress.go index 19cc09034..fa0ca853c 100644 --- a/internal/provider/adc/translator/ingress.go +++ b/internal/provider/adc/translator/ingress.go @@ -109,6 +109,8 @@ func (t *Translator) TranslateIngress(tctx *provider.TranslateContext, obj *netw Name: backendService.Name, }] + t.attachBackendTrafficPolicyToUpstream(nil, upstream) + // get the service port configuration var servicePort int32 = 0 var servicePortName string diff --git a/internal/provider/adc/translator/policies.go b/internal/provider/adc/translator/policies.go new file mode 100644 index 000000000..1ed0c0081 --- /dev/null +++ b/internal/provider/adc/translator/policies.go @@ -0,0 +1,54 @@ +package translator + +import ( + "github.com/api7/api7-ingress-controller/api/adc" + "github.com/api7/api7-ingress-controller/api/v1alpha1" + "k8s.io/apimachinery/pkg/types" + gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" + + adctypes "github.com/api7/api7-ingress-controller/api/adc" +) + +func (t *Translator) AttachBackendTrafficPolicyToUpstream(ref gatewayv1.BackendRef, policies map[types.NamespacedName]*v1alpha1.BackendTrafficPolicy, upstream *adctypes.Upstream) { + if len(policies) == 0 { + return + } + var policy *v1alpha1.BackendTrafficPolicy + for _, po := range policies { + for _, targetRef := range po.Spec.TargetRefs { + if ref.Name == targetRef.Name && + (ref.Namespace != nil && string(*ref.Namespace) == po.Namespace) { + policy = po + break + } + } + } + if policy == nil { + return + } + t.attachBackendTrafficPolicyToUpstream(policy, upstream) +} + +func (t *Translator) attachBackendTrafficPolicyToUpstream(policy *v1alpha1.BackendTrafficPolicy, upstream *adctypes.Upstream) { + if policy == nil { + return + } + upstream.UpstreamHost = string(policy.Spec.Host) + upstream.Scheme = policy.Spec.Scheme + if policy.Spec.Retries != nil { + upstream.Retries = new(int64) + *upstream.Retries = int64(*policy.Spec.Retries) + } + 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(), + } + } + if policy.Spec.LoadBalancer != nil { + upstream.Type = adc.UpstreamType(policy.Spec.LoadBalancer.Type) + upstream.HashOn = policy.Spec.LoadBalancer.HashOn + upstream.Key = policy.Spec.LoadBalancer.Key + } +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index a57675f27..395d7eaa7 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -18,21 +18,28 @@ type Provider interface { } type TranslateContext struct { - BackendRefs []gatewayv1.BackendRef - GatewayTLSConfig []gatewayv1.GatewayTLSConfig - GatewayProxy *v1alpha1.GatewayProxy - Credentials []v1alpha1.Credential - EndpointSlices map[types.NamespacedName][]discoveryv1.EndpointSlice - Secrets map[types.NamespacedName]*corev1.Secret - PluginConfigs map[types.NamespacedName]*v1alpha1.PluginConfig - Services map[types.NamespacedName]*corev1.Service + context.Context + ParentRefs []gatewayv1.ParentReference + BackendRefs []gatewayv1.BackendRef + GatewayTLSConfig []gatewayv1.GatewayTLSConfig + GatewayProxy *v1alpha1.GatewayProxy + Credentials []v1alpha1.Credential + EndpointSlices map[types.NamespacedName][]discoveryv1.EndpointSlice + Secrets map[types.NamespacedName]*corev1.Secret + PluginConfigs map[types.NamespacedName]*v1alpha1.PluginConfig + Services map[types.NamespacedName]*corev1.Service + BackendTrafficPolicies map[types.NamespacedName]*v1alpha1.BackendTrafficPolicy + + StatusUpdaters []client.Object } -func NewDefaultTranslateContext() *TranslateContext { +func NewDefaultTranslateContext(ctx context.Context) *TranslateContext { return &TranslateContext{ - EndpointSlices: make(map[types.NamespacedName][]discoveryv1.EndpointSlice), - Secrets: make(map[types.NamespacedName]*corev1.Secret), - PluginConfigs: make(map[types.NamespacedName]*v1alpha1.PluginConfig), - Services: make(map[types.NamespacedName]*corev1.Service), + Context: ctx, + EndpointSlices: make(map[types.NamespacedName][]discoveryv1.EndpointSlice), + Secrets: make(map[types.NamespacedName]*corev1.Secret), + PluginConfigs: make(map[types.NamespacedName]*v1alpha1.PluginConfig), + Services: make(map[types.NamespacedName]*corev1.Service), + BackendTrafficPolicies: make(map[types.NamespacedName]*v1alpha1.BackendTrafficPolicy), } } diff --git a/test/e2e/framework/manifests/ingress.yaml b/test/e2e/framework/manifests/ingress.yaml index 0a7296821..207916c2c 100644 --- a/test/e2e/framework/manifests/ingress.yaml +++ b/test/e2e/framework/manifests/ingress.yaml @@ -106,6 +106,21 @@ rules: - get - list - watch +- apiGroups: + - gateway.apisix.io + resources: + - backendtrafficpolicies + verbs: + - get + - list + - watch +- apiGroups: + - gateway.apisix.io + resources: + - backendtrafficpolicies/status + verbs: + - get + - update - apiGroups: - gateway.apisix.io resources: @@ -129,6 +144,21 @@ rules: - get - list - watch +- apiGroups: + - gateway.apisix.io + resources: + - httproutepolicies + verbs: + - get + - list + - watch +- apiGroups: + - gateway.apisix.io + resources: + - httproutepolicies/status + verbs: + - get + - update - apiGroups: - gateway.apisix.io resources: