[FEAT] Support sharing components/providers between manifests
brooksmtownsend opened this issue · 9 comments
Just like named configuration can be specified solely by name, without specifying the properties, and sourced from outside of an application, it would be great to be able to share a component or capability provider between different applications to cut down on duplication of compute.
For example, a minimal HTTP component manifest:
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: rust-hello-world
annotations:
version: v0.0.1
description: "HTTP hello world demo in Rust, using the WebAssembly Component Model and WebAssembly Interfaces Types (WIT)"
spec:
components:
- name: http-component
type: component
properties:
image: file://./build/http_hello_world_s.wasm
traits:
# Govern the spread/scheduling of the component
- type: spreadscaler
properties:
replicas: 1
# Add a capability provider that enables HTTP access
- name: httpserver
type: capability
properties:
image: ghcr.io/wasmcloud/http-server:0.20.0
traits:
# Link the httpserver to the component, and configure the HTTP server
# to listen on port 8080 for incoming requests
- type: link
properties:
target: http-component
namespace: wasi
package: http
interfaces: [incoming-handler]
source_config:
- name: default-http
properties:
address: 127.0.0.1:8080
If you imagine we have two of these basic HTTP component YAMLs, most of the configuration will look the same but we'll end up using two HTTP servers to facilitate communication.
To throw out a strawman, one possible solution to this would be to reference a provider by name and, optionally, manifest:
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: managed
annotations:
version: v0.0.1
description: "Managed shared provider"
spec:
components:
- name: httpserver
type: capability
properties:
image: ghcr.io/wasmcloud/http-server:0.20.0
Then, in our updated application manifest:
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: rust-hello-world
annotations:
version: v0.0.1
description: "HTTP hello world demo in Rust, using the WebAssembly Component Model and WebAssembly Interfaces Types (WIT)"
spec:
components:
- name: http-component
type: component
properties:
image: file://./build/http_hello_world_s.wasm
traits:
# Govern the spread/scheduling of the component
- type: spreadscaler
properties:
replicas: 1
# Reuse a capability from a different manifest
- name: httpserver
type: capability
properties:
app: managed
component: httpserver
traits:
# Link the httpserver to the component, and configure the HTTP server
# to listen on port 8080 for incoming requests
- type: link
properties:
target: http-component
namespace: wasi
package: http
interfaces: [incoming-handler]
source_config:
- name: default-http
properties:
address: 127.0.0.1:8080
The benefit of only allowing references to other applications is validation, which would let us look up dependent providers before deploying an application (and to warn when undeploying the managed
application)
This could be a real can of worms, but it's definitely worth it.
- There are some security implications here. It's been awhile since I discussed security and wasmcloud, so I forget where the trust boundaries are. I don't know if you can always assume that components under the same NATS topic can use each other's providers. Using someone else's MySQL provider would make for a very bad day.
- We may want to introduce a "weakchildof" concept to allow the original creator of the provider to be deleted without removing the shared provider. As of the last time I looked at Kubernetes, I don't think it ever really figured that out, but the concept certainly exists in ECS and other data structures.
Here is what @thomastaylor312 and I brainstormed here:
- Add metadata annotation wasmcloud.dev/shared=true
- Marking a manifest as shared means contents can be linked to/from in other manifests in that lattice
To change an example manifest from an HTTP server to a shared HTTP server:
Before
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: dog-fetcher
annotations:
description: 'An app that gets dog pics'
spec:
components:
- name: dog-fetcher
type: component
properties:
image: ghcr.io/wasmcloud/components/dog-fetcher-rust:0.1.0
traits:
- type: spreadscaler
properties:
instances: 5
- name: httpserver
type: capability
properties:
image: ghcr.io/wasmcloud/http-server:0.21.0
traits:
- type: link
properties:
target: dog-fetcher
namespace: wasi
package: http
interfaces:
- incoming-handler
source_config:
- name: dog-fetcher-address
properties:
address: 0.0.0.0:8081
After, shared manifest:
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: network
annotations:
description: 'Shared platform network capability providers'
wasmcloud.dev/shared: 'true'
spec:
components:
- name: httpserver
type: capability
properties:
image: ghcr.io/wasmcloud/http-server:0.21.0
App manifest:
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: dog-fetcher
annotations:
description: 'An app that gets dog pics'
spec:
components:
- name: dog-fetcher
type: component
properties:
image: ghcr.io/wasmcloud/components/dog-fetcher-rust:0.1.0
traits:
- type: spreadscaler
properties:
instances: 5
- name: httpserver
type: capability
properties:
# This references a component `httpserver` in manifest `network`
# Other manifest must be annotated with `wasmcloud.dev/shared=true`
# or this will be rejected at put time.
manifest:
name: network
component: httpserver
traits:
- type: link
properties:
target: dog-fetcher
namespace: wasi
package: http
interfaces:
- incoming-handler
source_config:
- name: dog-fetcher-address
properties:
address: 0.0.0.0:8081
As a part of putting the manifest we would resolve any manifest:
references by looking for the other manifest by name, checking the shared annotation, and then if set to true
see if that component exists. If the other manifest exists but is not deployed, we at least need to return a warning that this app cannot be deployed successfully, but this is probably an error.
We would do a similar lookup at deploy time to properly link from/to the component in the other manifest based on its actual component ID in the lattice.
The "weakchildof" concept that @cdmurph32 sounds great IMO, what we should absolutely do for this feature is prevent undeploying resources that other apps depend on (perhaps without a force). We may be able to detail a couple of strategies for the undeploy / delete such that the delete can be non_destructive, destructive, or a weakchild strategy where resources stick around if they are depended on
Supercedes #120
I like the option of shared manifests . Isn't the naming confusing though ?
The provider is being referred to as a component in this shared manifest .
manifest:
name: network
component: httpserver
Do you think it would be better to call this provider itself ?
Potentially we could have components and providers deployed as part of the shared manifests.
Isn't the naming confusing though ?
The provider is being referred to as a component in this shared manifest .
It is somewhat, what we're really referring to is the component keyword in the manifest, not webassembly component. We're overloading the term here, but happy to take suggestions
spec:
components:
- name: dog-fetcher
type: component
...
- name: httpserver
type: capability
Still thinking through the ramifications of this change, but I think naming is a bit off in the shared application reference. Instead of
# This references a component `httpserver` in manifest `network`
# Other manifest must be annotated with `wasmcloud.dev/shared=true`
# or this will be rejected at put time.
manifest:
name: network
component: httpserver
it should probably be
# This references a component `httpserver` in manifest `network`
# Other manifest must be annotated with `wasmcloud.dev/shared=true`
# or this will be rejected at put time.
application:
name: network
component: httpserver
Applications are what we're all working with, manifests are just the YAML version of them
@protochron I think this makes sense, and it likely means that we can fail a bit earlier in the deployment process if it's obvious you should be looking for a running application 🤔 I'll make this change in my WIP PR
Just wanted to add use cases here to further motivate this work...
There is a whole class of problems this would solve, where instead of having providers as proxies to singleton non-wasmCloud deployments of supporting software (reverse proxy, database, cache), the wasmCloud providers would become the singleton deployments. Eliminating the need for supporting software outside of wasmCloud means I can get rid of k8s or other managed services.
Example 1: a singleton HTTP server provider that multiple components can contribute endpoint handlers to (reverse proxy extensibility)
Example 2: a singleton cache provider that multiple components can get/set from
Example 3: a singleton pub-sub provider that components can exchange application messages on
Example 4: a singleton embedded database provider that components can contribute tables to
@atifsyedali thank you for the additional use cases, these will be helpful when setting up tests & edge cases in #381 !