This document will guide you through the process of creating a concrete resource converter that implements the Provider
interface in the i2gw
package.
Each provider implementation in the i2gw/providers
package is responsible for converting a provider specific Ingress
and related resources (e.g istio VirtualService) into Gateway API
resources.
A provider must be able to read its custom resources, and convert them.
- Familiarity with Go programming language.
- Basic understanding of Kubernetes and its custom resources.
- A setup Go development environment.
In this section, we will walk through a demo of how to add support for the example-gateway
provider.
- Add a new package under the
providers
package. Say we want to add a new gateway provider example.
.
├── ingress2gateway.go
├── ingress2gateway_test.go
├── provider.go
└── providers
├── common
├── examplegateway
└── ingressnginx
- Create a struct named
resourceReader
which implements theCustomResourceReader
interface in a file namedresource_converter.go
.
package examplegateway
import (
"context"
"github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw"
)
// converter implements the i2gw.CustomResourceReader interface.
type resourceReader struct {
conf *i2gw.ProviderConf
}
// newResourceReader returns a resourceReader instance.
func newResourceReader(conf *i2gw.ProviderConf) *resourceReader {
return &resourceReader{
conf: conf,
}
}
func (r *resourceReader) ReadResourcesFromCluster(ctx context.Context) error {
// read example-gateway related resources from the cluster.
return nil
}
func (r *resourceReader) ReadResourcesFromFiles(ctx context.Context, filename string) error {
// read example-gateway related resources from the file.
return nil
}
These methods are used by providers to read and store additional resources they may need during conversion.
- Create a struct named
converter
which implements theResourceConverter
interface in a file namedconverter.go
. The implementedToGatewayAPI
function should simply call every registeredfeatureParser
function, one by one. Take a look atingressnginx/converter.go
for an example. TheImplementationSpecificOptions
struct contains the handlers to customize native ingress implementation-specific fields. Take a look atkong/converter.go
for an example.
package examplegateway
import (
"github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw"
)
// converter implements the ToGatewayAPI function of i2gw.ResourceConverter interface.
type converter struct {
conf *i2gw.ProviderConf
featureParsers []i2gw.FeatureParser
implementationSpecificOptions i2gw.ProviderImplementationSpecificOptions
}
// newConverter returns an ingress-nginx converter instance.
func newConverter(conf *i2gw.ProviderConf) *converter {
return &converter{
conf: conf,
featureParsers: []i2gw.FeatureParser{
// The list of feature parsers comes here.
},
implementationSpecificOptions: i2gw.ProviderImplementationSpecificOptions{
// The list of the implementationSpecific ingress fields options comes here.
},
}
}
- Create a new struct named after the provider you are implementing. This struct should embed the previous 2 structs you created.
package examplegateway
import (
"github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw"
)
// Provider implements the i2gw.Provider interface.
type Provider struct {
conf *i2gw.ProviderConf
*resourceReader
*converter
}
// NewProvider constructs and returns the example-gateway implementation of i2gw.Provider.
func NewProvider(conf *i2gw.ProviderConf) i2gw.Provider {
return &Provider{
conf: conf,
resourceReader: newResourceReader(conf),
converter: newConverter(conf),
}
}
- Add the new provider to
i2gw.ProviderConstructorByName
.
package examplegateway
import (
"github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw"
)
// The Name of the provider.
const Name = "example-gateway-provider"
func init() {
i2gw.ProviderConstructorByName[Name] = NewProvider
}
- [optional] In order to use notification mechanism, create a
notify
function in a file namednotification.go
. This method is used to reduce the function signature for creating notifications during the conversion process.
package examplegateway
import "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/notifications"
func notify(mType notifications.MessageType, message string) {
newNotification := notifications.Notification{Type: mType, Message: message}
notifications.CommonNotification.DispatchNotication(newNotification, string(ProviderName))
}
- Import the new package at
cmd/print
.
package cmd
import (
// Call init function for the providers
_ "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/ingressnginx"
_ "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/examplegateway"
)
In case you want to add support for the conversion of a specific feature within a provider (see for example the canary
feature of ingress-nginx) you'll want to implement a FeatureParser
function.
Different FeatureParsers
within the same provider will run in undetermined order. This means that when building a
Gateway API
resource manifest, you cannot assume anything about previously initialized fields.
The function must modify / create only the required fields of the resource manifest and nothing else.
For example, lets say we are implementing the canary feature of some provider. When building the HTTPRoute
, we cannot
assume that the BackendRefs
is already initialized with every BackendRef
required. The canary FeatureParser
function must add every missing BackendRef
and update existing ones.
There are 2 main things that needs to be tested when creating a feature parser:
- The conversion logic is actually correct.
- The new function doesn't override other functions modifications.
For example, if one implemented the mirror backend feature and it deletes canary weight from
BackendRefs
, we have a problem.
To define provider-specific flags the user can supply in the print
command, call the
i2gw.RegisterProviderSpecificFlag(ProviderName, i2gw.ProviderSpecificFlag)
function in the init function of the
provider. E.g.:
const Name = "example-gateway-provider"
func init() {
i2gw.ProviderConstructorByName[Name] = NewProvider
i2gw.RegisterProviderSpecificFlag(ProviderName, i2gw.ProviderSpecificFlag{
Name: "infrastructure-labels",
Description: "Comma-separated list of Gateway infrastructure key=value labels",
DefaultValue: "",
})
}
Users can provide a value to the flag as follows:
./ingress2gateway print --providers=example-gateway-provider --example-gateway-provider-infrastructure-labels="app=my-app"
The values of all provider-specific flags supplied by the user can be retrieved from the provider conf
:
if ps := conf.ProviderSpecificFlags[ProviderName]; ps != nil {
labels := ps["infrastructure-labels"]
}