kubeshop / kusk-gateway

Kusk-gateway is an OpenAPI-driven API Gateway for Kubernetes
https://kubeshop.github.io/kusk-gateway/
MIT License
265 stars 21 forks source link

API paths not working properly #685

Closed gerardorn closed 2 years ago

gerardorn commented 2 years ago

Describe the bug Having a path like /api/v0/endpoint returns 404

To Reproduce Steps to reproduce the behavior:

  1. Start minikube cluster
    minikube start
  2. Install kusk
    kusk install
  3. Deploy OpenAPI file
    kusk api generate -i openapi.yaml | kubectl apply -f -
  4. Start minikube tunnel
    minikube tunnel
  5. Get external IP
    kubectl get service -n kusk-system kusk-gateway-envoy-fleet
  6. Send request to endpoint
    curl {EXTERNAL-IP}/accounting/v0/accounts
    curl {EXTERNAL-IP}/accounts

Expected behavior An upstream service request is sent instead of a 404 error

Screenshots image

Second request to the simple path returns an empty list ( [] ), which is expected

Desktop (please complete the following information):

Additional context

API specification used

openapi: 3.0.0
info:
  title: containerp
  version: 0.0.0
x-kusk:
  cors:
    origins:
      - "*"
    methods:
      - GET
      - POST
paths:
  /accounting/v0/accounts:
    post:
      x-kusk:
        upstream:
          service:
            name: accounts-svc
            namespace: default
            port: 80
      requestBody:
        content:
          application/json:
            schema:
              required:
                - name
                - nature
              type: object
              properties:
                name:
                  type: string
                  example: Caja
                id:
                  type: string
                nature:
                  type: string
                  example: Deudora|Accredora
      responses:
        '201':
          description: An account created
          content:
            application/json:
              schema:
                type: object
                properties:
                  name:
                    type: string
                    example: Caja
                  id:
                    type: string
                    example: MXYuiOiueDhaNdhxD
                  nature:
                    type: string
                    example: Deudora|Accredora
    get:
      x-kusk:
        upstream:
          service:
            name: accounts-svc
            namespace: default
            port: 80
      responses:
        '200':
          description: Some description value text
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    name:
                      type: string
                      example: Caja
                    id:
                      type: string
                    nature:
                      type: string
                      example: Deudora|Accredora
  /accounts:
    post:
      x-kusk:
        upstream:
          service:
            name: accounts-svc
            namespace: default
            port: 80
      requestBody:
        content:
          application/json:
            schema:
              required:
                - name
                - nature
              type: object
              properties:
                name:
                  type: string
                  example: Caja
                id:
                  type: string
                nature:
                  type: string
                  example: Deudora|Accredora
      responses:
        '201':
          description: An account created
          content:
            application/json:
              schema:
                type: object
                properties:
                  name:
                    type: string
                    example: Caja
                  id:
                    type: string
                    example: MXYuiOiueDhaNdhxD
                  nature:
                    type: string
                    example: Deudora|Accredora
    get:
      x-kusk:
        upstream:
          service:
            name: accounts-svc
            namespace: default
            port: 80
      responses:
        '200':
          description: Some description value text
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    name:
                      type: string
                      example: Caja
                    id:
                      type: string
                    nature:
                      type: string
                      example: Deudora|Accredora
jasmingacic commented 2 years ago

@gerardorn thank you very much for reporting this issue

It looks like you've put upstream extension on the route level where you should have had it in root level. Can you try and remove this bit of the configuration from under get and post

upstream: 
service: 
   name: accounts-svc 
   namespace: default 
   port: 80

And put it right before CORS section like this

openapi: 3.0.0
info:
  title: containerp
  version: 0.0.0
x-kusk:
  upstream: 
  service: 
    name: accounts-svc 
    namespace: default 
    port: 80
  cors:
    origins:
      - "*"
    methods:
      - GET
      - POST

Let me know if this helps

P.S. pay attention to my formatting as it might be incorrect

gerardorn commented 2 years ago

hey @jasmingacic thanks for the reply

So I updated the API specs but sill not working. However this won't be a long term solution for me since the API will in the future have other services and as far as I understand, if i leave it at the root level I won't be able to route requests to different services, is that right?

Here's the updated API spec:

openapi: 3.0.0
info:
  title: containerp
  version: 0.0.0
x-kusk:
  upstream:
    service:
      name: accounts-svc
      namespace: default
      port: 80
  cors:
    origins:
      - "*"
    methods:
      - GET
      - POST
