From a6ddbbd0f73baac6bbb8dd5e136fb135fded7091 Mon Sep 17 00:00:00 2001 From: Jeff Ortel Date: Wed, 4 Sep 2024 12:09:07 -0700 Subject: [PATCH 01/11] Add service handler. Signed-off-by: Jeff Ortel --- api/pkg.go | 1 + api/service.go | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 api/service.go diff --git a/api/pkg.go b/api/pkg.go index 36f78c052..18efbcb3e 100644 --- a/api/pkg.go +++ b/api/pkg.go @@ -78,6 +78,7 @@ func All() []Handler { &RuleSetHandler{}, &SchemaHandler{}, &SettingHandler{}, + &ServiceHandler{}, &StakeholderHandler{}, &StakeholderGroupHandler{}, &TagHandler{}, diff --git a/api/service.go b/api/service.go new file mode 100644 index 000000000..a41ee439f --- /dev/null +++ b/api/service.go @@ -0,0 +1,87 @@ +package api + +import ( + "context" + "net/http" + "net/http/httputil" + "strconv" + + "github.com/gin-gonic/gin" + core "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + k8s "sigs.k8s.io/controller-runtime/pkg/client" +) + +// Routes +const ( + ServiceRoot = "/service/:name/*" + Wildcard +) + +// ServiceHandler handles service routes. +type ServiceHandler struct { + BaseHandler +} + +// AddRoutes adds routes. +func (h ServiceHandler) AddRoutes(e *gin.Engine) { + e.Any(ServiceRoot, h.ReverseProxy) +} + +// ReverseProxy provides RBAC and forwards request to the service. +func (h ServiceHandler) ReverseProxy(ctx *gin.Context) { + name := ctx.Param(Name) + path := ctx.Param(Wildcard) + Required(name)(ctx) + if len(ctx.Errors) > 0 { + return + } + service := &core.Service{} + err := h.Client(ctx).Get( + context.TODO(), + k8s.ObjectKey{ + Namespace: Settings.Hub.Namespace, + Name: name, + }, + service) + if err != nil { + if errors.IsNotFound(err) { + h.Status(ctx, http.StatusNotFound) + return + } else { + _ = ctx.Error(err) + return + } + } + host := service.Spec.ClusterIP + port := int(service.Spec.Ports[0].Port) + host = host + ":" + strconv.Itoa(port) + proxy := httputil.ReverseProxy{ + Director: func(req *http.Request) { + req.URL.Scheme = h.scheme(service) + req.URL.Host = h.host(service) + req.URL.Path = path + }, + } + + proxy.ServeHTTP(ctx.Writer, ctx.Request) +} + +func (h *ServiceHandler) scheme(service *core.Service) (scheme string) { + scheme = "http" + s, found := service.Annotations["konveyor.io/scheme"] + if found { + scheme = s + } + return +} + +func (h *ServiceHandler) host(service *core.Service) (host string) { + host = service.Spec.ClusterIP + for _, p := range service.Spec.Ports { + port := int(p.Port) + host += ":" + host += strconv.Itoa(port) + break + } + return +} From 65a9f40805858ccb10b74933700eb80ed015d896 Mon Sep 17 00:00:00 2001 From: Jeff Ortel Date: Wed, 4 Sep 2024 12:26:38 -0700 Subject: [PATCH 02/11] checkpoint Signed-off-by: Jeff Ortel --- api/service.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/service.go b/api/service.go index a41ee439f..f92f09a1c 100644 --- a/api/service.go +++ b/api/service.go @@ -24,11 +24,11 @@ type ServiceHandler struct { // AddRoutes adds routes. func (h ServiceHandler) AddRoutes(e *gin.Engine) { - e.Any(ServiceRoot, h.ReverseProxy) + e.Any(ServiceRoot, h.Forward) } -// ReverseProxy provides RBAC and forwards request to the service. -func (h ServiceHandler) ReverseProxy(ctx *gin.Context) { +// Forward provides RBAC and forwards request to the service. +func (h ServiceHandler) Forward(ctx *gin.Context) { name := ctx.Param(Name) path := ctx.Param(Wildcard) Required(name)(ctx) From 5da90a7f8101952dd839e26646bef3a8b38fb28f Mon Sep 17 00:00:00 2001 From: Jeff Ortel Date: Wed, 4 Sep 2024 12:34:35 -0700 Subject: [PATCH 03/11] checkpoint Signed-off-by: Jeff Ortel --- api/service.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/service.go b/api/service.go index f92f09a1c..c5705876c 100644 --- a/api/service.go +++ b/api/service.go @@ -31,7 +31,7 @@ func (h ServiceHandler) AddRoutes(e *gin.Engine) { func (h ServiceHandler) Forward(ctx *gin.Context) { name := ctx.Param(Name) path := ctx.Param(Wildcard) - Required(name)(ctx) + Required("service." + name)(ctx) if len(ctx.Errors) > 0 { return } From f78c882ec177747ef8d5ffcbd73540160d094b8e Mon Sep 17 00:00:00 2001 From: Jeff Ortel Date: Fri, 6 Sep 2024 08:44:47 -0700 Subject: [PATCH 04/11] checkpoint Signed-off-by: Jeff Ortel --- api/service.go | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/api/service.go b/api/service.go index c5705876c..b7b3b3e83 100644 --- a/api/service.go +++ b/api/service.go @@ -9,6 +9,7 @@ import ( "github.com/gin-gonic/gin" core "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/utils/net" k8s "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -52,12 +53,9 @@ func (h ServiceHandler) Forward(ctx *gin.Context) { return } } - host := service.Spec.ClusterIP - port := int(service.Spec.Ports[0].Port) - host = host + ":" + strconv.Itoa(port) proxy := httputil.ReverseProxy{ Director: func(req *http.Request) { - req.URL.Scheme = h.scheme(service) + req.URL.Scheme = ctx.Request.URL.Scheme req.URL.Host = h.host(service) req.URL.Path = path }, @@ -66,22 +64,15 @@ func (h ServiceHandler) Forward(ctx *gin.Context) { proxy.ServeHTTP(ctx.Writer, ctx.Request) } -func (h *ServiceHandler) scheme(service *core.Service) (scheme string) { - scheme = "http" - s, found := service.Annotations["konveyor.io/scheme"] - if found { - scheme = s - } - return -} - func (h *ServiceHandler) host(service *core.Service) (host string) { host = service.Spec.ClusterIP for _, p := range service.Spec.Ports { - port := int(p.Port) - host += ":" - host += strconv.Itoa(port) - break + if net.Protocol(p.Protocol) == net.TCP { + port := int(p.Port) + host += ":" + host += strconv.Itoa(port) + break + } } return } From ac6d7aaa98b7adbde7fc995b2bdbf4f25084a85d Mon Sep 17 00:00:00 2001 From: Jeff Ortel Date: Tue, 10 Sep 2024 10:13:01 -0700 Subject: [PATCH 05/11] checkpoint Signed-off-by: Jeff Ortel --- api/error.go | 20 ++++++++++- api/service.go | 94 ++++++++++++++++++++++++++++++------------------- auth/roles.yaml | 12 +++++++ 3 files changed, 88 insertions(+), 38 deletions(-) diff --git a/api/error.go b/api/error.go index 01dc4a4ba..3406eccec 100644 --- a/api/error.go +++ b/api/error.go @@ -2,6 +2,7 @@ package api import ( "errors" + "fmt" "net/http" "os" @@ -78,6 +79,22 @@ func (r *Forbidden) Is(err error) (matched bool) { return } +// NotFound reports auth errors. +type NotFound struct { + Resource string + Reason string +} + +func (r *NotFound) Error() string { + return fmt.Sprintf("Resource '%s' not found. %s", r.Resource, r.Reason) +} + +func (r *NotFound) Is(err error) (matched bool) { + var forbidden *Forbidden + matched = errors.As(err, &forbidden) + return +} + // ErrorHandler handles error conditions from lower handlers. func ErrorHandler() gin.HandlerFunc { return func(ctx *gin.Context) { @@ -102,7 +119,8 @@ func ErrorHandler() gin.HandlerFunc { return } - if errors.Is(err, gorm.ErrRecordNotFound) { + if errors.Is(err, gorm.ErrRecordNotFound) || + errors.Is(err, &NotFound{}) { if ctx.Request.Method == http.MethodDelete { rtx.Status(http.StatusNoContent) return diff --git a/api/service.go b/api/service.go index b7b3b3e83..fff149775 100644 --- a/api/service.go +++ b/api/service.go @@ -1,23 +1,26 @@ package api import ( - "context" + "fmt" "net/http" "net/http/httputil" - "strconv" + "net/url" + "os" "github.com/gin-gonic/gin" - core "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/utils/net" - k8s "sigs.k8s.io/controller-runtime/pkg/client" ) // Routes const ( - ServiceRoot = "/service/:name/*" + Wildcard + ServicesRoot = "/services" + ServiceRoot = ServicesRoot + "/:name/*" + Wildcard ) +// serviceRoutes name to route map. +var serviceRoutes = map[string]string{ + "kai": os.Getenv("KAI_URL"), +} + // ServiceHandler handles service routes. type ServiceHandler struct { BaseHandler @@ -25,54 +28,71 @@ type ServiceHandler struct { // AddRoutes adds routes. func (h ServiceHandler) AddRoutes(e *gin.Engine) { + e.GET(ServicesRoot, h.List) e.Any(ServiceRoot, h.Forward) } +// List godoc +// @summary List named service routes. +// @description List named service routes. +// @tags services +// @produce json +// @success 200 {object} api.Service +// @router /services [get] +func (h ServiceHandler) List(ctx *gin.Context) { + var r []Service + for name, route := range serviceRoutes { + service := Service{Name: name, Route: route} + r = append(r, service) + } + + h.Respond(ctx, http.StatusOK, r) +} + // Forward provides RBAC and forwards request to the service. func (h ServiceHandler) Forward(ctx *gin.Context) { - name := ctx.Param(Name) path := ctx.Param(Wildcard) - Required("service." + name)(ctx) + name := ctx.Param(Name) + Required(name) if len(ctx.Errors) > 0 { return } - service := &core.Service{} - err := h.Client(ctx).Get( - context.TODO(), - k8s.ObjectKey{ - Namespace: Settings.Hub.Namespace, - Name: name, - }, - service) + route, found := serviceRoutes[name] + if !found { + err := &NotFound{Resource: name} + _ = ctx.Error(err) + return + } + if route == "" { + err := fmt.Errorf("route for: '%s' not defined", name) + _ = ctx.Error(err) + return + } + u, err := url.Parse(route) if err != nil { - if errors.IsNotFound(err) { - h.Status(ctx, http.StatusNotFound) - return - } else { - _ = ctx.Error(err) - return - } + err = &BadRequestError{Reason: err.Error()} + _ = ctx.Error(err) + return } proxy := httputil.ReverseProxy{ Director: func(req *http.Request) { - req.URL.Scheme = ctx.Request.URL.Scheme - req.URL.Host = h.host(service) + req.URL.Scheme = u.Scheme + req.URL.Host = u.Host req.URL.Path = path + Log.Info( + "Routing (service)", + "path", + ctx.Request.URL.Path, + "route", + req.URL.String()) }, } proxy.ServeHTTP(ctx.Writer, ctx.Request) } -func (h *ServiceHandler) host(service *core.Service) (host string) { - host = service.Spec.ClusterIP - for _, p := range service.Spec.Ports { - if net.Protocol(p.Protocol) == net.TCP { - port := int(p.Port) - host += ":" - host += strconv.Itoa(port) - break - } - } - return +// Service REST resource. +type Service struct { + Name string `json:"name"` + Route string `json:"route"` } diff --git a/auth/roles.yaml b/auth/roles.yaml index aa65e7a77..d035eca85 100644 --- a/auth/roles.yaml +++ b/auth/roles.yaml @@ -82,6 +82,9 @@ - get - post - put + - name: kai + verbs: + - get - name: proxies verbs: - delete @@ -286,6 +289,9 @@ - get - post - put + - name: kai + verbs: + - get - name: proxies verbs: - get @@ -443,6 +449,9 @@ - name: jobfunctions verbs: - get + - name: kai + verbs: + - get - name: proxies verbs: - get @@ -560,6 +569,9 @@ - name: jobfunctions verbs: - get + - name: kai + verbs: + - get - name: proxies verbs: - get From a4454103caee18014bde6a59b37a6ab4b0ff2b9c Mon Sep 17 00:00:00 2001 From: Jeff Ortel Date: Tue, 10 Sep 2024 10:20:02 -0700 Subject: [PATCH 06/11] checkpoint Signed-off-by: Jeff Ortel --- api/error.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/error.go b/api/error.go index 3406eccec..fe79e47d9 100644 --- a/api/error.go +++ b/api/error.go @@ -79,7 +79,7 @@ func (r *Forbidden) Is(err error) (matched bool) { return } -// NotFound reports auth errors. +// NotFound reports resource not-found errors. type NotFound struct { Resource string Reason string From 6c7c22271946903e05c35e687b23bfc75f932b08 Mon Sep 17 00:00:00 2001 From: Jeff Ortel Date: Tue, 10 Sep 2024 10:27:48 -0700 Subject: [PATCH 07/11] checkpoint Signed-off-by: Jeff Ortel --- api/service.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/api/service.go b/api/service.go index fff149775..811d3acaf 100644 --- a/api/service.go +++ b/api/service.go @@ -29,7 +29,7 @@ type ServiceHandler struct { // AddRoutes adds routes. func (h ServiceHandler) AddRoutes(e *gin.Engine) { e.GET(ServicesRoot, h.List) - e.Any(ServiceRoot, h.Forward) + e.Any(ServiceRoot, h.Required, h.Forward) } // List godoc @@ -49,14 +49,15 @@ func (h ServiceHandler) List(ctx *gin.Context) { h.Respond(ctx, http.StatusOK, r) } +// Required enforces RBAC. +func (h ServiceHandler) Required(ctx *gin.Context) { + Required(ctx.Param(Name)) +} + // Forward provides RBAC and forwards request to the service. func (h ServiceHandler) Forward(ctx *gin.Context) { path := ctx.Param(Wildcard) name := ctx.Param(Name) - Required(name) - if len(ctx.Errors) > 0 { - return - } route, found := serviceRoutes[name] if !found { err := &NotFound{Resource: name} From ad1ed6df34a3fc0249c23d1e5b8da845da02ff15 Mon Sep 17 00:00:00 2001 From: Jeff Ortel Date: Thu, 12 Sep 2024 09:09:52 -0700 Subject: [PATCH 08/11] github action runner using 1.22 and pin goimports 0.24. Signed-off-by: Jeff Ortel --- .github/workflows/main.yml | 10 +++++----- .github/workflows/test-nightly.yml | 4 ++-- Makefile | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 45642a6fa..7932abf21 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,7 +15,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v3 with: - go-version: '1.19' + go-version: '1.22' - run: make fmt vet: @@ -24,7 +24,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v3 with: - go-version: '1.19' + go-version: '1.22' - run: make vet build: @@ -33,7 +33,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v3 with: - go-version: '1.19' + go-version: '1.22' - run: make cmd test-unit: @@ -42,7 +42,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v3 with: - go-version: '1.19' + go-version: '1.22' - run: make test test-api: @@ -60,7 +60,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v3 with: - go-version: '1.19' + go-version: '1.22' - run: | rm -f $DB_PATH make run & diff --git a/.github/workflows/test-nightly.yml b/.github/workflows/test-nightly.yml index 60e81cf83..d2c4b36f9 100644 --- a/.github/workflows/test-nightly.yml +++ b/.github/workflows/test-nightly.yml @@ -11,7 +11,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-go@v3 with: - go-version: '1.19' + go-version: '1.22' - run: make test test-api: @@ -20,7 +20,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-go@v3 with: - go-version: '1.19' + go-version: '1.22' - run: | make vet DISCONNECTED=1 make run & diff --git a/Makefile b/Makefile index e8463bc85..b092ad5fd 100644 --- a/Makefile +++ b/Makefile @@ -84,7 +84,7 @@ $(CONTROLLERGEN): # Ensure goimports installed. $(GOIMPORTS): - go install golang.org/x/tools/cmd/goimports@latest + go install golang.org/x/tools/cmd/goimports@v0.24 # Build SAMPLE ADDON addon: fmt vet From 9c9ae19171f4dd7b755ea0d5d171f739e40b3fd5 Mon Sep 17 00:00:00 2001 From: Jeff Ortel Date: Thu, 12 Sep 2024 09:23:15 -0700 Subject: [PATCH 09/11] Fix controller-gen SEGV. Runner go 1.21. Signed-off-by: Jeff Ortel --- .github/workflows/main.yml | 10 +++++----- .github/workflows/test-nightly.yml | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7932abf21..ab89020dd 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,7 +15,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v3 with: - go-version: '1.22' + go-version: '1.21' - run: make fmt vet: @@ -24,7 +24,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v3 with: - go-version: '1.22' + go-version: '1.21' - run: make vet build: @@ -33,7 +33,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v3 with: - go-version: '1.22' + go-version: '1.21' - run: make cmd test-unit: @@ -42,7 +42,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v3 with: - go-version: '1.22' + go-version: '1.21' - run: make test test-api: @@ -60,7 +60,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v3 with: - go-version: '1.22' + go-version: '1.21' - run: | rm -f $DB_PATH make run & diff --git a/.github/workflows/test-nightly.yml b/.github/workflows/test-nightly.yml index d2c4b36f9..90350c819 100644 --- a/.github/workflows/test-nightly.yml +++ b/.github/workflows/test-nightly.yml @@ -11,7 +11,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-go@v3 with: - go-version: '1.22' + go-version: '1.21' - run: make test test-api: @@ -20,7 +20,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-go@v3 with: - go-version: '1.22' + go-version: '1.21' - run: | make vet DISCONNECTED=1 make run & From 6973a379155ea48d5f17dab9ed6bf583cbd6b53a Mon Sep 17 00:00:00 2001 From: Jeff Ortel Date: Thu, 12 Sep 2024 10:23:53 -0700 Subject: [PATCH 10/11] Fix auth. Signed-off-by: Jeff Ortel --- api/service.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/service.go b/api/service.go index 811d3acaf..d3e269727 100644 --- a/api/service.go +++ b/api/service.go @@ -51,7 +51,7 @@ func (h ServiceHandler) List(ctx *gin.Context) { // Required enforces RBAC. func (h ServiceHandler) Required(ctx *gin.Context) { - Required(ctx.Param(Name)) + Required(ctx.Param(Name))(ctx) } // Forward provides RBAC and forwards request to the service. From 802a11c59d867783dbb74f08b205c0537793233e Mon Sep 17 00:00:00 2001 From: Jeff Ortel Date: Thu, 12 Sep 2024 12:43:27 -0700 Subject: [PATCH 11/11] POST needed because kai-api uses POST for queries. Signed-off-by: Jeff Ortel --- auth/roles.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/auth/roles.yaml b/auth/roles.yaml index d035eca85..361a2c1ae 100644 --- a/auth/roles.yaml +++ b/auth/roles.yaml @@ -85,6 +85,7 @@ - name: kai verbs: - get + - post - name: proxies verbs: - delete @@ -292,6 +293,7 @@ - name: kai verbs: - get + - post - name: proxies verbs: - get @@ -452,6 +454,7 @@ - name: kai verbs: - get + - post - name: proxies verbs: - get @@ -572,6 +575,7 @@ - name: kai verbs: - get + - post - name: proxies verbs: - get