---
# System prepended metadata

title: Tech Forum ytt schema Annotations Scratchpad
tags: [ytt]

---

---
title: Tech Forum ytt schema Annotations Scratchpad
tags: ytt
---

Existing annotations
--
`@overlay`
- `@overlay/match-child-defaults`
- `@overlay/merge`
- `@overlay/remove`
- `@overlay/replace`
- `@overlay/insert`
- `@overlay/append`
- `@overlay/assert`
- `@overlay/match by="x"`
- `@overlay/match missing_ok=True`
- `@overlay/match expects=2`
- `@overlay/match when=2`
- 

`@schema`
- `@schema/type any=True`
- `@schema/nullable`
- (future)
- (future) `@schema/default value`
- (future) `@schema/key [allowed=Regexp] [(expects=Int|String|List|Function | missing_ok=Bool)]`
- (future) `@schema/validate func()`
- (future) `@schema/title "string"`
- (future) `@schema/doc "string"`
- (future) `@schema/example example`
- (future) `@schema/examples [examples]`
- (future) `@schema/deprecated "string"`
- (future) `@schema/removed`

`@data`
- `@data/values`

`@library`
- `@library/ref "@app"`
- `#@library/ref "@lib1@~foo"`

`@ load("x.star", "x")`

Considerations
---
* We want to avoid breaking changes
* We want a simple UX
* Can we utilize patterns in our current annotations?
* Does it make sense to have one schema definition for inputs _and_ outputs?

Current schema example
---
```yaml 
#@schema/match data_values=True
---
#@schema/nullable
nothing:
string: ""
#@schema/type any=True
any:
```

Alternatives
---
```yaml
#@schema/match "data_values"
---
#@schema/nullable
nothing:
string: ""
#@schema/type any=True
any:
```

```yaml
#@schema/data_values
---
#@schema/nullable
nothing:
string: ""
#@schema/type any=True
any:
```


```yaml
#@data/values
#@schema/values
#@schema/k8s
#@schema/concourse


#@schema/definition name="data/values"
#@schema/def name="data/values"
#@schema/def data_values=True
#@schema/

#@data/schema

#@template/schema
#@output/schema
```


```yaml=
#@data/schema
#@library/ref "@libby"
---
#@schema/validate min=1
bananas: 1


#@data/schema name="schema2"
---
#@schema/validate min=1
bananas: 1
```

```yaml=
#@template/schema name=""
---

```

```yaml
#@schema/use name="schema1"
---
bananas: "wrong"
```
1. in input bananas is an int and >0
2. in output 

```yaml
#@schema/use name=""
---
a:
  somevalue: #@ data.values.bananas 

```
:banana: 
:banana: 
:banana: 
:monkey: 

## Schema functions?
```
# current data module function:
vals = data.list()

# future schema functions 
template.validate()
data.validate()

#@ s = schema.get("schema.yml")
#@ violations = s.check(yaml_docset)
#@ violations = s.validate(yaml_docset)

```
Schema and template in the same file
```
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 1


#@schema/def 
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: default-deployment
  labels:
    app: default
spec:
  replicas: 3
  selector:
    matchLabels:
      app: default
  template:
    metadata:
      labels:
        app: default
    spec:
      containers:
      - name: default
        image: default:1.14.2
        ports:
        - containerPort: 80
```
```

#@schema/values


#@schema/def name="k8s"
#@schema/def name="concourse"

#@schema/def name="deployment" group="apps" version="v1"

#@schema/def name="" match_by=matcher_func()
```

Future state?
---
`schema.yml`
```yaml=
#@schema/def name="k8/deployment"
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: default-deployment
  labels:
    app: default
spec:
  replicas: 3
  selector:
    matchLabels:
      app: default
  template:
    metadata:
      labels:
        app: default
    spec:
      containers:
      - name: default
        image: default:1.14.2
        ports:
        - containerPort: 80
```

`config.yml`
```yaml=
#@schema/use name="k8/deployment" version="v1"
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 1
 ```
 
 
 
## Additional Context

- the "Data module" while serving lots of value has room to grow:
    - today there are two channels of inputs: Data Values and Data files.
    - e.g. perhaps having other named sources of data (like some subset of Kubernetes Cluster state)


In terms of considering different syntax
- practical test: how likely will the user remember one syntax over another?
- what are the range of mental models (low fidelity ~ copy-and-paste <=== ===> high fidelity ~ deep conceptual understanding of individual features)

We'd maximize value/flexibility if we solve for as much of that range as we can.


### Additional Option: `@data/values-schema`

```yaml=
#@data/values-schema
---

#@schema/nullable
optional_bits:
```

- if we look deeper at it, `@data/values` is actually an overlay... one could say the full name of that annotation is `@data/values-overlay`
- today, there's only one schema, but given notes above about potential future growth, it's not a limitation we'd be wise to commit to (which makes `@data/schema` a bit "too" terse)
- we've got what seems to be a conceptually balanced thing going with `@(module-name)/(annotation-name)`, so adding another `/` into the mix seems like its going to make that whole thing more complicated.
- the schema we're declaring is specifically for Data's Values. So, `values-schema` seems like a fitting noun.

### Exploring a Programmatic API for Schema

- draws in bottom-up design forces: what _ought_ to be fundamentally true about a given concept (like schemas)?
- can help maintain orthogonality between features (the stronger the identity/definition/set of responsibilities, the less likely we'll accidentally misplace one)

sketching...
```
#@ blah = library.get("blah")
#@ child_schema = blah.data_values_schema()
#@ blah = blah.with_data_values_schema(my_add_ons())
#@ s = schema.get("data_values")
#@ schema.get_data_values()

#@ s.check(doc|docset)
#@ s.validate(config_map)
```

... also, if we build up programmatic primitives, we might be able to realize higher-level features through composition.

For example, what if schema could be `load()`'ed and then applied via an overlay?
```
#@overlay/match by=overlay.all
#@overlay/assert via=lambda left, right : k8s_schema.find(left).validate(left)
---
```

or used directly in a template?
```
#@ load("k8s_schemas", "k8s")

#@schema/type via=k8s.apps.v1.Deployment
#@schema/type k8s.apps.v1.Deployment
---
foo : #@ ...
spec:
  #@schema/type k8s.core.v1.PodSpec
  template:
    containers:
    #@schema/type k8s.core.v1.PodSpec.Container, default_omitted=True
    - name:
      image:
```