Skip to content

Commit

Permalink
Add "Getting Started" Guide to website
Browse files Browse the repository at this point in the history
- moved existing examples to `examples/playground/ref` to make way for
  another set of examples.
- plumbed from one "Examples" to many "ExampleSets": now there's a
  title, description, and set of examples (each containing a set of
  files).
  - each example set has a button next to the "Interactive Playground"
    header; when clicked toggles the contents of that example set. Each
    button has an indicator of whether it is expanded or collapsed.
  - by default, the first example set listed is expanded.
  - by default, the default example is/remains `demo` since this example
    plays well with the "Basic Usage" text and is the most appropriate
    first impression for brand new users.
- added a nearly complete track of examples (a baker's dozen) that form
  the "Getting Started" example set. Each includes a "readme" with
  instructions, a sample command-line, and the input files.
  - there's an overlay that subtracts the text of the "readme" so that
    it is not repeated in the output.

Co-authored-by: "Aaron Hurley" <[email protected]>
Co-authored-by: "Cari Dean" <[email protected]>
Signed-off-by: "Dmitriy Kalinin" <[email protected]>
Signed-off-by: "Eli Wrenn" <[email protected]>
  • Loading branch information
3 people committed May 28, 2020
1 parent 08e9cad commit c8e2211
Show file tree
Hide file tree
Showing 166 changed files with 2,658 additions and 93 deletions.
5 changes: 5 additions & 0 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@


1. cat deployment.yaml
1. (extract fragment) ytt -f deployment.yaml
1.
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
readme: |
=====================
Appending Array Items
=====================
Map items are easy to match: if the key of the "left" and
the key of the "right" are equal, we can assume they
refer to the same item. Lemon squeezy, default behavior.
Array items don't have keys; they are referenced by index.
We can not assume two items at the same position in the
array refer to the same item.
In ytt, you must specify how to match array items. Do this
using the same `#@overlay/match by=...` annotation used on
documents.
So, all of these operations must be paired with a
`@overlay/match`:
- `@overlay/merge`
- `@overlay/replace`
- `@overlay/remove`
- `@overlay/insert`
The one exception is `@overlay/append` which ignores any
`@overlay/match` and always inserts after the last item.
------------------------
Append vs. Ensure Exists
------------------------
`append-https-port.yml` showcases how to add new array
items.
Explore:
1. Summarize what this overlay does in a sentence.
2. Examine the output of `config/service.yml`; what's
wrong there? (hint: ports must be unique)
3. Just above `#@overlay/append` _add_ the following:
```
#@overlay/remove
#@overlay/match by="name", missing_ok=True
- name: https
```
How does `config/service.yml` look?
4. Does the same possibility exist for the `Deployment`?
................
Thinking in ytt:
"Overlays apply not to collections, but items:
not docsets, documents;
not maps, map items;
not arrays, array items."
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
cmd: |
This is what you would type on the command-line.
$ ytt -f config -f optional/append-https-port.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#@ load("@ytt:data", "data")
#@ load("lib/consts.star", "version", "deployment")

---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/name: #@ data.values.app_name
app.kubernetes.io/version: #@ version
name: #@ deployment["name"]
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/component: controller
app.kubernetes.io/name: #@ data.values.app_name
template:
metadata:
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/name: #@ data.values.app_name
app.kubernetes.io/version: #@ version
spec:
containers:
- name: prometheus-operator
image: #@ "quay.io/coreos/prometheus-operator:" + version
args:
- --kubelet-service=kube-system/kubelet
- --logtostderr=true
- --config-reloader-image=jimmidyson/configmap-reload:v0.3.0
- #@ "--prometheus-config-reloader=quay.io/coreos/prometheus-config-reloader:" + version
ports:
- name: http
containerPort: #@ deployment["containerPort"]
resources:
requests:
cpu: 100m
memory: 100Mi
limits:
cpu: 200m
memory: 200Mi
securityContext:
runAsNonRoot: yes
runAsUser: 65534


Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
load("@ytt:data", "data")

# Version of prometheus
version = "v0.39.0"

# Shared values from Service
service = {
"name": "{}-service".format(data.values.app_name),
"port": 38080
}

deployment = {
"name": data.values.app_name,
"containerPort": 8080
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#@ load("@ytt:data", "data")
#@ load("lib/consts.star", "version", "service", "deployment")

---
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/name: #@ data.values.app_name
app.kubernetes.io/version: #@ version
name: #@ service["name"]
spec:
type: ClusterIP
selector:
app.kubernetes.io/component: controller
app.kubernetes.io/name: #@ data.values.app_name
ports:
- name: http
port: #@ service["port"]
protocol: TCP
targetPort: #@ deployment["containerPort"]
- name: https
port: 30443
targetPort: 443
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#@data/values
---
app_name: "prometheus-operator"
scaling_factor: 2
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#@ load("@ytt:overlay", "overlay")
#@ load("/config/lib/consts.star", "deployment", "service")

#@overlay/match by=overlay.subset({"kind": "Service", "metadata": {"name": service["name"]}})
---
spec:
ports:
#@overlay/append
- name: https
port: 30443
protocol: TCP
targetPort: 443

#@overlay/match by=overlay.subset({"kind": "Deployment", "metadata": {"name": deployment["name"]}})
---
spec:
template:
spec:
containers:
#@overlay/match by=overlay.subset({"name": "prometheus-operator"})
- ports:
#@overlay/append
- name: https
containerPort: 443
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#@overlay/match by=lambda i,l,r: ("readme" in l) or ("cmd" in l),expects=2
---
#@overlay/match by=lambda i,l,r: True
#@overlay/replace via=lambda o,_: "\n".join(["" for l in o.split("\n")])
_:
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
readme: |
=================================
Capture Calculations In Functions
=================================
Functions in ytt can either explicitly return a value
or implicitly return a YAMLFragment.
In `deployment.yml`, below...
- `cpus()` and `memorys()` explicitly return resource
values with units.
- `request_50m_per_factor()` implicitly returns
a YAMLFragment that is the resource configuration.
---------------------
Changing One Thing...
---------------------
Note what happens when you change `scaling_factor` to 4.
In Kubernetes, `replicas` must be an integer...
- Change `scaling_factor` to 3.
- What happens to `replicas`?
- What happens to `resources`?
- How would you make `replicas` an integer (regardless of
the value of `scaling_factor`)?
-------------------------
Intention-Revealing Names
-------------------------
Naming is hard. When humans are reading the code, the time
you spend crafting words that convey intention pay off.
Consider `request_50m_per_factor`. Contrast that name with:
- `resources()`
- `requests_and_limits()`
- `generate_resource_request_and_limits_based_on_factor()`
The name we gave assumes the reader has _some_ familiarity
with the domain and describes what "novel" bit the function
brings.
Pro-Tip: look at a line the function is being invoked. Ask,
"How much can a reader familiar with the domain properly
infer about what the function does without reading its
definition?"
The answer shows how much intention has been revealed by
that name.
For those seeking extra credit, consider
`labels(withVersion)`. Could the parameter name be made
more intention revealing?
................
Thinking in ytt:
"Optimize for the global maxima of reader comprehension...
...over the local maxima of author 'productivity.'"
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
cmd: |
This is what you would type on the command-line.
$ ytt -f config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#@ app_name = "prometheus-operator"
#@ version = "v0.39.0"
#@ scaling_factor = 2

#@ def cpus(n):
#@ return str(n)+"m"
#@ end

#@ def memorys(n):
#@ return "{}Mi".format(n)
#@ end

#! request_50m_per_factor — generates a resources request
#! given a scaling factor such that the limits are twice
#! the initial request.
#@ def request_50m_per_factor(factor):
#@ initial = { "cpu": 50*factor, "memory": 50*factor }
requests:
cpu: #@ cpus(initial["cpu"])
memory: #@ memorys(initial["memory"])
limits:
cpu: #@ cpus(2*initial["cpu"])
memory: #@ memorys(2*initial["memory"])
#@ end
---

#@ def labels(withVersion=False):
app.kubernetes.io/component: controller
app.kubernetes.io/name: #@ app_name
#@ if withVersion:
app.kubernetes.io/version: #@ version
#@ end
#@ end
---
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
comment: "This deployment was lifted from\nthe prometheus K8s configuration.\nYou might notice that this line\nis supplied as a single string\nin the source, but a multi-line\nstring in the output."
labels: #@ labels(withVersion=True)
name: #@ app_name
namespace: default
spec:
replicas: #@ scaling_factor / 2
selector:
matchLabels: #@ labels()
template:
metadata:
labels: #@ labels(withVersion=True)
spec:
containers:
- name: prometheus-operator
image: #@ "quay.io/coreos/prometheus-operator:" + version
args:
- --kubelet-service=kube-system/kubelet
- --logtostderr=true
- --config-reloader-image=jimmidyson/configmap-reload:v0.3.0
- #@ "--prometheus-config-reloader=quay.io/coreos/prometheus-config-reloader:" + version
ports: [{name: http, containerPort: 8080}]
resources: #@ request_50m_per_factor(scaling_factor)
securityContext:
runAsNonRoot: yes
runAsUser: 65534
---
apiVersion: v1
kind: Service
metadata:
labels: #@ labels(withVersion=True)
name: #@ app_name + "-service"
spec:
type: ClusterIP
ports:
- name: http
port: 38080
protocol: TCP
targetPort: 8080
selector: #@ labels()

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#@overlay/match by=lambda i,l,r: ("readme" in l) or ("cmd" in l),expects=2
---
#@overlay/match by=lambda i,l,r: True
#@overlay/replace via=lambda o,_: "\n".join(["" for l in o.split("\n")])
_:
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
readme: |
=============================
Collect Modules into Packages
=============================
Naming each and every file has started to get out of hand.
In ytt, you can specify a directory and it will scan that
directory (and sub-directories) for all files.
Our project now looks like this:
config
├── common.lib.yml
├── consts.star
├── deployment.yml
├── service.yml
└── values.yml
And our `ytt` command-line looks sane again.
We say `config` is a package containing two templates,
a data value file, and two modules.
................
Thinking in ytt:
"Packages gather files that go together...
...and separate those that do not."
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
cmd: |
This is what you would type on the command-line.
$ ytt -f config
Loading

0 comments on commit c8e2211

Please sign in to comment.