swagger-api / swagger-api/swagger-ui

swagger-ui: "Try it Now" functionality stripping path prefixes when run behind nginx-ingress reverse proxy in Kubernetes/K8S.

Open
#6,838 8 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
29k
Forks
9.3k
Avg merge
2d 23h
Merged PRs (30d)
25

Description

Q&A (please complete the following information)
  • OS: Linux (Ubuntu x86_64 LTS 20.04).
  • Browser: Firefox.
  • Version: v84.0.1 64-bit.
  • Method of installation: Helm v3.x in K8S (though trying the manual "dist/index.html" approach yields similar results).
  • Swagger-UI version: v3.24.3 (pre-built Docker container), v3.40.0 (plain old "dist/index.html" approach).
  • Swagger/OpenAPI version: OpenAPI 3.0.
Content & configuration
  • Setup an instance of an application exposed through an nginx-ingress rule in K8S. Sample ingress YAML included below. In this example, the host is roadrunner.acme.co, and the service is exposed on http://roadrunner.acme.com/meep/v1.
    • Note: I've found a few other projects on GitHub or via Google searches where the solution is "just expose your app on hostname/, rather than a custom path invoking nginx rewrites such as hostname/meep/v1.
    • This is not an option, as that would assume only a single application is exposed via the ingress controller. In a typical/production use case, each micro-service in behind the ingress-control would get its own dedicated base path, like meep/v1 in my example).
  • Download and extract the contents of the "plain old HTML/JS" archive.
  • Modify the dist/index.html file so the URL field points to the OpenAPI spec of my service (i.e. http://roadrunner.acme.co/meep/v1/openapi.json; the service is a simple Python Flash+Swagger example that auto-exposes the openapi.json endpoint).
  • I am able to access the Swagger-UI web UI example, interact with it, etc. However, if I attempt to use the "Try It Now" ==> "Execute" functionality, the API calls to the live/running app fail due to a "Network error".
  • Upon closer inspection, I see that the "Execute" calls are attempting to communicate with http://roadrunner.acme.co/v1/ rather than http://roadrunner.acme.co/meep/v1 (i.e. the re-written portion of the URL is absent).
  • I was able to arrive at the same result by deploying swagger-ui into the cluster directly via a third-party helm chart.

Example ingress:

---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: sample-ingress-meep-meep
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
  rules:
    - http:
        paths:
          - backend:
              serviceName: velocitus-good-with-ketchup-ius
              servicePort: 80
            path: /meep/(v1/.*)

Example Swagger/OpenAPI definition: N/A.

Swagger-UI configuration options:

window.onload = function() {
      // Begin Swagger UI call region
      const ui = SwaggerUIBundle({
        url: "http://roadrunner.acme.co/meep/v1/openapi.json",
        dom_id: '#swagger-ui',
        deepLinking: true,
        presets: [
          SwaggerUIBundle.presets.apis,
          SwaggerUIStandalonePreset
        ],
        plugins: [
          SwaggerUIBundle.plugins.DownloadUrl
        ],
        layout: "StandaloneLayout"
      })
      // End Swagger UI call region

      window.ui = ui
    }
Describe the bug you're encountering
  • The "Try It Now" ==> "Execute" functionality does not work, but access to the HTML app in general works.
To reproduce...

Example described above.

Expected behavior
  • The "Try It Now" ==> "Execute" functionality uses the exact same URL (i.e. hostname plus base path) that is used to access the running OpenAPI-enabled service (maybe using X-Forwarded-For or additional parameters supplied to the ingress-controller, so that re-writes don't break functionality).
Additional context or thoughts

There's a hack I've figured out in order to make the "Try It Now" ==> "Execute" functionality work, but the downside (severe), is that I need to supply the external hostname to the swagger-ui app at chart installation time (i.e. when I install swagger-ui, it needs to be provided with the string http://roadrunner.acme.co/meep/v1 at installation time). This isn't a permanent/viable solution though, as various users may "see" a different hostname and base path when accessing this micro-service (e.g. if it's exposed via multiple domains with a common load balancer, or if some people attempt to access it via IP address rather than this specific host name). It also prevents 100% end-to-end automated installation, as someone needs to supply the external FQDN + base path at chart installation time.

Hack-y workaround:

  • Deploy an OpenAPI-enabled microservice, expose it via an ingress rule (below). Can be deployed via kubectl apply --namespace=acme-ns -f ingress-meep.yaml.

ingress-meep.yaml

---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: sample-ingress-meep-meep
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
  rules:
    - http:
        paths:
          - backend:
              serviceName: velocitus-good-with-ketchup-ius
              servicePort: 80
            path: /meep/(v1/.*)
  • Pre-create an ingress rule for swagger-ui (below). Can be deployed via kubectl apply --namespace=acme-ns -f ingress-demo.yaml.

ingress-demo.yaml

---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: sample-ingress-swaggerui-demo
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$1
    nginx.ingress.kubernetes.io/enable-cors: "true"
spec:
  rules:
    - http:
        paths:
          - backend:
              serviceName: swagger-demo-swaggerui
              servicePort: 8080
            path: /swagger-demo/(.*)
  • Using the following commands to deploy the chart.
# Make chart "visible" to Helm.
helm repo add cetic https://cetic.github.io/helm-charts
helm repo update

# Delete old copy if present.
helm delete --namespace=acme-ns swagger-demo

# Install chart.
helm install --namespace=acme-ns swagger-demo cetic/swagger-ui \
    --set swaggerui.jsonUrl="http://velocitus-good-with-ketchup-ius.acme-ns.svc.cluster.local/v1/openapi.json" \
    --set swaggerui.server.url="http://roadrunner.acme.co/meep/v1/"
  • Navigate to <http://roadrunner.acme.co/swagger-demo/> in my browser. Application loads and is visible in my browser.
  • Change the "Servers" via the drop-down UI menu (top-left) from v1 to http://roadrunner.acme.co/meep/v1. Both the general UI features, and the live debug "Try It Now" features work as expected.

Possibly related issues (external projects)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with dist/index.html and the window.onload SwaggerUIBundle configuration described in the report, then reproduce the Execute request through the nginx-ingress setup. Trace how the OpenAPI server URL and ingress base path are handled; done means Try It Now targets the externally visible hostname and path without installation-time host configuration.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, kubernetes, nginx
Domain
api, frontend, infrastructure
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.