paths:
  /accounting/v0/accounts:
    post:
      requestBody:
        content:
          application/json:
            schema:
              required:
                - name
                - nature
              type: object
              properties:
                name:
                  type: string
                  example: Caja
                id:
                  type: string
                nature:
                  type: string
                  example: Deudora|Accredora
      responses:
        '201':
          description: An account created
          content:
            application/json:
              schema:
                type: object
                properties:
                  name:
                    type: string
                    example: Caja
                  id:
                    type: string
                    example: MXYuiOiueDhaNdhxD
                  nature:
                    type: string
                    example: Deudora|Accredora
    get:
      responses:
        '200':
          description: Some description value text
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    name:
                      type: string
                      example: Caja
                    id:
                      type: string
                    nature:
                      type: string
                      example: Deudora|Accredora
  /accounts:
    post:
      requestBody:
        content:
          application/json:
            schema:
              required:
                - name
                - nature
              type: object
              properties:
                name:
                  type: string
                  example: Caja
                id:
                  type: string
                nature:
                  type: string
                  example: Deudora|Accredora
      responses:
        '201':
          description: An account created
          content:
            application/json:
              schema:
                type: object
                properties:
                  name:
                    type: string
                    example: Caja
                  id:
                    type: string
                    example: MXYuiOiueDhaNdhxD
                  nature:
                    type: string
                    example: Deudora|Accredora
    get:
      responses:
        '200':
          description: Some description value text
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    name:
                      type: string
                      example: Caja
                    id:
                      type: string
                    nature:
                      type: string
                      example: Deudora|Accredora

and here's the same result image

jasmingacic commented 2 years ago

The accounts-svc you are using is it of type loadbalancer or clusterIP. If it is set to loadbalancer then the envoyfleet is set the same way which is causing the problem.

Would you mind setting up a call with the team so we can help you troubleshoot the issue.https://calendly.com/kusk_gateway/demo

Meanwhile I will try to replicate the issue on my setup and get back to you.

gerardorn commented 2 years ago

It is a ClusterIP. Im assuming is an issue with how the requests are being listened or routed to a service because the simple route /accounts is working fine and is the same service just different API path

Thanks for the link, I've scheduled a meeting on monday

Here's the service definition:

apiVersion: v1
kind: Service
metadata:
  creationTimestamp: null
  labels:
    app: accounts
  name: accounts-svc
spec:
  ports:
  - name: 80-8080
    port: 80
    targetPort: 8080
  selector:
    app: accounts
  type: ClusterIP
gerardorn commented 2 years ago

Hey @jasmingacic

After a deep reading of the documentation, I found this sentence in the Rewriting Request Paths section:

If we receive a request at /foo/bar, the request will be forwarded to the foo service. foo will throw a 404 error as it doesn't have a path /foo/bar.

Which made me look into my controller definition and I had something like

func (g *ginRestAccountsController) Start() {
    router := gin.Default()

    router.POST("/accounts", g.CreateAccount)
    router.GET("/accounts", g.ListAccounts)
    router.Run(":8080")
}

So I changed it to look someting more similar to how I was defining the API

func (g *ginRestAccountsController) Start() {
    router := gin.Default()

    router.POST("/accounting/v0/accounts", g.CreateAccount)
    router.GET("/accounting/v0/accounts", g.ListAccounts)
    router.Run(":8080")
}

Which solved my problem 🤦🏼

P.D. I'm having trouble with external references used in my OpenAPI file:

openapi: 3.0.0
info:
  title: containerp
  version: 0.0.0
x-kusk:
  upstream:
    service:
      name: accounts-svc
      namespace: default
      port: 80
  validation:
    request:
      enabled:
  cors:
    origins:
      - "*"
    methods:
      - GET
      - POST
paths:
  /accounting/v0/accounts:
    post:
      requestBody:
        content:
          application/json:
            schema:
              $ref: 'https://gitlab.com/evolucion-digital/containerp/core-library/-/blob/main/accounting/Schemas.yaml#/components/schemas/Account'
      responses:
        '201':
          description: Create a new accounting account
          content:
            application/json:
              schema:
                $ref: 'https://gitlab.com/evolucion-digital/containerp/core-library/-/blob/main/accounting/Schemas.yaml#/components/schemas/Account'
    get:
      responses:
        '200':
          description: Some description value text
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: 'https://gitlab.com/evolucion-digital/containerp/core-library/-/blob/main/accounting/Schemas.yaml#/components/schemas/Account'
  /accounting/v0/journal/entries:
    get:
      responses:
        '200':
          description: Some description value text
          content:
            application/json:
              schema:
                $ref: 'https://gitlab.com/evolucion-digital/containerp/core-library/-/blob/main/accounting/Schemas.yaml#/components/schemas/JournalEntry'

When trying to generate the API I get this message

$ kusk api generate -i openapi.yaml  > api.yaml
unable to load spec: encountered disallowed external reference: "https://gitlab.com/evolucion-digital/containerp/core-library/-/blob/main/accounting/Schemas.yaml#/components/schemas/JournalEntry"

How can I allow this external reference?