Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable SmartApp (and other nginx apps) to be deployed in other contexts #2

Open
justb4 opened this issue Apr 16, 2018 · 1 comment
Open
Assignees
Labels
enhancement New feature or request

Comments

@justb4
Copy link
Contributor

justb4 commented Apr 16, 2018

Currently the SmartApp and other nginx apps like WaalKade are proxied on path prefixes like https://pdok.smartemission.nl/smartapp. As to still allow local resources (javascript, css, media) to be served, the prefix-path /smartapp is rewritten in Kubernetes Ingress to /smartapp/ (trailing slash). This does not work nicely in all deployment contexts, in particular Traefik. Also it is a different method as used with (Flask) Python Apps. There the HTTP Header X_SCRIPT_NAME is set in the related Ingress and handled internally within the Flask app.

It appears to be possible to use the same X_SCRIPT_NAME mechanism to work for nginx backend apps. Thus the rewrite is not required anymore.

This can be effected by a simple addition to the nginx config, using the sub_filter feature on HTML (by default) and JavaScript (sub_filter_types application/javascript) content:

server {
    listen       80;
    server_name  localhost;

    #charset koi8-r;
    #access_log  /var/log/nginx/log/host.access.log  main;

    location / {
        # rewrite ^([^.]*[^/])$ $uri/ permanent;
        root   /usr/share/nginx/html/smartapp;

        # substitute /SCRIPT_NAME so if proxied the proper URLs are set
        sub_filter_once off;
        sub_filter_types application/javascript;
        sub_filter "/X_SCRIPT_NAME" $http_x_script_name;
        index  index.html;
    }


    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page  500 502 503 504 /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

The HTML and JavaScript content will need to have /X_SCRIPT_NAME in the content e.g.

<!DOCTYPE html>
<html>
<head>
    <title>SmartApp</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">

    <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css"
       integrity="sha512-M2wvCLH6DSRazYeZRIm1JnYyh22purTM+FDB5CsyxtQJYeKq83arPe5wgbNmcFXGqiSH2XR8dT/fJISVA1r/zQ=="
       crossorigin=""/>
    <link rel="stylesheet" href="/X_SCRIPT_NAME/lib/markercluster/1.1.0/MarkerCluster.css" />
   	<link rel="stylesheet" href="/X_SCRIPT_NAME/lib/markercluster/1.1.0/MarkerCluster.Default.css" />

    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css"/>

This replaces href="/X_SCRIPT_NAME/... with e.g. href="/smartapp/... when the X_SCRIPT_NAME header is set. If not set, e.g. when accessed directly this substitutes to href="/... and resolves as well. A very basic form of templating.

The SmartApp Ingress needs to be adapted to:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: smartapp
  namespace: smartemission
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
    nginx.ingress.kubernetes.io/add-base-url: "true"
    nginx.ingress.kubernetes.io/configuration-snippet: |
      proxy_set_header X-Script-Name /smartapp;
spec:
  rules:
  - host: pdok.smartemission.nl
    http:
      paths:
      - path: /smartapp
        backend:
          serviceName: smartapp-service
          servicePort: 80

The nice thing is that we have a single method within the Ingress-es (proxy_set_header X-Script-Name) and getting rid of the sometimes problematic rewrite.

In docker-compose this becomes

version: "3"

services:

  smartapp:

    image: smartemission/se-smartapp:latest

    container_name: smartapp

    restart: unless-stopped

    labels:
      - "traefik.backend=smartapp"
      - "traefik.enable=true"
      - "traefik.frontend.priority=500"
      - "traefik.frontend.rule=HostRegexp:{subdomain:[a-z]+}.smartemission.nl;PathPrefixStrip:/smartapp"
      - "traefik.frontend.headers.customRequestHeaders=X-Script-Name:/smartapp"
      - "traefik.docker.network=se_back"

    networks:
      - se_back

#    ports:
#      - 80:80

networks:
  se_back:
    external: true
@justb4 justb4 added the enhancement New feature or request label Apr 16, 2018
@justb4 justb4 self-assigned this Apr 16, 2018
justb4 added a commit that referenced this issue Apr 16, 2018
@justb4
Copy link
Contributor Author

justb4 commented May 1, 2018

The above solution was applied, but is in the long term too complex and cumbersome. The latest version (see also README) has the following characteristics, which make proxying much easier:

  • the nginx app does the redirect itself
  • the nginx app requires the /app path i.s.o. root, e.g. /smartapp
  • the nginx app is unaware of this, assumes root path
  • all the above is solved in the nginx config

Example for SmartApp config:

server {
    listen       80 default_server;
    absolute_redirect off;

    location /smartapp {
        root   /usr/share/nginx/html;
        index  index.html;
    }
.
.

}
  • app is installed under /usr/share/nginx/html/smartapp so resolves
  • /smartapp is always redirected 301 to /smartapp/
  • absolute_redirect off keeps outer host/port even when redirecting

For Traefik in Docker the Compose config is now simply:

version: "3"

services:

  smartapp:

    image: smartemission/se-smartapp:1.0.0

    container_name: smartapp

    restart: unless-stopped

    labels:
      - "traefik.backend=smartapp"
      - "traefik.enable=true"
      - "traefik.frontend.priority=500"
      - "traefik.frontend.rule=PathPrefix:/smartapp"
      - "traefik.docker.network=se_back"

    networks:
      - se_back

networks:
  se_back:
    external: true

For Kubernetes the redirect is not required and the path can be preserved in the ingress.yml:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: smartapp
  namespace: smartemission
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
spec:
  rules:
  - host: pdok.smartemission.nl
    http:
      paths:
      - path: /smartapp
        backend:
          serviceName: smartapp-service
          servicePort: 80

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant