---
tags: fbc
---
# A slightly more complex (semver-based) veneer story
In the [previous post](https://hackmd.io/J6PQYDnLTfqYGLWpOs44aQ), I walked through the basics of file-based catalogs and the simplest possible "veneer" (any tool that facilitates building file-based catalogs idempotently)
In this post, I'll describe a more useful veneer that touches on three major use cases:
1. Automated channel definitions and upgrade edge generation based on semver
2. Channel promotion
3. Cross channel upgrades
### Terminology
- __Channel__: a curated and ordered set of bundle versions that defines an upgrade graph through a subset of a package's bundles.
- __Upgrade edge__: a property of a channel entry that declares that _BundleB_ can be upgraded from _BundleA_. There are various types of upgrade edges supported by OLM, which we'll cover later in more detail.
- __Channel promotion__: the act of promoting a bundle from one channel to another, typically based on qualitative assesments of maturity and the level of testing of that bundle (e.g. promotion from a "fast" channel to a stable "channel"). When a bundle is promoted from one channel to another, its upgrade edges typically change. If "fast" has bundles A -> B -> C, and "stable" has only A -> C, C upgrades from B in "fast" and from A in "stable"
- __Cross-channel upgrades__: A cross-channel upgrade occurs on a cluster when a cluster admin changes the channel of an existing subscription so that they can begin receiving a different stream of upgrades. Perhaps they desire more stability, so they update their subscription from "fast" to "stable". Perhaps they want more agility, so they update their subscription from "stable" to "fast".
## Veneer
Can we build a veneer that covers all of the above-mentioned use cases? Let's think through a scenario.
### Input bundles
Given a set of bundles:
- foo.v0.1.0
- foo.v0.1.1
- foo.v0.1.2
- foo.v0.1.3
- foo.v0.2.0
- foo.v0.2.1
- foo.v0.2.2
- foo.v0.3.0
- foo.v1.0.0
- foo.v1.0.1
- foo.v1.1.0
we can derive a variety of semver-based channels.
### Channel construction
We'll ignore channel promotion for now and assume that _all_ releases will be shipped to a "candidate" set of channels. We want to construct channels to enable cluster admins to select from a variety of upgrade strategies, so we'll build "candidate-vX" and "candidate-vX.Y" channels.
There are three main features of these channels:
- The replaces chains are formed by connecting the highest patch versions of each minor version
- Within each minor version the highest patch version skips all other patch versions
- In X.Y channels, the tail of each channel (the last version in the replaces chain) should replace the head of the previous channel (previous being next lowest X.Y version's channel)
#### Major version channels
##### `candidate-v0`
```mermaid
graph LR
foo.v0.3.0 -- replaces --> foo.v0.2.2
foo.v0.2.2 -- replaces --> foo.v0.1.3
foo.v0.2.2 -- skips --> foo.v0.2.1
foo.v0.2.2 -- skips --> foo.v0.2.0
foo.v0.1.3 -- skips --> foo.v0.1.2
foo.v0.1.3 -- skips --> foo.v0.1.1
foo.v0.1.3 -- skips --> foo.v0.1.0
```
##### `candidate-v1`
```mermaid
graph LR
foo.v1.1.0 -- replaces --> foo.v1.0.1
foo.v1.0.1 -- skips --> foo.v1.0.0
```
:::warning
**NOTE**: there is no `replaces` link to any v0 version here. That's because semver dictates that major version bumps are breaking changes. Therefore we should never allow automatic upgrades to traverse major versions.
:::
#### Minor version channels
##### `candidate-v0.1`
```mermaid
graph LR
foo.v0.1.3 -- skips --> foo.v0.1.2
foo.v0.1.3 -- skips --> foo.v0.1.1
foo.v0.1.3 -- skips --> foo.v0.1.0
```
##### `candidate-v0.2`
```mermaid
graph LR
foo.v0.2.2 -- skips --> foo.v0.2.1
foo.v0.2.2 -- skips --> foo.v0.2.0
style foo.v0.1.3 fill:#fff,stroke:#999,stroke-width:2px,stroke-dasharray: 5 5,color:#999
foo.v0.2.2 -- replaces --> foo.v0.1.3
```
##### `candidate-v0.3`
```mermaid
graph LR
foo.v0.3.0
style foo.v0.2.2 fill:#fff,stroke:#999,stroke-width:2px,stroke-dasharray: 5 5,color:#999
foo.v0.3.0 -- replaces --> foo.v0.2.2
```
##### `candidate-v1.0`
```mermaid
graph LR
foo.v1.0.1 -- skips --> foo.v1.0.0
```
##### `candidate-v1.1`
```mermaid
graph LR
style foo.v1.0.1 fill:#fff,stroke:#999,stroke-width:2px,stroke-dasharray: 5 5,color:#999
foo.v1.1.0 -- replaces --> foo.v1.0.1
```
### Veneer config file
Now that we have an idea of what we want the veneer to do, we can try to define a veneer config file:
```yaml=
schema: olm.semver
candidate:
bundles:
- image: quay.io/foo/foo-bundle:v0.1.0
- image: quay.io/foo/foo-bundle:v0.1.1
- image: quay.io/foo/foo-bundle:v0.1.2
- image: quay.io/foo/foo-bundle:v0.1.3
- image: quay.io/foo/foo-bundle:v0.2.0
- image: quay.io/foo/foo-bundle:v0.2.1
- image: quay.io/foo/foo-bundle:v0.2.2
- image: quay.io/foo/foo-bundle:v0.3.0
- image: quay.io/foo/foo-bundle:v1.0.0
- image: quay.io/foo/foo-bundle:v1.0.1
- image: quay.io/foo/foo-bundle:v1.1.0
fast:
bundles:
- image: quay.io/foo/foo-bundle:v0.2.1
- image: quay.io/foo/foo-bundle:v0.2.2
- image: quay.io/foo/foo-bundle:v0.3.0
- image: quay.io/foo/foo-bundle:v1.0.1
- image: quay.io/foo/foo-bundle:v1.1.0
stable:
bundles:
- image: quay.io/foo/foo-bundle:v1.0.1
```
With this config file, we could:
1. Build all of the `olm.bundle` blobs by building a set of unique images and then rendering them.
2. Build all of the `olm.channel` blobs by repeating the approach described in the **Channel construction** section of this doc for each of the `candidate`, `fast`, and `stable` configurations.
3. Build the `olm.package` blob by using content from the highest semver bundle in the global set of bundles, and choosing the default channel as the most mature X.Y channel (i.e. favor `stable` over `fast` and favor `fast` over `candidate`).