From c83f60b82855882773c9c6c9924be72cbffa6411 Mon Sep 17 00:00:00 2001 From: jjaferson Date: Thu, 27 Jan 2022 17:34:40 +0000 Subject: [PATCH 1/2] Adds Gateway API HTTPRoute integration --- cmd/generate.go | 1 + cmd/generate_gatewayapi.go | 17 +++ cmd/generate_gatewayapi_httproute.go | 161 ++++++++++++++++++++++++ examples/oas3/gateway-api-petstore.yaml | 36 ++++++ go.mod | 2 +- go.sum | 32 ++++- pkg/gatewayapi/http_route.go | 74 +++++++++++ 7 files changed, 317 insertions(+), 6 deletions(-) create mode 100644 cmd/generate_gatewayapi.go create mode 100644 cmd/generate_gatewayapi_httproute.go create mode 100644 examples/oas3/gateway-api-petstore.yaml create mode 100644 pkg/gatewayapi/http_route.go diff --git a/cmd/generate.go b/cmd/generate.go index b94c30c..3347d47 100644 --- a/cmd/generate.go +++ b/cmd/generate.go @@ -13,6 +13,7 @@ func generateCommand() *cobra.Command { cmd.AddCommand(generateIstioCommand()) cmd.AddCommand(generateKuadrantCommand()) + cmd.AddCommand(generateGatewayAPICommand()) return cmd } diff --git a/cmd/generate_gatewayapi.go b/cmd/generate_gatewayapi.go new file mode 100644 index 0000000..8c6e583 --- /dev/null +++ b/cmd/generate_gatewayapi.go @@ -0,0 +1,17 @@ +package cmd + +import ( + "github.com/spf13/cobra" +) + +func generateGatewayAPICommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "gatewayapi", + Short: "Generate Gataway API resources", + Long: "Generate Gataway API resources", + } + + cmd.AddCommand(generateGatewayApiHttpRouteCommand()) + + return cmd +} diff --git a/cmd/generate_gatewayapi_httproute.go b/cmd/generate_gatewayapi_httproute.go new file mode 100644 index 0000000..202ecf4 --- /dev/null +++ b/cmd/generate_gatewayapi_httproute.go @@ -0,0 +1,161 @@ +package cmd + +import ( + "encoding/json" + "fmt" + + "github.com/getkin/kin-openapi/openapi3" + "github.com/kuadrant/kuadrantctl/pkg/gatewayapi" + "github.com/kuadrant/kuadrantctl/pkg/utils" + "github.com/spf13/cobra" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gatewayapiv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" +) + +var ( + generateGatewayAPIHTTPRouteOAS string + generateGatewayAPIHTTPRouteHost string + generateGatewayAPIHTTPRouteSvcName string + generateGatewayAPIHTTPRouteSvcNamespace string + generateGatewayAPIHTTPRouteSvcPort int32 + generateGatewayAPIHTTPRouteGateways []string +) + +//kuadrantctl generate istio virtualservice --namespace myns --oas petstore.yaml --public-host www.kuadrant.io --service-name myservice --gateway kuadrant-gateway +// --namespace myns +// --service-name myservice +// --public-host www.kuadrant.io +// --gateway kuadrant-gateway +// -- service-port 80 + +func generateGatewayApiHttpRouteCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "httproute", + Short: "Generate Gateway API HTTPRoute from OpenAPI 3.x", + Long: "Generate Gateway API HTTPRoute from OpenAPI 3.x", + RunE: func(cmd *cobra.Command, args []string) error { + return generateGatewayApiHttpRoute(cmd, args) + }, + } + + // OpenAPI ref + cmd.Flags().StringVar(&generateGatewayAPIHTTPRouteOAS, "oas", "", "/path/to/file.[json|yaml|yml] OR http[s]://domain/resource/path.[json|yaml|yml] OR - (required)") + err := cmd.MarkFlagRequired("oas") + if err != nil { + panic(err) + } + + // service ref + cmd.Flags().StringVar(&generateGatewayAPIHTTPRouteSvcName, "service-name", "", "Service name (required)") + err = cmd.MarkFlagRequired("service-name") + if err != nil { + panic(err) + } + + // service namespace + cmd.Flags().StringVarP(&generateGatewayAPIHTTPRouteSvcNamespace, "namespace", "n", "", "Service namespace (required)") + err = cmd.MarkFlagRequired("namespace") + if err != nil { + panic(err) + } + + // service host + cmd.Flags().StringVar(&generateGatewayAPIHTTPRouteHost, "public-host", "", "Public host (required)") + err = cmd.MarkFlagRequired("public-host") + if err != nil { + panic(err) + } + + // service port + cmd.Flags().Int32VarP(&generateGatewayAPIHTTPRouteSvcPort, "port", "p", 80, "Service Port (required)") + + // gateway + cmd.Flags().StringSliceVar(&generateGatewayAPIHTTPRouteGateways, "gateway", []string{}, "Gateways (required)") + err = cmd.MarkFlagRequired("gateway") + if err != nil { + panic(err) + } + + return cmd +} + +func generateGatewayApiHttpRoute(cmd *cobra.Command, args []string) error { + oasDataRaw, err := utils.ReadExternalResource(generateGatewayAPIHTTPRouteOAS) + if err != nil { + return err + } + + openapiLoader := openapi3.NewLoader() + doc, err := openapiLoader.LoadFromData(oasDataRaw) + if err != nil { + return err + } + + err = doc.Validate(openapiLoader.Context) + if err != nil { + return fmt.Errorf("OpenAPI validation error: %w", err) + } + + httpRoute, err := generateGatewayAPIHTTPRoute(cmd, doc) + if err != nil { + return err + } + + jsonData, err := json.Marshal(httpRoute) + if err != nil { + return err + } + + fmt.Fprintln(cmd.OutOrStdout(), string(jsonData)) + return nil +} + +func generateGatewayAPIHTTPRoute(cmd *cobra.Command, doc *openapi3.T) (*gatewayapiv1alpha2.HTTPRoute, error) { + + //loop through gateway + // https://github.com/getkin/kin-openapi + gatewaysRef := []gatewayapiv1alpha2.ParentRef{} + for _, gateway := range generateGatewayAPIHTTPRouteGateways { + gatewaysRef = append(gatewaysRef, gatewayapiv1alpha2.ParentRef{ + Name: gatewayapiv1alpha2.ObjectName(gateway), + }) + } + + port := gatewayapiv1alpha2.PortNumber(generateGatewayAPIHTTPRouteSvcPort) + service := fmt.Sprintf("%s.%s.svc", generateGatewayAPIHTTPRouteSvcName, generateGatewayAPIHTTPRouteSvcNamespace) + matches, err := gatewayapi.HTTPRouteMatchesFromOAS(doc) + if err != nil { + return nil, err + } + + httpRoute := gatewayapiv1alpha2.HTTPRoute{ + TypeMeta: v1.TypeMeta{ + Kind: "HTTPRoute", + APIVersion: "gateway.networking.k8s.io/v1alpha2", + }, + Spec: gatewayapiv1alpha2.HTTPRouteSpec{ + CommonRouteSpec: gatewayapiv1alpha2.CommonRouteSpec{ + ParentRefs: gatewaysRef, + }, + Hostnames: []gatewayapiv1alpha2.Hostname{ + gatewayapiv1alpha2.Hostname(generateGatewayAPIHTTPRouteHost), + }, + Rules: []gatewayapiv1alpha2.HTTPRouteRule{ + { + BackendRefs: []gatewayapiv1alpha2.HTTPBackendRef{ + { + BackendRef: gatewayapiv1alpha2.BackendRef{ + BackendObjectReference: gatewayapiv1alpha2.BackendObjectReference{ + Name: gatewayapiv1alpha2.ObjectName(service), + Port: &port, + }, + }, + }, + }, + Matches: matches, + }, + }, + }, + } + return &httpRoute, nil +} diff --git a/examples/oas3/gateway-api-petstore.yaml b/examples/oas3/gateway-api-petstore.yaml new file mode 100644 index 0000000..9a54d97 --- /dev/null +++ b/examples/oas3/gateway-api-petstore.yaml @@ -0,0 +1,36 @@ +--- +openapi: "3.0.0" +info: + title: "Pet Store API" + version: "1.0.0" +servers: + - url: https://toplevel.example.io/v1 +paths: + /cat: + get: + parameters: + - in: header + name: X-username + schema: + type: string + operationId: "getCat" + responses: + 405: + description: "invalid input" + post: + operationId: "postCat" + responses: + 405: + description: "invalid input" + /dog: + get: + parameters: + - in: query + name: pathprefix + required: true + schema: + type: string + operationId: "getDog" + responses: + 405: + description: "invalid input" diff --git a/go.mod b/go.mod index a923a6a..c4c1c36 100644 --- a/go.mod +++ b/go.mod @@ -19,6 +19,6 @@ require ( k8s.io/apimachinery v0.22.1 k8s.io/client-go v0.22.1 sigs.k8s.io/controller-runtime v0.10.0 - sigs.k8s.io/gateway-api v0.3.0 + sigs.k8s.io/gateway-api v0.4.1 sigs.k8s.io/yaml v1.2.0 ) diff --git a/go.sum b/go.sum index ddd39dd..6adb08e 100644 --- a/go.sum +++ b/go.sum @@ -84,6 +84,7 @@ github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/ github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/ahmetb/gen-crd-api-reference-docs v0.2.1-0.20201224172655-df869c1245d4/go.mod h1:TdjdkYhlOifCQWPs1UdTma97kQQMozf5h26hTuG70u8= +github.com/ahmetb/gen-crd-api-reference-docs v0.3.0/go.mod h1:TdjdkYhlOifCQWPs1UdTma97kQQMozf5h26hTuG70u8= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -190,6 +191,7 @@ github.com/evanphx/json-patch v4.11.0+incompatible h1:glyUF9yIYtMHzn8xaKw5rMhdWc github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= @@ -278,6 +280,7 @@ github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gobuffalo/flect v0.2.2/go.mod h1:vmkQwuZYhN5Pc4ljYQZzP+1sq+NEkK+lh20jmEmX3jc= +github.com/gobuffalo/flect v0.2.3/go.mod h1:vmkQwuZYhN5Pc4ljYQZzP+1sq+NEkK+lh20jmEmX3jc= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= @@ -337,8 +340,9 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -551,6 +555,7 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= +github.com/onsi/gomega v1.14.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= github.com/onsi/gomega v1.15.0 h1:WjP/FQ/sk43MRmnEcT+MlDw2TFvkrXlprrPST/IudjU= github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= @@ -756,6 +761,7 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.19.0 h1:mZQZefskPPCMIBCSEH0v2/iUqqLrYtaeqwD6FUGUnFE= go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -965,6 +971,7 @@ golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2 h1:c8PlLMqBbOHoqtjteWm5/kbe6rNY2pbRfbIMVnepueo= golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -1056,8 +1063,9 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.2 h1:kRBLX7v7Af8W7Gdbbc908OJcdgtK8bOz9Uaj8/F1ACA= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1245,12 +1253,14 @@ k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= k8s.io/api v0.20.2/go.mod h1:d7n6Ehyzx+S+cE3VhTGfVNNqtGc/oL9DCdYYahlurV8= k8s.io/api v0.21.0/go.mod h1:+YbrhBBGgsxbF6o6Kj4KJPJnBmAKuXDeS3E18bgHNVU= k8s.io/api v0.21.2/go.mod h1:Lv6UGJZ1rlMI1qusN8ruAp9PUBFyBwpEHAdG24vIsiU= +k8s.io/api v0.21.3/go.mod h1:hUgeYHUbBp23Ue4qdX9tR8/ANi/g3ehylAqDn9NWVOg= k8s.io/api v0.22.1 h1:ISu3tD/jRhYfSW8jI/Q1e+lRxkR7w9UwQEZ7FgslrwY= k8s.io/api v0.22.1/go.mod h1:bh13rkTp3F1XEaLGykbyRD2QaTTzPm0e/BMd8ptFONY= k8s.io/apiextensions-apiserver v0.19.2/go.mod h1:EYNjpqIAvNZe+svXVx9j4uBaVhTB4C94HkY3w058qcg= k8s.io/apiextensions-apiserver v0.20.1/go.mod h1:ntnrZV+6a3dB504qwC5PN/Yg9PBiDNt1EVqbW2kORVk= k8s.io/apiextensions-apiserver v0.20.2/go.mod h1:F6TXp389Xntt+LUq3vw6HFOLttPa0V8821ogLGwb6Zs= k8s.io/apiextensions-apiserver v0.21.2/go.mod h1:+Axoz5/l3AYpGLlhJDfcVQzCerVYq3K3CvDMvw6X1RA= +k8s.io/apiextensions-apiserver v0.21.3/go.mod h1:kl6dap3Gd45+21Jnh6utCx8Z2xxLm8LGDkprcd+KbsE= k8s.io/apiextensions-apiserver v0.22.1 h1:YSJYzlFNFSfUle+yeEXX0lSQyLEoxoPJySRupepb0gE= k8s.io/apiextensions-apiserver v0.22.1/go.mod h1:HeGmorjtRmRLE+Q8dJu6AYRoZccvCMsghwS8XTUYb2c= k8s.io/apimachinery v0.18.1/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= @@ -1259,12 +1269,14 @@ k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRp k8s.io/apimachinery v0.20.2/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.21.0/go.mod h1:jbreFvJo3ov9rj7eWT7+sYiRx+qZuCYXwWT1bcDswPY= k8s.io/apimachinery v0.21.2/go.mod h1:CdTY8fU/BlvAbJ2z/8kBwimGki5Zp8/fbVuLY8gJumM= +k8s.io/apimachinery v0.21.3/go.mod h1:H/IM+5vH9kZRNJ4l3x/fXP/5bOPJaVP/guptnZPeCFI= k8s.io/apimachinery v0.22.1 h1:DTARnyzmdHMz7bFWFDDm22AM4pLWTQECMpRTFu2d2OM= k8s.io/apimachinery v0.22.1/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= k8s.io/apiserver v0.19.2/go.mod h1:FreAq0bJ2vtZFj9Ago/X0oNGC51GfubKK/ViOKfVAOA= k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= k8s.io/apiserver v0.20.2/go.mod h1:2nKd93WyMhZx4Hp3RfgH2K5PhwyTrprrkWYnI7id7jA= k8s.io/apiserver v0.21.2/go.mod h1:lN4yBoGyiNT7SC1dmNk0ue6a5Wi6O3SWOIw91TsucQw= +k8s.io/apiserver v0.21.3/go.mod h1:eDPWlZG6/cCCMj/JBcEpDoK+I+6i3r9GsChYBHSbAzU= k8s.io/apiserver v0.22.1/go.mod h1:2mcM6dzSt+XndzVQJX21Gx0/Klo7Aen7i0Ai6tIa400= k8s.io/client-go v0.18.1/go.mod h1:iCikYRiXOj/yRRFE/aWqrpPtDt4P2JVWhtHkmESTcfY= k8s.io/client-go v0.19.2/go.mod h1:S5wPhCqyDNAlzM9CnEdgTGV4OqhsW3jGO1UM1epwfJA= @@ -1272,6 +1284,7 @@ k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= k8s.io/client-go v0.20.2/go.mod h1:kH5brqWqp7HDxUFKoEgiI4v8G1xzbe9giaCenUWJzgE= k8s.io/client-go v0.21.0/go.mod h1:nNBytTF9qPFDEhoqgEPaarobC8QPae13bElIVHzIglA= k8s.io/client-go v0.21.2/go.mod h1:HdJ9iknWpbl3vMGtib6T2PyI/VYxiZfq936WNVHBRrA= +k8s.io/client-go v0.21.3/go.mod h1:+VPhCgTsaFmGILxR/7E1N0S+ryO010QBeNCv5JwRGYU= k8s.io/client-go v0.22.1 h1:jW0ZSHi8wW260FvcXHkIa0NLxFBQszTlhiAVsU5mopw= k8s.io/client-go v0.22.1/go.mod h1:BquC5A4UOo4qVDUtoc04/+Nxp1MeHcVc1HJm1KmG8kk= k8s.io/code-generator v0.19.2/go.mod h1:moqLn7w0t9cMs4+5CQyxnfA/HV8MF6aAVENF+WZZhgk= @@ -1279,11 +1292,14 @@ k8s.io/code-generator v0.20.1/go.mod h1:UsqdF+VX4PU2g46NC2JRs4gc+IfrctnwHb76RNbW k8s.io/code-generator v0.20.2/go.mod h1:UsqdF+VX4PU2g46NC2JRs4gc+IfrctnwHb76RNbWHJg= k8s.io/code-generator v0.21.0/go.mod h1:hUlps5+9QaTrKx+jiM4rmq7YmH8wPOIko64uZCHDh6Q= k8s.io/code-generator v0.21.2/go.mod h1:8mXJDCB7HcRo1xiEQstcguZkbxZaqeUOrO9SsicWs3U= +k8s.io/code-generator v0.21.3/go.mod h1:K3y0Bv9Cz2cOW2vXUrNZlFbflhuPvuadW6JdnN6gGKo= +k8s.io/code-generator v0.22.0/go.mod h1:eV77Y09IopzeXOJzndrDyCI88UBok2h6WxAlBwpxa+o= k8s.io/code-generator v0.22.1/go.mod h1:eV77Y09IopzeXOJzndrDyCI88UBok2h6WxAlBwpxa+o= k8s.io/component-base v0.19.2/go.mod h1:g5LrsiTiabMLZ40AR6Hl45f088DevyGY+cCE2agEIVo= k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= k8s.io/component-base v0.20.2/go.mod h1:pzFtCiwe/ASD0iV7ySMu8SYVJjCapNM9bjvk7ptpKh0= k8s.io/component-base v0.21.2/go.mod h1:9lvmIThzdlrJj5Hp8Z/TOgIkdfsNARQ1pT+3PByuiuc= +k8s.io/component-base v0.21.3/go.mod h1:kkuhtfEHeZM6LkX0saqSK8PbdO7A0HigUngmhhrwfGQ= k8s.io/component-base v0.22.1/go.mod h1:0D+Bl8rrnsPN9v0dyYvkqFfBeAd4u7n77ze+p8CMiPo= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= @@ -1300,8 +1316,9 @@ k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/klog/v2 v2.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM= k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= +k8s.io/klog/v2 v2.10.0 h1:R2HDMDJsHVTHA2n4RjwbeYXdOcBymXdX/JRb1v0VGhE= +k8s.io/klog/v2 v2.10.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= @@ -1316,8 +1333,10 @@ k8s.io/utils v0.0.0-20210111153108-fddb29f9d009/go.mod h1:jPW/WVKK9YHAvNhRxK0md/ k8s.io/utils v0.0.0-20210305010621-2afb4311ab10/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210527160623-6fdb442a123b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210707171843-4b05e18ac7d9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210802155522-efc7438f0176 h1:Mx0aa+SUAcNRQbs5jUzV8lkDlGFU8laZsY9jrcVX5SY= +k8s.io/utils v0.0.0-20210722164352-7f3ee0f31471/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20210820185131-d34e5cb4466e h1:ldQh+neBabomh7+89dTpiFAB8tGdfVmuIzAHbvtl+9I= +k8s.io/utils v0.0.0-20210820185131-d34e5cb4466e/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= @@ -1328,11 +1347,14 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyz sigs.k8s.io/controller-runtime v0.7.2/go.mod h1:pJ3YBrJiAqMAZKi6UVGuE98ZrroV1p+pIhoHsMm9wdU= sigs.k8s.io/controller-runtime v0.8.3/go.mod h1:U/l+DUopBc1ecfRZ5aviA9JDmGFQKvLf5YkZNx2e0sU= sigs.k8s.io/controller-runtime v0.9.2/go.mod h1:TxzMCHyEUpaeuOiZx/bIdc2T81vfs/aKdvJt9wuu0zk= +sigs.k8s.io/controller-runtime v0.9.6/go.mod h1:q6PpkM5vqQubEKUKOM6qr06oXGzOBcCby1DA9FbyZeA= sigs.k8s.io/controller-runtime v0.10.0 h1:HgyZmMpjUOrtkaFtCnfxsR1bGRuFoAczSNbn2MoKj5U= sigs.k8s.io/controller-runtime v0.10.0/go.mod h1:GCdh6kqV6IY4LK0JLwX0Zm6g233RtVGdb/f0+KSfprg= sigs.k8s.io/controller-tools v0.5.0/go.mod h1:JTsstrMpxs+9BUj6eGuAaEb6SDSPTeVtUyp0jmnAM/I= -sigs.k8s.io/gateway-api v0.3.0 h1:mKbQRlRIIY3dsCCbNF9Jv30V9vvOf6SRG82l0MfJQ9U= +sigs.k8s.io/controller-tools v0.6.2/go.mod h1:oaeGpjXn6+ZSEIQkUe/+3I40PNiDYp9aeawbt3xTgJ8= sigs.k8s.io/gateway-api v0.3.0/go.mod h1:Wb8bx7QhGVZxOSEU3i9vw/JqTB5Nlai9MLMYVZeDmRQ= +sigs.k8s.io/gateway-api v0.4.1 h1:Tof9/PNSZXyfDuTTe1XFvaTlvBRE6bKq1kmV6jj6rQE= +sigs.k8s.io/gateway-api v0.4.1/go.mod h1:r3eiNP+0el+NTLwaTfOrCNXy8TukC+dIM3ggc+fbNWk= sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= diff --git a/pkg/gatewayapi/http_route.go b/pkg/gatewayapi/http_route.go new file mode 100644 index 0000000..73afee8 --- /dev/null +++ b/pkg/gatewayapi/http_route.go @@ -0,0 +1,74 @@ +package gatewayapi + +import ( + "strings" + + "github.com/getkin/kin-openapi/openapi3" + gatewayapiv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" +) + +func HTTPRouteMatchesFromOAS(doc *openapi3.T) ([]gatewayapiv1alpha2.HTTPRouteMatch, error) { + httpRouteMatches := []gatewayapiv1alpha2.HTTPRouteMatch{} + pathMatchPathPrefix := gatewayapiv1alpha2.PathMatchPathPrefix + pathMatchExactPath := gatewayapiv1alpha2.PathMatchExact + + for path, pathItem := range doc.Paths { + + headers := []gatewayapiv1alpha2.HTTPHeaderMatch{} + queryParams := []gatewayapiv1alpha2.HTTPQueryParamMatch{} + headers, queryParams = addRuleMatcherFromParams(pathItem.Parameters, headers, queryParams) + + for verb, operation := range pathItem.Operations() { + + headers, queryParams = addRuleMatcherFromParams(operation.Parameters, headers, queryParams) + + pathMatch := &pathMatchExactPath + for _, param := range queryParams { + if param.Name == strings.ToLower(string(pathMatchPathPrefix)) { + pathMatch = &pathMatchPathPrefix + } + } + pathValue := path + path := &gatewayapiv1alpha2.HTTPPathMatch{ + Type: pathMatch, + Value: &pathValue, + } + + httpMethod := gatewayapiv1alpha2.HTTPMethod(verb) + httpRouteMatches = append(httpRouteMatches, gatewayapiv1alpha2.HTTPRouteMatch{ + Method: &httpMethod, + Path: path, + Headers: headers, + QueryParams: queryParams, + }) + } + } + + return httpRouteMatches, nil +} + +func addRuleMatcherFromParams(params openapi3.Parameters, headers []gatewayapiv1alpha2.HTTPHeaderMatch, queryParams []gatewayapiv1alpha2.HTTPQueryParamMatch) ([]gatewayapiv1alpha2.HTTPHeaderMatch, []gatewayapiv1alpha2.HTTPQueryParamMatch) { + headerMatchType := gatewayapiv1alpha2.HeaderMatchExact + queryParamMatchExact := gatewayapiv1alpha2.QueryParamMatchExact + + for _, parameter := range params { + if !parameter.Value.Required { + continue + } + + if parameter.Value.In == openapi3.ParameterInHeader { + headers = append(headers, gatewayapiv1alpha2.HTTPHeaderMatch{ + Type: &headerMatchType, + Name: gatewayapiv1alpha2.HTTPHeaderName(parameter.Value.Name), + }) + } + if parameter.Value.In == openapi3.ParameterInQuery { + queryParams = append(queryParams, gatewayapiv1alpha2.HTTPQueryParamMatch{ + Type: &queryParamMatchExact, + Name: parameter.Value.Name, + }) + } + } + + return headers, queryParams +} From 57f8323b5c97e983a7c70a1fd6e09c745ebe901f Mon Sep 17 00:00:00 2001 From: jjaferson Date: Mon, 31 Jan 2022 16:46:15 +0000 Subject: [PATCH 2/2] Adds docs on how to generate Gateway API HTTP Route --- README.md | 2 ++ doc/generate-gataway-api-httproute.md | 35 +++++++++++++++++++++++++ examples/oas3/gateway-api-petstore.yaml | 7 +---- pkg/gatewayapi/http_route.go | 21 ++++----------- 4 files changed, 43 insertions(+), 22 deletions(-) create mode 100644 doc/generate-gataway-api-httproute.md diff --git a/README.md b/README.md index 8bbbf67..0f43c76 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,8 @@ go install github.com/kuadrant/kuadrantctl@latest * [Generate Istio virtualservice objects](doc/generate-istio-virtualservice.md) * [Generate Istio authenticationpolicy objects](doc/generate-istio-authorizationpolicy.md) * [Generate kuadrat authconfig objects](doc/generate-kuadrant-authconfig.md) +* [Generate Gateway API HTTPRoute objects](doc/generate-gateway-api-httproute.md) + ## Contributing The [Development guide](doc/development.md) describes how to build the kuadrantctl CLI and how to test your changes before submitting a patch or opening a PR. diff --git a/doc/generate-gataway-api-httproute.md b/doc/generate-gataway-api-httproute.md new file mode 100644 index 0000000..95a7ba9 --- /dev/null +++ b/doc/generate-gataway-api-httproute.md @@ -0,0 +1,35 @@ +## Generate Gateway API HTTPRoute object + +The `kuadrantctl generate gatewayapi httproute` command generates an [Gateway API HTTPRoute](https://gateway-api.sigs.k8s.io/v1alpha2/guides/http-routing/) +from your [OpenAPI Specification (OAS) 3.x](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.2.md) and kubernetes service information. + +### OpenAPI specification + +OpenAPI document resource can be provided by one of the following channels: +* Filename in the available path. +* URL format (supported schemes are HTTP and HTTPS). The CLI will try to download from the given address. +* Read from stdin standard input stream. + +### Usage : + +```shell +$ kuadrantctl generate gatewayapi httproute -h +Generate Gateway API HTTPRoute from OpenAPI 3.x + +Usage: + kuadrantctl generate gatewayapi httproute [flags] + +Flags: + --gateway strings Gateways (required) + -h, --help help for httproute + -n, --namespace string Service namespace (required) + --oas string /path/to/file.[json|yaml|yml] OR http[s]://domain/resource/path.[json|yaml|yml] OR - (required) + -p, --port int32 Service Port (required) (default 80) + --public-host string Public host (required) + --service-name string Service name (required) + +Global Flags: + -v, --verbose verbose output +``` + +> Under the example folder there are examples of OAS 3 that can be used to generate the resources \ No newline at end of file diff --git a/examples/oas3/gateway-api-petstore.yaml b/examples/oas3/gateway-api-petstore.yaml index 9a54d97..b7e81e6 100644 --- a/examples/oas3/gateway-api-petstore.yaml +++ b/examples/oas3/gateway-api-petstore.yaml @@ -11,6 +11,7 @@ paths: parameters: - in: header name: X-username + required: true schema: type: string operationId: "getCat" @@ -24,12 +25,6 @@ paths: description: "invalid input" /dog: get: - parameters: - - in: query - name: pathprefix - required: true - schema: - type: string operationId: "getDog" responses: 405: diff --git a/pkg/gatewayapi/http_route.go b/pkg/gatewayapi/http_route.go index 73afee8..a8003b6 100644 --- a/pkg/gatewayapi/http_route.go +++ b/pkg/gatewayapi/http_route.go @@ -1,15 +1,12 @@ package gatewayapi import ( - "strings" - "github.com/getkin/kin-openapi/openapi3" gatewayapiv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" ) func HTTPRouteMatchesFromOAS(doc *openapi3.T) ([]gatewayapiv1alpha2.HTTPRouteMatch, error) { httpRouteMatches := []gatewayapiv1alpha2.HTTPRouteMatch{} - pathMatchPathPrefix := gatewayapiv1alpha2.PathMatchPathPrefix pathMatchExactPath := gatewayapiv1alpha2.PathMatchExact for path, pathItem := range doc.Paths { @@ -22,22 +19,14 @@ func HTTPRouteMatchesFromOAS(doc *openapi3.T) ([]gatewayapiv1alpha2.HTTPRouteMat headers, queryParams = addRuleMatcherFromParams(operation.Parameters, headers, queryParams) - pathMatch := &pathMatchExactPath - for _, param := range queryParams { - if param.Name == strings.ToLower(string(pathMatchPathPrefix)) { - pathMatch = &pathMatchPathPrefix - } - } pathValue := path - path := &gatewayapiv1alpha2.HTTPPathMatch{ - Type: pathMatch, - Value: &pathValue, - } - httpMethod := gatewayapiv1alpha2.HTTPMethod(verb) httpRouteMatches = append(httpRouteMatches, gatewayapiv1alpha2.HTTPRouteMatch{ - Method: &httpMethod, - Path: path, + Method: &httpMethod, + Path: &gatewayapiv1alpha2.HTTPPathMatch{ + Type: &pathMatchExactPath, + Value: &pathValue, + }, Headers: headers, QueryParams: queryParams, })