Skip to content

Commit

Permalink
Migrate all providers to the new interface.
Browse files Browse the repository at this point in the history
* Providers now should implement ResourcesToIRConverter interface and
  IRToGatewayAPIConverter interface.
  • Loading branch information
sawsa307 committed Sep 9, 2024
1 parent 8ac522d commit c6a8035
Show file tree
Hide file tree
Showing 40 changed files with 1,254 additions and 1,585 deletions.
72 changes: 3 additions & 69 deletions pkg/i2gw/ingress2gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,13 @@ package i2gw
import (
"context"
"fmt"
"maps"

"github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/notifications"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/validation/field"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/config"
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
gatewayv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1"
)

func ToGatewayAPIResources(ctx context.Context, namespace string, inputFile string, providers []string, providerSpecificFlags map[string]map[string]string) ([]GatewayResources, map[string]string, error) {
Expand Down Expand Up @@ -73,7 +68,9 @@ func ToGatewayAPIResources(ctx context.Context, namespace string, inputFile stri
errs field.ErrorList
)
for _, provider := range providerByName {
providerGatewayResources, conversionErrs := provider.ToGatewayAPI()
ir, conversionErrs := provider.ToIR()
errs = append(errs, conversionErrs...)
providerGatewayResources, conversionErrs := provider.ToGatewayResources(ir)
errs = append(errs, conversionErrs...)
gatewayResources = append(gatewayResources, providerGatewayResources)
}
Expand Down Expand Up @@ -138,69 +135,6 @@ func GetSupportedProviders() []string {
return supportedProviders
}

// MergeGatewayResources accept multiple GatewayResources and create a unique Resource struct
// built as follows:
// - GatewayClasses, *Routes, and ReferenceGrants are grouped into the same maps
// - Gateways may have the same NamespaceName even if they come from different
// ingresses, as they have a their GatewayClass' name as name. For this reason,
// if there are mutiple gateways named the same, their listeners are merged into
// a unique Gateway.
//
// This behavior is likely to change after https://github.com/kubernetes-sigs/gateway-api/pull/1863 takes place.
func MergeGatewayResources(gatewayResources ...GatewayResources) (GatewayResources, field.ErrorList) {
mergedGatewayResources := GatewayResources{
Gateways: make(map[types.NamespacedName]gatewayv1.Gateway),
GatewayClasses: make(map[types.NamespacedName]gatewayv1.GatewayClass),
HTTPRoutes: make(map[types.NamespacedName]gatewayv1.HTTPRoute),
TLSRoutes: make(map[types.NamespacedName]gatewayv1alpha2.TLSRoute),
TCPRoutes: make(map[types.NamespacedName]gatewayv1alpha2.TCPRoute),
UDPRoutes: make(map[types.NamespacedName]gatewayv1alpha2.UDPRoute),
ReferenceGrants: make(map[types.NamespacedName]gatewayv1beta1.ReferenceGrant),
}
var errs field.ErrorList
mergedGatewayResources.Gateways, errs = mergeGateways(gatewayResources)
if len(errs) > 0 {
return GatewayResources{}, errs
}
for _, gr := range gatewayResources {
maps.Copy(mergedGatewayResources.GatewayClasses, gr.GatewayClasses)
maps.Copy(mergedGatewayResources.HTTPRoutes, gr.HTTPRoutes)
maps.Copy(mergedGatewayResources.TLSRoutes, gr.TLSRoutes)
maps.Copy(mergedGatewayResources.TCPRoutes, gr.TCPRoutes)
maps.Copy(mergedGatewayResources.UDPRoutes, gr.UDPRoutes)
maps.Copy(mergedGatewayResources.ReferenceGrants, gr.ReferenceGrants)
}
return mergedGatewayResources, errs
}

func mergeGateways(gatewaResources []GatewayResources) (map[types.NamespacedName]gatewayv1.Gateway, field.ErrorList) {
newGateways := map[types.NamespacedName]gatewayv1.Gateway{}
errs := field.ErrorList{}

for _, gr := range gatewaResources {
for _, g := range gr.Gateways {
nn := types.NamespacedName{Namespace: g.Namespace, Name: g.Name}
if existingGateway, ok := newGateways[nn]; ok {
g.Spec.Listeners = append(g.Spec.Listeners, existingGateway.Spec.Listeners...)
g.Spec.Addresses = append(g.Spec.Addresses, existingGateway.Spec.Addresses...)
}
newGateways[nn] = g
// 64 is the maximum number of listeners a Gateway can have
if len(g.Spec.Listeners) > 64 {
fieldPath := field.NewPath(fmt.Sprintf("%s/%s", nn.Namespace, nn.Name)).Child("spec").Child("listeners")
errs = append(errs, field.Invalid(fieldPath, g, "error while merging gateway listeners: a gateway cannot have more than 64 listeners"))
}
// 16 is the maximum number of addresses a Gateway can have
if len(g.Spec.Addresses) > 16 {
fieldPath := field.NewPath(fmt.Sprintf("%s/%s", nn.Namespace, nn.Name)).Child("spec").Child("addresses")
errs = append(errs, field.Invalid(fieldPath, g, "error while merging gateway listeners: a gateway cannot have more than 16 addresses"))
}
}
}

return newGateways, errs
}

func CastToUnstructured(obj runtime.Object) (*unstructured.Unstructured, error) {
// Convert the Kubernetes object to unstructured.Unstructured
unstructuredObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
Expand Down
12 changes: 12 additions & 0 deletions pkg/i2gw/intermediate/provider_apisix.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,15 @@ package intermediate
type ApisixGatewayIR struct{}
type ApisixHTTPRouteIR struct{}
type ApisixServiceIR struct{}

func MergeApisixGatewayIR(current, existing *ApisixGatewayIR) *ApisixGatewayIR {
var mergedGatewayIR *ApisixGatewayIR
// Populate the value of each fields once ApisixGatewayIR is not empty.
if existing != nil {
mergedGatewayIR = existing
}
if current != nil {
mergedGatewayIR = current
}
return mergedGatewayIR
}
12 changes: 12 additions & 0 deletions pkg/i2gw/intermediate/provider_ingressnginx.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,15 @@ package intermediate
type IngressNginxGatewayIR struct{}
type IngressNginxHTTPRouteIR struct{}
type IngressNginxServiceIR struct{}

func MergeIngressNginxGatewayIR(current, existing *IngressNginxGatewayIR) *IngressNginxGatewayIR {
var mergedGatewayIR *IngressNginxGatewayIR
// Populate the value of each fields once IngressNginxGatewayIR is not empty.
if existing != nil {
mergedGatewayIR = existing
}
if current != nil {
mergedGatewayIR = current
}
return mergedGatewayIR
}
12 changes: 12 additions & 0 deletions pkg/i2gw/intermediate/provider_istio.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,15 @@ package intermediate
type IstioGatewayIR struct{}
type IstioHTTPRouteIR struct{}
type IstioServiceIR struct{}

func MergeIstioGatewayIR(current, existing *IstioGatewayIR) *IstioGatewayIR {
var mergedGatewayIR *IstioGatewayIR
// Populate the value of each fields once IstioGatewayIR is not empty.
if existing != nil {
mergedGatewayIR = existing
}
if current != nil {
mergedGatewayIR = current
}
return mergedGatewayIR
}
12 changes: 12 additions & 0 deletions pkg/i2gw/intermediate/provider_kong.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,15 @@ package intermediate
type KongGatewayIR struct{}
type KongHTTPRouteIR struct{}
type KongServiceIR struct{}

func MergeKongGatewayIR(current, existing *KongGatewayIR) *KongGatewayIR {
var mergedGatewayIR *KongGatewayIR
// Populate the value of each fields once KongGatewayIR is not empty.
if existing != nil {
mergedGatewayIR = existing
}
if current != nil {
mergedGatewayIR = current
}
return mergedGatewayIR
}
12 changes: 12 additions & 0 deletions pkg/i2gw/intermediate/provider_openapi3.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,15 @@ package intermediate
type Openapi3GatewayIR struct{}
type Openapi3HTTPRouteIR struct{}
type Openapi3ServiceIR struct{}

func MergeOpenapi3GatewayIR(current, existing *Openapi3GatewayIR) *Openapi3GatewayIR {
var mergedGatewayIR *Openapi3GatewayIR
// Populate the value of each fields once Openapi3GatewayIR is not empty.
if existing != nil {
mergedGatewayIR = existing
}
if current != nil {
mergedGatewayIR = current
}
return mergedGatewayIR
}
24 changes: 16 additions & 8 deletions pkg/i2gw/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"context"
"sync"

"github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/intermediate"
networkingv1 "k8s.io/api/networking/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/types"
Expand Down Expand Up @@ -55,7 +56,8 @@ type ProviderConf struct {
// be used.
type Provider interface {
CustomResourceReader
ResourceConverter
ResourcesToIRConverter
IRToGatewayAPIConverter
}

type CustomResourceReader interface {
Expand All @@ -69,13 +71,19 @@ type CustomResourceReader interface {
ReadResourcesFromFile(ctx context.Context, filename string) error
}

// The ResourceConverter interface specifies all the implemented Gateway API resource
// conversion functions.
type ResourceConverter interface {
// The ResourcesToIRConverter interface specifies conversion functions from Ingress
// and extensions into IR.
type ResourcesToIRConverter interface {
// ToIR converts stored API entities associated with the Provider into IR.
ToIR() (intermediate.IR, field.ErrorList)
}

// ToGatewayAPIResources converts stored API entities associated
// with the Provider into GatewayResources.
ToGatewayAPI() (GatewayResources, field.ErrorList)
// The IRToGatewayAPIConverter interface specifies conversion functions from IR
// into Gateway and Gateway extensions.
type IRToGatewayAPIConverter interface {
// ToGatewayResources converts stored IR with the Provider into
// Gateway API resources and extensions
ToGatewayResources(intermediate.IR) (GatewayResources, field.ErrorList)
}

// ImplementationSpecificHTTPPathTypeMatchConverter is an option to customize the ingress implementationSpecific
Expand Down Expand Up @@ -110,7 +118,7 @@ type GatewayResources struct {
//
// Different FeatureParsers will run in undetermined order. The function must
// modify / create only the required fields of the gateway resources and nothing else.
type FeatureParser func([]networkingv1.Ingress, *GatewayResources) field.ErrorList
type FeatureParser func([]networkingv1.Ingress, *intermediate.IR) field.ErrorList

var providerSpecificFlagDefinitions = providerSpecificFlags{
flags: make(map[ProviderName]map[string]ProviderSpecificFlag),
Expand Down
24 changes: 15 additions & 9 deletions pkg/i2gw/providers/apisix/apisix.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import (
"fmt"

"github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw"
"github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/intermediate"
"github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/common"
"k8s.io/apimachinery/pkg/util/validation/field"
)

Expand All @@ -34,24 +36,28 @@ func init() {

// Provider implements the i2gw.Provider interface.
type Provider struct {
storage *storage
resourceReader *resourceReader
converter *converter
storage *storage
resourceReader *resourceReader
resourcesToIRConverter *resourcesToIRConverter
}

// NewProvider constructs and returns the apisix implementation of i2gw.Provider.
func NewProvider(conf *i2gw.ProviderConf) i2gw.Provider {
return &Provider{
storage: newResourcesStorage(),
resourceReader: newResourceReader(conf),
converter: newConverter(),
storage: newResourcesStorage(),
resourceReader: newResourceReader(conf),
resourcesToIRConverter: newResourcesToIRConverter(),
}
}

// ToGatewayAPI converts stored Apisix API entities to i2gw.GatewayResources
// ToIR converts stored Apisix API entities to intermediate.IR
// including the apisix specific features.
func (p *Provider) ToGatewayAPI() (i2gw.GatewayResources, field.ErrorList) {
return p.converter.convert(p.storage)
func (p *Provider) ToIR() (intermediate.IR, field.ErrorList) {
return p.resourcesToIRConverter.convertToIR(p.storage)
}

func (p *Provider) ToGatewayResources(ir intermediate.IR) (i2gw.GatewayResources, field.ErrorList) {
return common.ToGatewayResources(ir)
}

func (p *Provider) ReadResourcesFromCluster(ctx context.Context) error {
Expand Down
21 changes: 11 additions & 10 deletions pkg/i2gw/providers/apisix/converter.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,21 @@ package apisix

import (
"github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw"
"github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/intermediate"
"github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/common"
networkingv1 "k8s.io/api/networking/v1"
"k8s.io/apimachinery/pkg/util/validation/field"
)

// converter implements the ToGatewayAPI function of i2gw.ResourceConverter interface.
type converter struct {
// resourcesToIRConverter implements the ToIR function of i2gw.ResourcesToIRConverter interface.
type resourcesToIRConverter struct {
featureParsers []i2gw.FeatureParser
implementationSpecificOptions i2gw.ProviderImplementationSpecificOptions
}

// newConverter returns an apisix converter instance.
func newConverter() *converter {
return &converter{
// newResourcesToIRConverter returns an apisix resourcesToIRConverter instance.
func newResourcesToIRConverter() *resourcesToIRConverter {
return &resourcesToIRConverter{
featureParsers: []i2gw.FeatureParser{
httpToHTTPSFeature,
},
Expand All @@ -41,24 +42,24 @@ func newConverter() *converter {
}
}

func (c *converter) convert(storage *storage) (i2gw.GatewayResources, field.ErrorList) {
func (c *resourcesToIRConverter) convertToIR(storage *storage) (intermediate.IR, field.ErrorList) {
ingressList := []networkingv1.Ingress{}
for _, ing := range storage.Ingresses {
ingressList = append(ingressList, *ing)
}
// Convert plain ingress resources to gateway resources, ignoring all
// provider-specific features.
gatewayResources, errs := common.ToGateway(ingressList, c.implementationSpecificOptions)
ir, errs := common.ToIR(ingressList, c.implementationSpecificOptions)
if len(errs) > 0 {
return i2gw.GatewayResources{}, errs
return intermediate.IR{}, errs
}

for _, parseFeatureFunc := range c.featureParsers {
// Apply the feature parsing function to the gateway resources, one by one.
parseErrs := parseFeatureFunc(ingressList, &gatewayResources)
parseErrs := parseFeatureFunc(ingressList, &ir)
// Append the parsing errors to the error list.
errs = append(errs, parseErrs...)
}

return gatewayResources, errs
return ir, errs
}
6 changes: 3 additions & 3 deletions pkg/i2gw/providers/apisix/http_to_https.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ package apisix
import (
"fmt"

"github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw"
"github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/intermediate"
"github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/notifications"
"github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/common"
networkingv1 "k8s.io/api/networking/v1"
Expand All @@ -29,7 +29,7 @@ import (
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
)

func httpToHTTPSFeature(ingresses []networkingv1.Ingress, gatewayResources *i2gw.GatewayResources) field.ErrorList {
func httpToHTTPSFeature(ingresses []networkingv1.Ingress, ir *intermediate.IR) field.ErrorList {
var errs field.ErrorList
httpToHTTPSAnnotation := apisixAnnotation("http-to-https")
ruleGroups := common.GetRuleGroups(ingresses)
Expand All @@ -40,7 +40,7 @@ func httpToHTTPSFeature(ingresses []networkingv1.Ingress, gatewayResources *i2gw
continue
}
key := types.NamespacedName{Namespace: rule.Ingress.Namespace, Name: common.RouteName(rg.Name, rg.Host)}
httpRoute, ok := gatewayResources.HTTPRoutes[key]
httpRoute, ok := ir.HTTPRoutes[key]
if !ok {
errs = append(errs, field.NotFound(field.NewPath("HTTPRoute"), key))
}
Expand Down
Loading

0 comments on commit c6a8035

Please sign in to comment.