openanalytics/shinyproxy-operator

Applications as custom resources

Opened this issue · 4 comments

It would be neat to be able to take application definitions out of the shinyproxy custom resource and have their configuration instead be deployed to kubernetes as custom resources themselves. That way adding a new app or changing an existing one wouldn't require an update of the shinyproxy custom resource itself.

For example, currently I have apps set up using the latest tag on their docker image. That way I don't need to touch the application.yml whenever an app is updated. Having apps defined as a custom resource would allow for me to use immutable tagging on docker images without incurring the pain of updating an application.yml or shinyproxy custom resource at a high frequency.

Are there already plans for this? I'm curious if there has been any discussion around it

Hi

We have had ideas in the same direction, but we don't have any concrete plans for implementing it. Our idea is basically as follows:

  • a ShinyProxy CRD without the specs property. This CRD defines all configuration of ShinyProxy except the exact specs, such as the authentication of ShiynProxy
  • a ShinyProxyApp CRD: specifies the specification of a single app and a reference to the main ShinyProxy CRD.
  • every time one of these CRDs is updated (either the main ShinyProxy one or one of the apps), ShinyProxy compiles these CRDs in one big ShinyProxy configuration file. This file is then handled in the same way as a complete CRD would be handled. That is, any change made to it will trigger a re-deploy of ShinyProxy (and maybe keeping an old instance around).

This would mostly be a convenience feature for end-users, but we do some great value in it. Note that (as I stated above) this would not change the behavior of the operator: every time you add/remove/update an application a redeploy of ShinyProxy will happen.

Finally, by using Kustomize you can already achieve a similar result, although it's not that polished...

Create a kustomize.yaml file containing:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
 - shinyproxy.yaml

patches:
- path: app1.yaml
  target:
    group: openanalytics.eu
    version: v1alpha1
    kind: ShinyProxy
    name: example-shinyproxy
- path: app2.yaml
  target:
    group: openanalytics.eu
    version: v1alpha1
    kind: ShinyProxy
    name: example-shinyproxy

Create a shinyproxy.yaml file:

apiVersion: openanalytics.eu/v1alpha1
kind: ShinyProxy
metadata:
  name: example-shinyproxy
  namespace: shinyproxy-dev
spec:
  spring:
    session:
      store-type: redis
    redis:
      host: redis.redis
  proxy:
    title: Open Analytics Shiny Proxy
    logoUrl: https://www.openanalytics.eu/shinyproxy/logo.png
    landingPage: /
    heartbeatRate: 10000
    heartbeatTimeout: 60000
    port: 8080
    containerBackend: kubernetes
    kubernetes:
      internal-networking: true
    authentication: simple
    users:
    - name: jack
      password: password
    - name: jeff
      password: password
    specs: []
  image: openanalytics/shinyproxy:2.5.0
  fqdn: operator-demo-dev.local

Create two apps:

- op: add
  path: /spec/proxy/specs/-
  value:
    id: 06_tabsets
    container-cmd: ["R", "-e", "shinyproxy::run_06_tabsets()"]
    container-image: openanalytics/shinyproxy-demo
    description: Tabsets
- op: add
  path: /spec/proxy/specs/-
  value:
    id: 01_hello
    displayName: Hello Application
    description: Application which demonstrates the basics of a Shiny app
    containerCmd: ["R", "-e", "shinyproxy::run_01_hello()"]
    containerImage: openanalytics/shinyproxy-demo
    kubernetes-pod-patches: |
      - op: add
        path: /spec/containers/0/env
        value:
          - name: SOME_PASSWORD
            valueFrom:
              secretKeyRef:
              name: some-password
              key: password

Run kustomize:

kustomize build .  > build.yaml

Check the end-result:

apiVersion: openanalytics.eu/v1alpha1
kind: ShinyProxy
metadata:
  name: example-shinyproxy
  namespace: shinyproxy-dev
spec:
  fqdn: operator-demo-dev.local
  image: openanalytics/shinyproxy:2.5.0
  proxy:
    authentication: simple
    containerBackend: kubernetes
    heartbeatRate: 10000
    heartbeatTimeout: 60000
    kubernetes:
      internal-networking: true
      namespace: shinyproxy-dev-apps
    landingPage: /
    logoUrl: https://www.openanalytics.eu/shinyproxy/logo.png
    port: 8080
    specs:
    - container-cmd:
      - R
      - -e
      - shinyproxy::run_06_tabsets()
      container-image: openanalytics/shinyproxy-demo
      description: Tabsets
      id: 06_tabsets
    - containerCmd:
      - R
      - -e
      - shinyproxy::run_01_hello()
      containerImage: openanalytics/shinyproxy-demo
      description: Application which demonstrates the basics of a Shiny app
      displayName: Hello Application
      id: 01_hello
      kubernetes-pod-patches: |-
        - op: add
          path: /spec/containers/0/env
          value:
            - name: SOME_PASSWORD
              valueFrom:
                secretKeyRef:
                name: some-password
                key: password
    title: Open Analytics Shiny Proxy
    users:
    - name: jack
      password: password
    - name: jeff
      password: password
  spring:
    redis:
      host: redis.redis
    session:
      store-type: redis

I'm looking forward to your thoughts on this!

I really like the framework you laid out - that's exactly what I was hoping for. The ShinyProxyApp CRD would open the door for me to fully automate an app update from a developer's commit straight to whatever the destination shinyproxy deployment would be. Right now I work around it via mutable docker tags, but this would even allow for seamlessly adding a new application.

Regarding kustomize: I have done something similar with helm and I agree that does make things easier, but for me the real value would come from being able to define an application's config outside of the global shinyproxy config.

Thank you very much for your thorough response - I really appreciate the work you put into this project

The Application CR could have label selectors that match one or more shinyproxys. Could be a very interesting way to allow teams to slide their apps into a shared proxy.

That's an interesting idea @kfox1111! It would make things a bit more complex on the operator side, e.g. when an app CR has been updated, the operator needs to figure which ShinyProxy servers are affected. But I think it's worth the effort.
I will keep these ideas in my mind and think about them a bit more.
More ideas are always welcome :)