From f0b9fc291daef78a37db0d443be723b4e40f9809 Mon Sep 17 00:00:00 2001 From: Mattia Lavacca Date: Thu, 2 May 2024 12:38:57 +0200 Subject: [PATCH 1/2] feat: --all-resources enables all resources fetch --- cmd/print.go | 110 +++++------------- pkg/i2gw/ingress2gateway.go | 70 ++++++++++- pkg/i2gw/providers/apisix/resource_reader.go | 8 +- pkg/i2gw/providers/common/utils.go | 4 +- .../providers/ingressnginx/resource_reader.go | 8 +- .../istio/e2e_file_converter_test.go | 3 +- pkg/i2gw/providers/istio/istio.go | 2 + pkg/i2gw/providers/istio/resource_reader.go | 3 +- pkg/i2gw/providers/istio/types.go | 7 ++ pkg/i2gw/providers/kong/kong.go | 1 + pkg/i2gw/providers/kong/resource_reader.go | 7 +- pkg/i2gw/providers/openapi3/converter_test.go | 3 +- .../{providers/common => }/resource_reader.go | 24 ++-- .../common => }/resource_reader_test.go | 2 +- pkg/i2gw/resources.go | 34 ++++++ .../common => }/testdata/input-file.json | 0 .../common => }/testdata/input-file.yaml | 0 pkg/i2gw/utils.go | 36 ++++++ 18 files changed, 209 insertions(+), 113 deletions(-) rename pkg/i2gw/{providers/common => }/resource_reader.go (93%) rename pkg/i2gw/{providers/common => }/resource_reader_test.go (99%) create mode 100644 pkg/i2gw/resources.go rename pkg/i2gw/{providers/common => }/testdata/input-file.json (100%) rename pkg/i2gw/{providers/common => }/testdata/input-file.yaml (100%) create mode 100644 pkg/i2gw/utils.go diff --git a/cmd/print.go b/cmd/print.go index 77d513f9..378834b5 100644 --- a/cmd/print.go +++ b/cmd/print.go @@ -17,6 +17,7 @@ limitations under the License. package cmd import ( + "errors" "fmt" "log" "os" @@ -28,6 +29,7 @@ import ( "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" // Call init function for the providers _ "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/apisix" @@ -55,6 +57,10 @@ type PrintRunner struct { // --all-namespaces/-A flag. allNamespaces bool + // allResources indicates whether to print all the resources instead of printing only + // the Gateway API ones. It can be used only when reading from file. + allResources bool + // resourcePrinter determines how resource objects are printed out resourcePrinter printers.ResourcePrinter @@ -82,102 +88,31 @@ func (pr *PrintRunner) PrintGatewayAPIObjects(cmd *cobra.Command, _ []string) er return fmt.Errorf("failed to initialize namespace filter: %w", err) } - gatewayResources, err := i2gw.ToGatewayAPIResources(cmd.Context(), pr.namespaceFilter, pr.inputFile, pr.providers, pr.getProviderSpecificFlags()) + resources, err := i2gw.GetResources(cmd.Context(), pr.namespaceFilter, pr.inputFile, pr.allResources, pr.providers, pr.getProviderSpecificFlags()) if err != nil { return err } - pr.outputResult(gatewayResources) + pr.outputResult(resources) return nil } -func (pr *PrintRunner) outputResult(gatewayResources []i2gw.GatewayResources) { - resourceCount := 0 - - for _, r := range gatewayResources { - resourceCount += len(r.GatewayClasses) - for _, gatewayClass := range r.GatewayClasses { - gatewayClass := gatewayClass - err := pr.resourcePrinter.PrintObj(&gatewayClass, os.Stdout) - if err != nil { - fmt.Printf("# Error printing %s GatewayClass: %v\n", gatewayClass.Name, err) - } - } - } - - for _, r := range gatewayResources { - resourceCount += len(r.Gateways) - for _, gateway := range r.Gateways { - gateway := gateway - err := pr.resourcePrinter.PrintObj(&gateway, os.Stdout) - if err != nil { - fmt.Printf("# Error printing %s Gateway: %v\n", gateway.Name, err) - } - } - } - - for _, r := range gatewayResources { - resourceCount += len(r.HTTPRoutes) - for _, httpRoute := range r.HTTPRoutes { - httpRoute := httpRoute - err := pr.resourcePrinter.PrintObj(&httpRoute, os.Stdout) - if err != nil { - fmt.Printf("# Error printing %s HTTPRoute: %v\n", httpRoute.Name, err) - } - } - } - - for _, r := range gatewayResources { - resourceCount += len(r.TLSRoutes) - for _, tlsRoute := range r.TLSRoutes { - tlsRoute := tlsRoute - err := pr.resourcePrinter.PrintObj(&tlsRoute, os.Stdout) - if err != nil { - fmt.Printf("# Error printing %s TLSRoute: %v\n", tlsRoute.Name, err) - } - } - } - - for _, r := range gatewayResources { - resourceCount += len(r.TCPRoutes) - for _, tcpRoute := range r.TCPRoutes { - tcpRoute := tcpRoute - err := pr.resourcePrinter.PrintObj(&tcpRoute, os.Stdout) - if err != nil { - fmt.Printf("# Error printing %s TCPRoute: %v\n", tcpRoute.Name, err) - } - } - } - - for _, r := range gatewayResources { - resourceCount += len(r.UDPRoutes) - for _, udpRoute := range r.UDPRoutes { - udpRoute := udpRoute - err := pr.resourcePrinter.PrintObj(&udpRoute, os.Stdout) - if err != nil { - fmt.Printf("# Error printing %s UDPRoute: %v\n", udpRoute.Name, err) - } - } - } - - for _, r := range gatewayResources { - resourceCount += len(r.ReferenceGrants) - for _, referenceGrant := range r.ReferenceGrants { - referenceGrant := referenceGrant - err := pr.resourcePrinter.PrintObj(&referenceGrant, os.Stdout) - if err != nil { - fmt.Printf("# Error printing %s ReferenceGrant: %v\n", referenceGrant.Name, err) - } - } - } - - if resourceCount == 0 { +func (pr *PrintRunner) outputResult(resources []client.Object) { + if len(resources) == 0 { msg := "No resources found" if pr.namespaceFilter != "" { msg = fmt.Sprintf("%s in %s namespace", msg, pr.namespaceFilter) } fmt.Println(msg) + return + } + + for _, r := range resources { + err := pr.resourcePrinter.PrintObj(r, os.Stdout) + if err != nil { + fmt.Printf("# Error printing %s %s: %v\n", r.GetName(), r.GetObjectKind().GroupVersionKind().Kind, err) + } } } @@ -238,6 +173,12 @@ func newPrintCommand() *cobra.Command { Use: "print", Short: "Prints Gateway API objects generated from ingress and provider-specific resources.", RunE: pr.PrintGatewayAPIObjects, + PreRunE: func(cmd *cobra.Command, args []string) error { + if pr.allResources && pr.inputFile == "" { + return errors.New("--all-resources flag can be set only when --input-file is set") + } + return nil + }, } cmd.Flags().StringVarP(&pr.outputFormat, "output", "o", "yaml", @@ -253,6 +194,9 @@ func newPrintCommand() *cobra.Command { `If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.`) + cmd.Flags().BoolVar(&pr.allResources, "all-resources", false, + `If present, list all the objects across the selected namespaces. This flag can be set only when reading from a file.`) + cmd.Flags().StringSliceVar(&pr.providers, "providers", i2gw.GetSupportedProviders(), fmt.Sprintf("If present, the tool will try to convert only resources related to the specified providers, supported values are %v.", i2gw.GetSupportedProviders())) diff --git a/pkg/i2gw/ingress2gateway.go b/pkg/i2gw/ingress2gateway.go index dc4c7cef..415fc7c9 100644 --- a/pkg/i2gw/ingress2gateway.go +++ b/pkg/i2gw/ingress2gateway.go @@ -17,9 +17,11 @@ limitations under the License. package i2gw import ( + "bytes" "context" "fmt" "maps" + "os" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/validation/field" @@ -30,7 +32,7 @@ import ( 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, error) { +func GetResources(ctx context.Context, namespace string, inputFile string, allresources bool, providers []string, providerSpecificFlags map[string]map[string]string) ([]client.Object, error) { var clusterClient client.Client if inputFile == "" { @@ -55,10 +57,16 @@ func ToGatewayAPIResources(ctx context.Context, namespace string, inputFile stri return nil, err } + var genericResources []client.Object if inputFile != "" { if err = readProviderResourcesFromFile(ctx, providerByName, inputFile); err != nil { return nil, err } + if allresources { + if genericResources, err = readGenericResourcesFromFile(inputFile, namespace); err != nil { + return nil, err + } + } } else { if err = readProviderResourcesFromCluster(ctx, providerByName); err != nil { return nil, err @@ -78,7 +86,42 @@ func ToGatewayAPIResources(ctx context.Context, namespace string, inputFile stri return nil, aggregatedErrs(errs) } - return gatewayResources, nil + return append(gatewayResourcesToObjects(gatewayResources), genericResources...), nil +} + +func gatewayResourcesToObjects(gatewayResources []GatewayResources) []client.Object { + var objects []client.Object + for _, gr := range gatewayResources { + for _, gateway := range gr.Gateways { + gateway := gateway + objects = append(objects, &gateway) + } + for _, gatewayClass := range gr.GatewayClasses { + gatewayClass := gatewayClass + objects = append(objects, &gatewayClass) + } + for _, httpRoute := range gr.HTTPRoutes { + httpRoute := httpRoute + objects = append(objects, &httpRoute) + } + for _, tlsRoute := range gr.TLSRoutes { + tlsRoute := tlsRoute + objects = append(objects, &tlsRoute) + } + for _, tcpRoute := range gr.TCPRoutes { + tcpRoute := tcpRoute + objects = append(objects, &tcpRoute) + } + for _, udpRoute := range gr.UDPRoutes { + udpRoute := udpRoute + objects = append(objects, &udpRoute) + } + for _, referenceGrant := range gr.ReferenceGrants { + referenceGrant := referenceGrant + objects = append(objects, &referenceGrant) + } + } + return objects } func readProviderResourcesFromFile(ctx context.Context, providerByName map[ProviderName]Provider, inputFile string) error { @@ -90,6 +133,29 @@ func readProviderResourcesFromFile(ctx context.Context, providerByName map[Provi return nil } +func readGenericResourcesFromFile(inputFile, namespace string) ([]client.Object, error) { + objects := make([]client.Object, 0) + stream, err := os.ReadFile(inputFile) + if err != nil { + return nil, fmt.Errorf("failed to read file %v: %w", inputFile, err) + } + + unstructuredObjects, err := ExtractObjectsFromReader(bytes.NewReader(stream), namespace) + if err != nil { + return nil, fmt.Errorf("failed to extract objects: %w", err) + } + for _, o := range unstructuredObjects { + if o.GetNamespace() != namespace { + continue + } + if _, ok := FilteredResources[o.GetObjectKind().GroupVersionKind().GroupKind()]; ok { + continue + } + objects = append(objects, o) + } + return objects, nil +} + func readProviderResourcesFromCluster(ctx context.Context, providerByName map[ProviderName]Provider) error { for name, provider := range providerByName { if err := provider.ReadResourcesFromCluster(ctx); err != nil { diff --git a/pkg/i2gw/providers/apisix/resource_reader.go b/pkg/i2gw/providers/apisix/resource_reader.go index 7e774304..133f895e 100644 --- a/pkg/i2gw/providers/apisix/resource_reader.go +++ b/pkg/i2gw/providers/apisix/resource_reader.go @@ -19,9 +19,9 @@ package apisix import ( "context" - "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw" - "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/common" "k8s.io/apimachinery/pkg/util/sets" + + "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw" ) // converter implements the i2gw.CustomResourceReader interface. @@ -40,7 +40,7 @@ func (r *resourceReader) readResourcesFromCluster(ctx context.Context) (*storage // read apisix related resources from cluster. storage := newResourcesStorage() - ingresses, err := common.ReadIngressesFromCluster(ctx, r.conf.Client, sets.New(ApisixIngressClass)) + ingresses, err := i2gw.ReadIngressesFromCluster(ctx, r.conf.Client, sets.New(ApisixIngressClass)) if err != nil { return nil, err } @@ -52,7 +52,7 @@ func (r *resourceReader) readResourcesFromFile(filename string) (*storage, error // read apisix related resources from file. storage := newResourcesStorage() - ingresses, err := common.ReadIngressesFromFile(filename, r.conf.Namespace, sets.New[string](ApisixIngressClass)) + ingresses, err := i2gw.ReadIngressesFromFile(filename, r.conf.Namespace, sets.New[string](ApisixIngressClass)) if err != nil { return nil, err } diff --git a/pkg/i2gw/providers/common/utils.go b/pkg/i2gw/providers/common/utils.go index c395bf36..994f23cc 100644 --- a/pkg/i2gw/providers/common/utils.go +++ b/pkg/i2gw/providers/common/utils.go @@ -24,6 +24,8 @@ import ( networkingv1beta1 "k8s.io/api/networking/v1beta1" "k8s.io/apimachinery/pkg/util/validation/field" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" + + "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw" ) func GetIngressClass(ingress networkingv1.Ingress) string { @@ -58,7 +60,7 @@ func GetRuleGroups(ingresses []networkingv1.Ingress) map[string]IngressRuleGroup ruleGroups := make(map[string]IngressRuleGroup) for _, ingress := range ingresses { - ingressClass := GetIngressClass(ingress) + ingressClass := i2gw.GetIngressClass(ingress) for _, rule := range ingress.Spec.Rules { diff --git a/pkg/i2gw/providers/ingressnginx/resource_reader.go b/pkg/i2gw/providers/ingressnginx/resource_reader.go index 383d2a6c..dc384ba5 100644 --- a/pkg/i2gw/providers/ingressnginx/resource_reader.go +++ b/pkg/i2gw/providers/ingressnginx/resource_reader.go @@ -19,9 +19,9 @@ package ingressnginx import ( "context" - "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw" - "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/common" "k8s.io/apimachinery/pkg/util/sets" + + "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw" ) // converter implements the i2gw.CustomResourceReader interface. @@ -39,7 +39,7 @@ func newResourceReader(conf *i2gw.ProviderConf) *resourceReader { func (r *resourceReader) readResourcesFromCluster(ctx context.Context) (*storage, error) { storage := newResourcesStorage() - ingresses, err := common.ReadIngressesFromCluster(ctx, r.conf.Client, sets.New(NginxIngressClass)) + ingresses, err := i2gw.ReadIngressesFromCluster(ctx, r.conf.Client, sets.New(NginxIngressClass)) if err != nil { return nil, err } @@ -50,7 +50,7 @@ func (r *resourceReader) readResourcesFromCluster(ctx context.Context) (*storage func (r *resourceReader) readResourcesFromFile(filename string) (*storage, error) { storage := newResourcesStorage() - ingresses, err := common.ReadIngressesFromFile(filename, r.conf.Namespace, sets.New(NginxIngressClass)) + ingresses, err := i2gw.ReadIngressesFromFile(filename, r.conf.Namespace, sets.New(NginxIngressClass)) if err != nil { return nil, err } diff --git a/pkg/i2gw/providers/istio/e2e_file_converter_test.go b/pkg/i2gw/providers/istio/e2e_file_converter_test.go index c9b35b18..84b26341 100644 --- a/pkg/i2gw/providers/istio/e2e_file_converter_test.go +++ b/pkg/i2gw/providers/istio/e2e_file_converter_test.go @@ -27,7 +27,6 @@ import ( "github.com/google/go-cmp/cmp" "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw" - "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/common" apiequality "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" @@ -99,7 +98,7 @@ func readGatewayResourcesFromFile(t *testing.T, filename string) (*i2gw.GatewayR return nil, fmt.Errorf("failed to read file %v: %w", filename, err) } - unstructuredObjects, err := common.ExtractObjectsFromReader(bytes.NewReader(stream), "") + unstructuredObjects, err := i2gw.ExtractObjectsFromReader(bytes.NewReader(stream), "") if err != nil { return nil, fmt.Errorf("failed to extract objects: %w", err) } diff --git a/pkg/i2gw/providers/istio/istio.go b/pkg/i2gw/providers/istio/istio.go index 97aa55b3..00d61383 100644 --- a/pkg/i2gw/providers/istio/istio.go +++ b/pkg/i2gw/providers/istio/istio.go @@ -29,6 +29,8 @@ const ProviderName = "istio" func init() { i2gw.ProviderConstructorByName[ProviderName] = NewProvider + i2gw.FilteredResources[gatewayGVK.GroupKind()] = struct{}{} + i2gw.FilteredResources[virtualServiceGVK.GroupKind()] = struct{}{} } type Provider struct { diff --git a/pkg/i2gw/providers/istio/resource_reader.go b/pkg/i2gw/providers/istio/resource_reader.go index 12959ea3..116442ae 100644 --- a/pkg/i2gw/providers/istio/resource_reader.go +++ b/pkg/i2gw/providers/istio/resource_reader.go @@ -24,7 +24,6 @@ import ( "os" "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw" - "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/common" istiov1beta1 "istio.io/client-go/pkg/apis/networking/v1beta1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" @@ -69,7 +68,7 @@ func (r *reader) readResourcesFromFile(_ context.Context, filename string) (*sto return nil, fmt.Errorf("failed to read file %v: %w", filename, err) } - unstructuredObjects, err := common.ExtractObjectsFromReader(bytes.NewReader(stream), r.conf.Namespace) + unstructuredObjects, err := i2gw.ExtractObjectsFromReader(bytes.NewReader(stream), r.conf.Namespace) if err != nil { return nil, fmt.Errorf("failed to extract objects: %w", err) } diff --git a/pkg/i2gw/providers/istio/types.go b/pkg/i2gw/providers/istio/types.go index f99084a3..11d7f48d 100644 --- a/pkg/i2gw/providers/istio/types.go +++ b/pkg/i2gw/providers/istio/types.go @@ -16,6 +16,8 @@ limitations under the License. package istio +import "k8s.io/apimachinery/pkg/runtime/schema" + const ( APIVersion = "networking.istio.io/v1beta1" GatewayKind = "Gateway" @@ -23,3 +25,8 @@ const ( K8SGatewayClassName = "istio" ) + +var ( + gatewayGVK = schema.GroupVersionKind{Group: "networking.istio.io", Version: "v1beta1", Kind: "Gateway"} + virtualServiceGVK = schema.GroupVersionKind{Group: "networking.istio.io", Version: "v1beta1", Kind: "VirtualService"} +) diff --git a/pkg/i2gw/providers/kong/kong.go b/pkg/i2gw/providers/kong/kong.go index b606a34c..0ab15168 100644 --- a/pkg/i2gw/providers/kong/kong.go +++ b/pkg/i2gw/providers/kong/kong.go @@ -30,6 +30,7 @@ const KongIngressClass = "kong" func init() { i2gw.ProviderConstructorByName[Name] = NewProvider + i2gw.FilteredResources[tcpIngressGVK.GroupKind()] = struct{}{} } // Provider implements the i2gw.Provider interface. diff --git a/pkg/i2gw/providers/kong/resource_reader.go b/pkg/i2gw/providers/kong/resource_reader.go index a44601af..c4a783bf 100644 --- a/pkg/i2gw/providers/kong/resource_reader.go +++ b/pkg/i2gw/providers/kong/resource_reader.go @@ -30,7 +30,6 @@ import ( kongv1beta1 "github.com/kong/kubernetes-ingress-controller/v2/pkg/apis/configuration/v1beta1" "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw" - "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/common" ) // converter implements the i2gw.CustomResourceReader interface. @@ -52,7 +51,7 @@ func newResourceReader(conf *i2gw.ProviderConf) *resourceReader { func (r *resourceReader) readResourcesFromCluster(ctx context.Context) (*storage, error) { storage := newResourceStorage() - ingresses, err := common.ReadIngressesFromCluster(ctx, r.conf.Client, sets.New(KongIngressClass)) + ingresses, err := i2gw.ReadIngressesFromCluster(ctx, r.conf.Client, sets.New(KongIngressClass)) if err != nil { return nil, err } @@ -70,7 +69,7 @@ func (r *resourceReader) readResourcesFromCluster(ctx context.Context) (*storage func (r *resourceReader) readResourcesFromFile(filename string) (*storage, error) { storage := newResourceStorage() - ingresses, err := common.ReadIngressesFromFile(filename, r.conf.Namespace, sets.New(KongIngressClass)) + ingresses, err := i2gw.ReadIngressesFromFile(filename, r.conf.Namespace, sets.New(KongIngressClass)) if err != nil { return nil, err } @@ -122,7 +121,7 @@ func (r *resourceReader) readTCPIngressesFromFile(filename string) ([]kongv1beta } reader := bytes.NewReader(stream) - objs, err := common.ExtractObjectsFromReader(reader, r.conf.Namespace) + objs, err := i2gw.ExtractObjectsFromReader(reader, r.conf.Namespace) if err != nil { return nil, err } diff --git a/pkg/i2gw/providers/openapi3/converter_test.go b/pkg/i2gw/providers/openapi3/converter_test.go index d994b8f7..d398767f 100644 --- a/pkg/i2gw/providers/openapi3/converter_test.go +++ b/pkg/i2gw/providers/openapi3/converter_test.go @@ -36,7 +36,6 @@ import ( "github.com/google/go-cmp/cmp" "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw" - "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/common" ) const fixturesDir = "./fixtures" @@ -158,7 +157,7 @@ func readGatewayResourcesFromFile(t *testing.T, filename string) (*i2gw.GatewayR return nil, fmt.Errorf("failed to read file %v: %w", filename, err) } - unstructuredObjects, err := common.ExtractObjectsFromReader(bytes.NewReader(stream), "") + unstructuredObjects, err := i2gw.ExtractObjectsFromReader(bytes.NewReader(stream), "") if err != nil { return nil, fmt.Errorf("failed to extract objects: %w", err) } diff --git a/pkg/i2gw/providers/common/resource_reader.go b/pkg/i2gw/resource_reader.go similarity index 93% rename from pkg/i2gw/providers/common/resource_reader.go rename to pkg/i2gw/resource_reader.go index dca2cbe0..f09e1ed1 100644 --- a/pkg/i2gw/providers/common/resource_reader.go +++ b/pkg/i2gw/resource_reader.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package common +package i2gw import ( "bytes" @@ -52,14 +52,9 @@ func ReadIngressesFromCluster(ctx context.Context, client client.Client, ingress } func ReadIngressesFromFile(filename, namespace string, ingressClasses sets.Set[string]) (map[types.NamespacedName]*networkingv1.Ingress, error) { - stream, err := os.ReadFile(filename) + unstructuredObjects, err := ReadResourcesFromFile(filename, namespace) if err != nil { - return nil, fmt.Errorf("failed to read file %v: %w", filename, err) - } - - unstructuredObjects, err := ExtractObjectsFromReader(bytes.NewReader(stream), namespace) - if err != nil { - return nil, fmt.Errorf("failed to extract objects: %w", err) + return nil, fmt.Errorf("failed to read objects: %w", err) } ingresses := map[types.NamespacedName]*networkingv1.Ingress{} @@ -81,6 +76,19 @@ func ReadIngressesFromFile(filename, namespace string, ingressClasses sets.Set[s return ingresses, nil } +func ReadResourcesFromFile(filename, namespace string) ([]*unstructured.Unstructured, error) { + stream, err := os.ReadFile(filename) + if err != nil { + return nil, fmt.Errorf("failed to read file %v: %w", filename, err) + } + + unstructuredObjects, err := ExtractObjectsFromReader(bytes.NewReader(stream), namespace) + if err != nil { + return nil, fmt.Errorf("failed to extract objects: %w", err) + } + return unstructuredObjects, nil +} + // ExtractObjectsFromReader extracts all objects from a reader, // which is created from YAML or JSON input files. // It retrieves all objects, including nested ones if they are contained within a list. diff --git a/pkg/i2gw/providers/common/resource_reader_test.go b/pkg/i2gw/resource_reader_test.go similarity index 99% rename from pkg/i2gw/providers/common/resource_reader_test.go rename to pkg/i2gw/resource_reader_test.go index 865958e5..c55f02b7 100644 --- a/pkg/i2gw/providers/common/resource_reader_test.go +++ b/pkg/i2gw/resource_reader_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package common +package i2gw import ( "bytes" diff --git a/pkg/i2gw/resources.go b/pkg/i2gw/resources.go new file mode 100644 index 00000000..7c78636f --- /dev/null +++ b/pkg/i2gw/resources.go @@ -0,0 +1,34 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package i2gw + +import ( + networkingv1 "k8s.io/api/networking/v1" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +var ( + ingressClassGK = schema.GroupKind{Group: networkingv1.GroupName, Kind: "IngressClass"} + ingressGK = schema.GroupKind{Group: networkingv1.GroupName, Kind: "Ingress"} +) + +var ( + FilteredResources = map[schema.GroupKind]struct{}{ + ingressClassGK: {}, + ingressGK: {}, + } +) diff --git a/pkg/i2gw/providers/common/testdata/input-file.json b/pkg/i2gw/testdata/input-file.json similarity index 100% rename from pkg/i2gw/providers/common/testdata/input-file.json rename to pkg/i2gw/testdata/input-file.json diff --git a/pkg/i2gw/providers/common/testdata/input-file.yaml b/pkg/i2gw/testdata/input-file.yaml similarity index 100% rename from pkg/i2gw/providers/common/testdata/input-file.yaml rename to pkg/i2gw/testdata/input-file.yaml diff --git a/pkg/i2gw/utils.go b/pkg/i2gw/utils.go new file mode 100644 index 00000000..bee20812 --- /dev/null +++ b/pkg/i2gw/utils.go @@ -0,0 +1,36 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package i2gw + +import ( + networkingv1 "k8s.io/api/networking/v1" + networkingv1beta1 "k8s.io/api/networking/v1beta1" +) + +func GetIngressClass(ingress networkingv1.Ingress) string { + var ingressClass string + + if ingress.Spec.IngressClassName != nil && *ingress.Spec.IngressClassName != "" { + ingressClass = *ingress.Spec.IngressClassName + } else if _, ok := ingress.Annotations[networkingv1beta1.AnnotationIngressClass]; ok { + ingressClass = ingress.Annotations[networkingv1beta1.AnnotationIngressClass] + } else { + ingressClass = ingress.Name + } + + return ingressClass +} From 25cff8860238d314664abb96a0c1aaafa66b7cdf Mon Sep 17 00:00:00 2001 From: Mattia Lavacca Date: Wed, 5 Jun 2024 10:31:06 +0200 Subject: [PATCH 2/2] address review comments Signed-off-by: Mattia Lavacca --- cmd/print.go | 2 +- pkg/i2gw/ingress2gateway.go | 11 +++++++++-- pkg/i2gw/provider.go | 4 ++++ pkg/i2gw/providers/apisix/apisix.go | 5 +++++ pkg/i2gw/providers/gce/gce.go | 5 +++++ pkg/i2gw/providers/gce/resource_reader.go | 5 ++--- pkg/i2gw/providers/ingressnginx/ingressnginx.go | 5 +++++ pkg/i2gw/providers/istio/istio.go | 10 ++++++++-- pkg/i2gw/providers/istio/resource_reader.go | 6 +++--- pkg/i2gw/providers/istio/types.go | 10 +++++++--- pkg/i2gw/providers/kong/kong.go | 8 +++++++- pkg/i2gw/providers/openapi3/openapi.go | 5 +++++ 12 files changed, 61 insertions(+), 15 deletions(-) diff --git a/cmd/print.go b/cmd/print.go index 378834b5..333cebe6 100644 --- a/cmd/print.go +++ b/cmd/print.go @@ -195,7 +195,7 @@ func newPrintCommand() *cobra.Command { if specified with --namespace.`) cmd.Flags().BoolVar(&pr.allResources, "all-resources", false, - `If present, list all the objects across the selected namespaces. This flag can be set only when reading from a file.`) + `If present, list all the objects across the selected namespaces. This flag can be set only when the --input-file flag is set as well.`) cmd.Flags().StringSliceVar(&pr.providers, "providers", i2gw.GetSupportedProviders(), fmt.Sprintf("If present, the tool will try to convert only resources related to the specified providers, supported values are %v.", i2gw.GetSupportedProviders())) diff --git a/pkg/i2gw/ingress2gateway.go b/pkg/i2gw/ingress2gateway.go index 415fc7c9..1fa7a434 100644 --- a/pkg/i2gw/ingress2gateway.go +++ b/pkg/i2gw/ingress2gateway.go @@ -63,7 +63,7 @@ func GetResources(ctx context.Context, namespace string, inputFile string, allre return nil, err } if allresources { - if genericResources, err = readGenericResourcesFromFile(inputFile, namespace); err != nil { + if genericResources, err = readGenericResourcesFromFile(inputFile, namespace, providerByName); err != nil { return nil, err } } @@ -133,7 +133,7 @@ func readProviderResourcesFromFile(ctx context.Context, providerByName map[Provi return nil } -func readGenericResourcesFromFile(inputFile, namespace string) ([]client.Object, error) { +func readGenericResourcesFromFile(inputFile, namespace string, providerByName map[ProviderName]Provider) ([]client.Object, error) { objects := make([]client.Object, 0) stream, err := os.ReadFile(inputFile) if err != nil { @@ -144,6 +144,13 @@ func readGenericResourcesFromFile(inputFile, namespace string) ([]client.Object, if err != nil { return nil, fmt.Errorf("failed to extract objects: %w", err) } + + for _, p := range providerByName { + for _, crd := range p.GetCRDs() { + FilteredResources[crd] = struct{}{} + } + } + for _, o := range unstructuredObjects { if o.GetNamespace() != namespace { continue diff --git a/pkg/i2gw/provider.go b/pkg/i2gw/provider.go index c7141998..d5733f65 100644 --- a/pkg/i2gw/provider.go +++ b/pkg/i2gw/provider.go @@ -21,6 +21,7 @@ import ( "sync" networkingv1 "k8s.io/api/networking/v1" + "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" @@ -55,6 +56,9 @@ type ProviderConf struct { type Provider interface { CustomResourceReader ResourceConverter + + // GetCRDs returns the list of CustomResourceDefinitions associated with the Provider. + GetCRDs() []schema.GroupKind } type CustomResourceReader interface { diff --git a/pkg/i2gw/providers/apisix/apisix.go b/pkg/i2gw/providers/apisix/apisix.go index 585424c0..9478b8f1 100644 --- a/pkg/i2gw/providers/apisix/apisix.go +++ b/pkg/i2gw/providers/apisix/apisix.go @@ -21,6 +21,7 @@ import ( "fmt" "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" ) @@ -73,3 +74,7 @@ func (p *Provider) ReadResourcesFromFile(_ context.Context, filename string) err p.storage = storage return nil } + +func (p *Provider) GetCRDs() []schema.GroupKind { + return nil +} diff --git a/pkg/i2gw/providers/gce/gce.go b/pkg/i2gw/providers/gce/gce.go index 602a0c5b..01df66cb 100644 --- a/pkg/i2gw/providers/gce/gce.go +++ b/pkg/i2gw/providers/gce/gce.go @@ -21,6 +21,7 @@ import ( "fmt" "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" ) @@ -69,3 +70,7 @@ func (p *Provider) ReadResourcesFromFile(_ context.Context, filename string) err func (p *Provider) ToGatewayAPI() (i2gw.GatewayResources, field.ErrorList) { return p.converter.convert(p.storage) } + +func (p *Provider) GetCRDs() []schema.GroupKind { + return nil +} diff --git a/pkg/i2gw/providers/gce/resource_reader.go b/pkg/i2gw/providers/gce/resource_reader.go index be749776..7b8bb20a 100644 --- a/pkg/i2gw/providers/gce/resource_reader.go +++ b/pkg/i2gw/providers/gce/resource_reader.go @@ -20,7 +20,6 @@ import ( "context" "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw" - "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/common" "k8s.io/apimachinery/pkg/util/sets" ) @@ -45,7 +44,7 @@ func newResourceReader(conf *i2gw.ProviderConf) reader { func (r *reader) readResourcesFromCluster(ctx context.Context) (*storage, error) { storage := newResourcesStorage() - ingresses, err := common.ReadIngressesFromCluster(ctx, r.conf.Client, supportedGCEIngressClass) + ingresses, err := i2gw.ReadIngressesFromCluster(ctx, r.conf.Client, supportedGCEIngressClass) if err != nil { return nil, err } @@ -56,7 +55,7 @@ func (r *reader) readResourcesFromCluster(ctx context.Context) (*storage, error) func (r *reader) readResourcesFromFile(filename string) (*storage, error) { storage := newResourcesStorage() - ingresses, err := common.ReadIngressesFromFile(filename, r.conf.Namespace, supportedGCEIngressClass) + ingresses, err := i2gw.ReadIngressesFromFile(filename, r.conf.Namespace, supportedGCEIngressClass) if err != nil { return nil, err } diff --git a/pkg/i2gw/providers/ingressnginx/ingressnginx.go b/pkg/i2gw/providers/ingressnginx/ingressnginx.go index 8e0a33b7..332f9292 100644 --- a/pkg/i2gw/providers/ingressnginx/ingressnginx.go +++ b/pkg/i2gw/providers/ingressnginx/ingressnginx.go @@ -21,6 +21,7 @@ import ( "fmt" "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" ) @@ -73,3 +74,7 @@ func (p *Provider) ReadResourcesFromFile(_ context.Context, filename string) err p.storage = storage return nil } + +func (p *Provider) GetCRDs() []schema.GroupKind { + return nil +} diff --git a/pkg/i2gw/providers/istio/istio.go b/pkg/i2gw/providers/istio/istio.go index 00d61383..b291d1f2 100644 --- a/pkg/i2gw/providers/istio/istio.go +++ b/pkg/i2gw/providers/istio/istio.go @@ -21,6 +21,7 @@ import ( "fmt" "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" ) @@ -29,8 +30,6 @@ const ProviderName = "istio" func init() { i2gw.ProviderConstructorByName[ProviderName] = NewProvider - i2gw.FilteredResources[gatewayGVK.GroupKind()] = struct{}{} - i2gw.FilteredResources[virtualServiceGVK.GroupKind()] = struct{}{} } type Provider struct { @@ -72,3 +71,10 @@ func (p *Provider) ReadResourcesFromFile(ctx context.Context, filename string) e p.storage = storage return nil } + +func (p *Provider) GetCRDs() []schema.GroupKind { + return []schema.GroupKind{ + gatewayGK, + virtualServiceGK, + } +} diff --git a/pkg/i2gw/providers/istio/resource_reader.go b/pkg/i2gw/providers/istio/resource_reader.go index 116442ae..0c7f77fb 100644 --- a/pkg/i2gw/providers/istio/resource_reader.go +++ b/pkg/i2gw/providers/istio/resource_reader.go @@ -85,7 +85,7 @@ func (r *reader) readUnstructuredObjects(objects []*unstructured.Unstructured) ( res := newResourcesStorage() for _, obj := range objects { - if obj.GetAPIVersion() != APIVersion { + if obj.GetAPIVersion() != APIVersion.String() { log.Printf("%v provider: skipped resource with unsupported APIVersion: %v", ProviderName, obj.GetAPIVersion()) continue } @@ -122,7 +122,7 @@ func (r *reader) readUnstructuredObjects(objects []*unstructured.Unstructured) ( func (r *reader) readGatewaysFromCluster(ctx context.Context) (map[types.NamespacedName]*istiov1beta1.Gateway, error) { gatewayList := &unstructured.UnstructuredList{} - gatewayList.SetAPIVersion(APIVersion) + gatewayList.SetAPIVersion(APIVersion.String()) gatewayList.SetKind(GatewayKind) err := r.conf.Client.List(ctx, gatewayList) @@ -152,7 +152,7 @@ func (r *reader) readGatewaysFromCluster(ctx context.Context) (map[types.Namespa func (r *reader) readVirtualServicesFromCluster(ctx context.Context) (map[types.NamespacedName]*istiov1beta1.VirtualService, error) { virtualServicesList := &unstructured.UnstructuredList{} - virtualServicesList.SetAPIVersion(APIVersion) + virtualServicesList.SetAPIVersion(APIVersion.String()) virtualServicesList.SetKind(VirtualServiceKind) err := r.conf.Client.List(ctx, virtualServicesList) diff --git a/pkg/i2gw/providers/istio/types.go b/pkg/i2gw/providers/istio/types.go index 11d7f48d..16921bbe 100644 --- a/pkg/i2gw/providers/istio/types.go +++ b/pkg/i2gw/providers/istio/types.go @@ -19,7 +19,6 @@ package istio import "k8s.io/apimachinery/pkg/runtime/schema" const ( - APIVersion = "networking.istio.io/v1beta1" GatewayKind = "Gateway" VirtualServiceKind = "VirtualService" @@ -27,6 +26,11 @@ const ( ) var ( - gatewayGVK = schema.GroupVersionKind{Group: "networking.istio.io", Version: "v1beta1", Kind: "Gateway"} - virtualServiceGVK = schema.GroupVersionKind{Group: "networking.istio.io", Version: "v1beta1", Kind: "VirtualService"} + APIVersion = schema.GroupVersion{ + Group: "networking.istio.io", + Version: "v1beta1", + } + + gatewayGK = schema.GroupKind{Group: APIVersion.Group, Kind: GatewayKind} + virtualServiceGK = schema.GroupKind{Group: APIVersion.Group, Kind: VirtualServiceKind} ) diff --git a/pkg/i2gw/providers/kong/kong.go b/pkg/i2gw/providers/kong/kong.go index 0ab15168..f74a6f6b 100644 --- a/pkg/i2gw/providers/kong/kong.go +++ b/pkg/i2gw/providers/kong/kong.go @@ -19,6 +19,7 @@ package kong import ( "context" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw" @@ -30,7 +31,6 @@ const KongIngressClass = "kong" func init() { i2gw.ProviderConstructorByName[Name] = NewProvider - i2gw.FilteredResources[tcpIngressGVK.GroupKind()] = struct{}{} } // Provider implements the i2gw.Provider interface. @@ -71,3 +71,9 @@ func (p *Provider) ReadResourcesFromFile(_ context.Context, filename string) err p.storage = storage return nil } + +func (p *Provider) GetCRDs() []schema.GroupKind { + return []schema.GroupKind{ + tcpIngressGVK.GroupKind(), + } +} diff --git a/pkg/i2gw/providers/openapi3/openapi.go b/pkg/i2gw/providers/openapi3/openapi.go index bb8da559..2d7cafea 100644 --- a/pkg/i2gw/providers/openapi3/openapi.go +++ b/pkg/i2gw/providers/openapi3/openapi.go @@ -21,6 +21,7 @@ import ( "fmt" "github.com/getkin/kin-openapi/openapi3" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw" @@ -107,3 +108,7 @@ func readSpecFromFile(ctx context.Context, filename string) (*openapi3.T, error) return spec, nil } + +func (p *Provider) GetCRDs() []schema.GroupKind { + return nil +}