Skip to content

Commit

Permalink
feat: all resources printed
Browse files Browse the repository at this point in the history
Signed-off-by: Mattia Lavacca <[email protected]>
  • Loading branch information
mlavacca committed Oct 20, 2023
1 parent 8eacee6 commit f8d7498
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 51 deletions.
14 changes: 7 additions & 7 deletions cmd/print.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ import (

"github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/printers"
"k8s.io/client-go/tools/clientcmd"
"sigs.k8s.io/controller-runtime/pkg/client"
gatewayv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1"

// Call init function for the providers
Expand Down Expand Up @@ -84,9 +84,9 @@ func (pr *PrintRunner) PrintResources(cmd *cobra.Command, _ []string) error {
return err
}

var additionalResources []client.Object
var additionalResources []*unstructured.Unstructured
if pr.allResources {
additionalResources, err = i2gw.FetchAllResources(cmd.Context(), pr.namespaceFilter, pr.inputFile)
additionalResources, err = i2gw.ConstructOtherResourcesFromFile(pr.namespaceFilter, pr.inputFile, pr.providers)
if err != nil {
return err
}
Expand All @@ -96,7 +96,7 @@ func (pr *PrintRunner) PrintResources(cmd *cobra.Command, _ []string) error {
return nil
}

func (pr *PrintRunner) outputResult(httpRoutes []gatewayv1beta1.HTTPRoute, gateways []gatewayv1beta1.Gateway, additionalResources ...client.Object) {
func (pr *PrintRunner) outputResult(httpRoutes []gatewayv1beta1.HTTPRoute, gateways []gatewayv1beta1.Gateway, additionalResources ...*unstructured.Unstructured) {
for i := range additionalResources {
err := pr.resourcePrinter.PrintObj(additionalResources[i], os.Stdout)
if err != nil {
Expand All @@ -120,14 +120,14 @@ func (pr *PrintRunner) outputResult(httpRoutes []gatewayv1beta1.HTTPRoute, gatew
for i := range gateways {
err := pr.resourcePrinter.PrintObj(&gateways[i], os.Stdout)
if err != nil {
fmt.Printf("# Error printing %s HTTPRoute: %v\n", gateways[i].Name, err)
fmt.Printf("# Error printing %s/%s Gateway: %v\n", gateways[i].Namespace, gateways[i].Name, err)
}
}

for i := range httpRoutes {
err := pr.resourcePrinter.PrintObj(&httpRoutes[i], os.Stdout)
if err != nil {
fmt.Printf("# Error printing %s HTTPRoute: %v\n", httpRoutes[i].Name, err)
fmt.Printf("# Error printing %s/%s HTTPRoute: %v\n", httpRoutes[i].Namespace, httpRoutes[i].Name, err)
}
}
}
Expand Down Expand Up @@ -204,7 +204,7 @@ func newPrintCommand() *cobra.Command {
`Path to the manifest file. When set, the tool will read ingresses from the file instead of reading from the cluster. Supported files are yaml and json`)

cmd.Flags().BoolVarP(&pr.allResources, "all-resources", "", false,
`If present, list all the object(s) across the selected namespaces.`)
`If present, list all the object across the selected namespaces.`)

cmd.Flags().StringVarP(&pr.namespace, "namespace", "n", "",
`If present, the namespace scope for this CLI request`)
Expand Down
80 changes: 45 additions & 35 deletions pkg/i2gw/ingress2gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,44 +35,41 @@ import (
)

func ToGatewayAPIResources(ctx context.Context, namespace string, inputFile string, providers []string) ([]gatewayv1beta1.HTTPRoute, []gatewayv1beta1.Gateway, error) {
conf, err := config.GetConfig()
if err != nil {
return nil, nil, fmt.Errorf("failed to get client config: %w", err)
}

cl, err := client.New(conf, client.Options{})
if err != nil {
return nil, nil, fmt.Errorf("failed to create client: %w", err)
}
cl = client.NewNamespacedClient(cl, namespace)

var ingresses networkingv1.IngressList
var gateways []gatewayv1beta1.Gateway
var httpRoutes []gatewayv1beta1.HTTPRoute

providerByName, err := constructProviders(&ProviderConf{
Client: cl,
}, providers)
providerByName, err := constructProviders(providers)
if err != nil {
return nil, nil, err
}

resources := InputResources{}

if inputFile != "" {
if err = ConstructResourcesFromFile(&ingresses, inputFile, namespace, false); err != nil {
if err = ConstructIngressesFromFile(&ingresses, inputFile, namespace); err != nil {
return nil, nil, fmt.Errorf("failed to read ingresses from file: %w", err)
}
resources.Ingresses = ingresses.Items
if err = readProviderResourcesFromFile(ctx, providerByName, &resources, inputFile); err != nil {
return nil, nil, err
}
} else {
conf, err := config.GetConfig()
if err != nil {
return nil, nil, fmt.Errorf("failed to get client config: %w", err)
}

cl, err := client.New(conf, client.Options{})
if err != nil {
return nil, nil, fmt.Errorf("failed to create client: %w", err)
}
cl = client.NewNamespacedClient(cl, namespace)
if err = ConstructIngressesFromCluster(ctx, cl, &ingresses); err != nil {
return nil, nil, fmt.Errorf("failed to read ingresses from cluster: %w", err)
}
resources.Ingresses = ingresses.Items
if err = readProviderResourcesFromCluster(ctx, providerByName, &resources); err != nil {
if err = readProviderResourcesFromCluster(ctx, cl, providerByName, &resources); err != nil {
return nil, nil, err
}
}
Expand All @@ -95,19 +92,6 @@ func ToGatewayAPIResources(ctx context.Context, namespace string, inputFile stri
return httpRoutes, gateways, nil
}

// TODO: comment
// inputFile is always set here, as we perform valdiation in the Cobra PreRun phase.
func FetchAllResources(ctx context.Context, namespace string, inputFile string) ([]client.Object, error) {
if err = ConstructResourcesFromFile(&ingresses, inputFile, namespace, true); err != nil {
return nil, fmt.Errorf("failed to read ingresses from file: %w", err)
}
resources.Ingresses = ingresses.Items
if err = readProviderResourcesFromFile(ctx, providerByName, &resources, inputFile); err != nil {
return nil, nil, err
}
return nil, nil
}

func readProviderResourcesFromFile(ctx context.Context, providerByName map[ProviderName]Provider, resources *InputResources, inputFile string) error {
for name, provider := range providerByName {
if err := provider.ReadResourcesFromFiles(ctx, resources.CustomResources, inputFile); err != nil {
Expand All @@ -117,9 +101,9 @@ func readProviderResourcesFromFile(ctx context.Context, providerByName map[Provi
return nil
}

func readProviderResourcesFromCluster(ctx context.Context, providerByName map[ProviderName]Provider, resources *InputResources) error {
func readProviderResourcesFromCluster(ctx context.Context, cl client.Client, providerByName map[ProviderName]Provider, resources *InputResources) error {
for name, provider := range providerByName {
if err := provider.ReadResourcesFromCluster(ctx, resources.CustomResources); err != nil {
if err := provider.ReadResourcesFromCluster(ctx, cl, resources.CustomResources); err != nil {
return fmt.Errorf("failed to read %s resources from the cluster: %w", name, err)
}
}
Expand All @@ -136,7 +120,7 @@ func ConstructIngressesFromCluster(ctx context.Context, cl client.Client, ingres

// constructProviders constructs a map of concrete Provider implementations
// by their ProviderName.
func constructProviders(conf *ProviderConf, providers []string) (map[ProviderName]Provider, error) {
func constructProviders(providers []string) (map[ProviderName]Provider, error) {
providerByName := make(map[ProviderName]Provider, len(ProviderConstructorByName))

for _, requestedProvider := range providers {
Expand All @@ -145,7 +129,8 @@ func constructProviders(conf *ProviderConf, providers []string) (map[ProviderNam
if !ok {
return nil, fmt.Errorf("%s is not a supported provider", requestedProvider)
}
providerByName[requestedProviderName] = newProviderFunc(conf)
conf := ProviderConfByName[requestedProviderName]
providerByName[requestedProviderName] = newProviderFunc(&conf)
}

return providerByName, nil
Expand Down Expand Up @@ -195,10 +180,10 @@ func extractObjectsFromReader(reader io.Reader) ([]*unstructured.Unstructured, e
return finalObjs, nil
}

// ConstructResourcesFromFile reads the inputFile in either json/yaml formats,
// ConstructIngressesFromFile reads the inputFile in either json/yaml formats,
// then deserialize the file into Ingresses resources.
// All ingresses will be pushed into the supplied IngressList for return.
func ConstructResourcesFromFile(l *networkingv1.IngressList, inputFile string, namespace string, allResource bool) error {
func ConstructIngressesFromFile(l *networkingv1.IngressList, inputFile string, namespace string) error {
stream, err := os.ReadFile(inputFile)
if err != nil {
return err
Expand Down Expand Up @@ -228,6 +213,31 @@ func ConstructResourcesFromFile(l *networkingv1.IngressList, inputFile string, n
return nil
}

// ConstructOtherResourcesFromFile reads the inputFile in either json/yaml formats,
// then deserialize the file into client.object resources.
func ConstructOtherResourcesFromFile(namespace string, inputFile string, providers []string) ([]*unstructured.Unstructured, error) {
providerByName, err := constructProviders(providers)
if err != nil {
return nil, err
}

stream, err := os.ReadFile(inputFile)
if err != nil {
return nil, err
}

reader := bytes.NewReader(stream)
objs, err := extractObjectsFromReader(reader)
if err != nil {
return nil, err
}

for _, p := range providerByName {
objs = p.Filter(objs)
}
return objs, err
}

func aggregatedErrs(errs field.ErrorList) error {
errMsg := fmt.Errorf("\n# Encountered %d errors", len(errs))
for _, err := range errs {
Expand Down
7 changes: 2 additions & 5 deletions pkg/i2gw/ingress2gateway_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func Test_constructIngressesFromFile(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
gotIngressList := &networkingv1.IngressList{}
err := ConstructResourcesFromFile(gotIngressList, tc.filePath, tc.namespace)
err := ConstructIngressesFromFile(gotIngressList, tc.filePath, tc.namespace)
if err != nil {
t.Errorf("Failed to open test file: %v", err)
}
Expand Down Expand Up @@ -174,10 +174,7 @@ func Test_constructProviders(t *testing.T) {
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
cl := fake.NewClientBuilder().WithRuntimeObjects([]runtime.Object{}...).Build()
providerByName, err := constructProviders(&ProviderConf{
Client: cl,
}, tc.providers)
providerByName, err := constructProviders(tc.providers)
if tc.expectedError != nil {
if err == nil {
t.Errorf("Expected error but got none")
Expand Down
22 changes: 20 additions & 2 deletions pkg/i2gw/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
"context"

networkingv1 "k8s.io/api/networking/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/validation/field"
"sigs.k8s.io/controller-runtime/pkg/client"
Expand All @@ -32,6 +34,9 @@ import (
// func at startup.
var ProviderConstructorByName = map[ProviderName]ProviderConstructor{}

// TODO: comment
var ProviderConfByName = map[ProviderName]ProviderConf{}

// ProviderName is a string alias that stores the concrete Provider name.
type ProviderName string

Expand All @@ -42,7 +47,14 @@ type ProviderConstructor func(conf *ProviderConf) Provider
// ProviderConf contains all the configuration required for every concrete
// Provider implementation.
type ProviderConf struct {
Client client.Client
FilteredObjects []schema.GroupKind
}

var DefaultFilteredObjects = []schema.GroupKind{
{
Group: networkingv1.SchemeGroupVersion.Group,
Kind: "Ingress",
},
}

// The Provider interface specifies the required functionality which needs to be
Expand All @@ -51,13 +63,14 @@ type ProviderConf struct {
type Provider interface {
CustomResourceReader
ResourceConverter
ResourceFilter
}

type CustomResourceReader interface {

// ReadResourcesFromCluster reads custom resources associated with
// the underlying Provider implementation from the kubernetes cluster.
ReadResourcesFromCluster(ctx context.Context, customResources interface{}) error
ReadResourcesFromCluster(ctx context.Context, cl client.Client, customResources interface{}) error

// ReadResourcesFromFiles reads custom resources associated with
// the underlying Provider implementation from the files.
Expand All @@ -73,6 +86,11 @@ type ResourceConverter interface {
ToGatewayAPI(resources InputResources) (GatewayResources, field.ErrorList)
}

type ResourceFilter interface {
// Filter filters out the objects that must not be printed.
Filter([]*unstructured.Unstructured) []*unstructured.Unstructured
}

// InputResources contains all Ingress objects, and Provider specific
// custom resources.
type InputResources struct {
Expand Down
32 changes: 32 additions & 0 deletions pkg/i2gw/providers/ingressnginx/filter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package ingressnginx

import (
"github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

func init() {
i2gw.ProviderConfByName[Name] = i2gw.ProviderConf{
FilteredObjects: i2gw.DefaultFilteredObjects,
}
}

func (c *converter) Filter(objects []*unstructured.Unstructured) []*unstructured.Unstructured {
filteredObjects := []*unstructured.Unstructured{}

for _, o := range objects {
var filterOut bool
for _, f := range c.conf.FilteredObjects {
if o.GetObjectKind().GroupVersionKind().Group == f.Group &&
o.GetObjectKind().GroupVersionKind().Kind == f.Kind {
filterOut = true
break
}
}
if !filterOut {
filteredObjects = append(filteredObjects, o)
}
}

return filteredObjects
}
3 changes: 2 additions & 1 deletion pkg/i2gw/providers/ingressnginx/resource_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"context"

"github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw"
"sigs.k8s.io/controller-runtime/pkg/client"
)

// converter implements the i2gw.CustomResourceReader interface.
Expand All @@ -34,7 +35,7 @@ func newResourceReader(conf *i2gw.ProviderConf) *resourceReader {
}
}

func (r *resourceReader) ReadResourcesFromCluster(ctx context.Context, customResources interface{}) error {
func (r *resourceReader) ReadResourcesFromCluster(ctx context.Context, cl client.Client, customResources interface{}) error {
// ingress-nginx does not have any CRDs.
return nil
}
Expand Down
44 changes: 44 additions & 0 deletions pkg/i2gw/providers/kong/filter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package kong

import (
"github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
)

var filteredObjects = []schema.GroupKind{
{
Group: "configuration.konghq.com",
Kind: "TCPIngress",
},
{
Group: "configuration.konghq.com",
Kind: "UDPIngress",
},
}

func init() {
i2gw.ProviderConfByName[Name] = i2gw.ProviderConf{
FilteredObjects: append(i2gw.DefaultFilteredObjects, filteredObjects...),
}
}

func (c *converter) Filter(objects []*unstructured.Unstructured) []*unstructured.Unstructured {
filteredObjects := []*unstructured.Unstructured{}

for _, o := range objects {
var filterOut bool
for _, f := range c.conf.FilteredObjects {
if o.GetObjectKind().GroupVersionKind().Group == f.Group &&
o.GetObjectKind().GroupVersionKind().Kind == f.Kind {
filterOut = true
break
}
}
if !filterOut {
filteredObjects = append(filteredObjects, o)
}
}

return filteredObjects
}
Loading

0 comments on commit f8d7498

Please sign in to comment.