implementable
olm.bundle.object
from non-head bundles)?olm.channel
schema? Should our channel veneers support extra static entries? If we do that, how would we hook the generated entries into the extra static entries?This enhancement proposes a minimal set of veneers that operator authors can use to simplify maintaining file based catalogs. It also describes an overview of what workflows using veneers may look like.
File based catalogs place all information about bundles, channels and packages in one or more flat files that can be directly manipulated by operator authors. This however exposes a lot of information that authors did not have to concern themselves with the imperative workflow followed by opm with sqlite based catalogs.
Veneers aim at simplifying the file based catalog generation for operator authors, presenting a set of veneers that may be expanded to get a valid file based catalog while reducing the configuration that needs to be done to obtain a valid file based catalog.
File based catalogs allows for operator authors to declaratively define metadata surrounding their operator, including specific properties and upgrade behavior. In the process of exposing this information, it also exposes a lot of complexity that they then have to deal with, making publishing an operator difficult. This enhancement proposes a API to allow interacting with a simplified view or veneer, containing only subset of information provided by the whole file based catalog.
This enhancement describes:
A veneer is a binary that can reduce a file based catalog into a subset of its fields and override specific fields on a catalog. Any executable can act as a veneer as long as it respects the following restrictions:
schema
and package
information, along with any source information like the image
of a bundleEach veneer need not create a catalog that is valid. The final output of applying a series of veneers is completed by the command opm inflate
, which fills in any required and missing fields from a data source like an image.
The veneers exist outside of the catalog itself and can be ignored with .indexignore
entries. An operator using veneers may have its repository structured as below:
.
├── awesome-operator
│ ├── .indexignore
│ ├── bundle-1.yaml
│ ├── bundle-2.yaml
│ ├── bundle-3.yaml
│ ├── channels.yaml
│ └── package.yaml
└── veneer
├── awesome-operator
│ └── properties.config.yaml
├── olm.properties.veneer
├── olm.channel.veneer
├── my.awesome.veneer
└── veneer.history
The veneers may then applied like:
./veneer/olm.properties.veneer -o ./awesome-operator/ ./awesome-operator/properties.yaml --config ./veneer/awesome-operator/properties.config.yaml
./veneer/olm.channel.veneer -o ./awesome-operator/ ./awesome-operator/channels.yaml
./veneer/my.awesome.veneer ./awesome-operator/bundle-* -o - > ./awesome-operator/bundles.yaml
opm inflate ./awesome-operator > catalog.yaml
For a catalog structure as show above, applying a set of veneers looks like:
$ ./veneer/olm.properties.veneer ./awesome-operator/ -i --config=config/properties.yaml
$ ./veneer/olm.channel.veneer ./awesome-operator/channels.yaml -o ./awesome-operator/channels.foo.yaml
$ ./veneer/my.awesome.veneer --bar=baz ./awesome-operator -o - > final-catalog.yaml
There are some commonly encountered operations on file based catalogs that operator-framework will provide veneers for. Apart from these officially supported veneers, operator authors may use third party veneers or create their own veneers for specific use cases. olm supported veneers are additive: each veneer ensures certain fields on their input.
The two types of veneers supported by operator-framework are:
These are meant for adding bundles to packages and channels and to define specific upgrade edges between bundle versions. Veneers in this set ensure specific upgrade edges on a channel. The olm channel veneers do not generate skipRange
entries, so some post processing is required in cases where this is needed.
The following officially supported veneers fall into this category:
This veneer allows a channel to be populated with a set of bundles in semver order. The generated upgrade chain will have no skipped bundles unless skipPatch
is set to true. This veneer takes the following arguments:
A sample configuration for this veneer might look like:
{
"schema":"olm.veneer.semver",
"package":"awesome-operator",
"name":"release-v{{.Major}}.{{.Minor}}",
"semverRange":">={{.Major}}.{{.Minor}}.x",
"skipPatch": true
}
When applied to a catalog like below, an olm.channel
object is generated.
.
└── awesome-operator
├── .indexignore
├── bundle.v1.0.0.yaml
├── bundle.v1.0.1.yaml
├── bundle-v1.0.2.yaml
├── bundle-v2.0.0.yaml
└── package.yaml
{
"schema": "olm.channel",
"name": "release-v1.0",
"package": "awesome-operator",
"entries": [
{
"name": "bundle.v1.0.0",
},
{
"name": "bundle.v1.0.1",
"replaces": "bundle.v1.0.0"
},
{
"name": "bundle.v1.0.2",
"skips": [
"bundle.v1.0.0",
"bundle.v1.0.1"
]
}
]
}
{
"schema": "olm.channel",
"name": "release-v2.0",
"package": "awesome-operator",
"entries": [
{
"name": "bundle.v2.0.0"
}
]
}
Covington (named for its proximity to Cincinnati) is a veneer that builds a channel based on a defined order of versions in a channel. It creates an upgrade chain out of a provided list of bundles.
The config for Covington has the following fields:
This veneer forms a replaces chain from non-tombstoned versions. The basic rules are:
tailEdges
in its entry.entries
array must not be in the tombstones
array.A sample config for this looks like:
{
"schema": "olm.veneer.covington",
"package": "awesome-operator",
"name": "release-v1.0",
"entries": [
"example.v0.1.0",
"example.v0.2.0",
"example.v0.2.1",
"example.v0.2.2",
"example.v1.0.0",
"example.v1.0.1"
],
"tombstones": [
"example.v0.2.0",
"example.v0.2.1",
"example.v1.0.0"
],
"tailEdges": {
"skips": [
"example.v0.0.1",
"example.v0.0.2"
]
}
}
When applied, the above config generates an olm.channel
object like:
{
"schema": "olm.channel",
"name": "release-v1.0",
"package": "awesome-operator",
"entries": [
{
"name": "example.v0.1.0",
"replaces": "example.v0.1.0",
"skips": [
"example.v0.0.1",
"example.v0.0.2"
]
},
{
"name": "example.v0.2.2",
"replaces": "example.v0.1.0",
"skips": [
"example.v0.2.0",
"example.v0.2.1"
]
},
{
"name": "example.v1.0.1",
"replaces": "example.v0.2.2",
"skips": [
"example.v1.0.0"
]
}
]
}
The upgrade edge veneer allows explicitly specifying edges to be included in a channel.
The veneer's config accepts the following arguments:
upgradeEdges
fieldA sample config file for this veneer would look like:
{
"schema": "olm.veneer.upgrade",
"package": "awesome-operator",
"name": "release-v1.0",
"tombstones": [
"awesome-operator.v1.0.0"
],
"upgradeEdges": [
["awesome-operator.v1.0.0", "awesome-operator.v1.0.1"],
["awesome-operator.v1.0.1", "awesome-operator.v1.0.2"],
["awesome-operator.v1.0.0", "awesome-operator.v1.0.3"],
["awesome-operator.v1.0.2", "awesome-operator.v1.0.3"],
["awesome-operator.v1.0.1", "awesome-operator.v1.0.3"],
]
}
Applying the above config results in an olm.channel
object like:
{
"schema": "olm.channel",
"name": "release-v1.0",
"package": "awesome-operator",
"entries": [
{
"name": "awesome-operator.v1.0.1",
"skips": [
"awesome-operator.v1.0.0"
]
},
{
"name": "awesome-operator.v1.0.2",
"replaces": "awesome-operator.v1.0.1",
"skips": [
"awesome-operator.v1.0.0"
]
},
{
"name": "bundle.v1.0.3",
"replaces": "awesome-operator.v1.0.2",
"skips": [
"bundle.v1.0.0",
"bundle.v1.0.1"
]
}
]
}
These are meant for adding or modifying metadata to objects. The bundle override veneer described below belongs to this type of veneers.
This veneer allows ensuring or overriding specific fields on a bundle. It searches for an object with a matching schema, package and name, ensuring members of the override
field on the bundle. Any property
entries are appended to the existing list of properties while relatedImages
are overridden based on the name
.
It accepts the following arguments:
olm.bundle
object must already be present in the input catalog directory, or a stub olm.bundle
object with only the specified properties will be generated.name
to identify the olm.bundle
object to modify.olm.bundle
object. It supports overriding properties
and relatedImages
, but may be extended to support arbitrary fields.properties
: These are properties that must be ensured on the bundle object. Property objects can have multiple entries for each type.relatedImages
: These are overrides to the relatedImages field in a bundle object. Overrides allow only one value per name
for this field, so a value may be overwritten.A sample config file for the veneer looks like:
{
"schema": "olm.veneer.bundle",
"name": "my-awesome-operator",
"package": "awesome-package",
"override": {
"properties": [
{
"type": "olm.maxOpenShiftVersion",
"value": "4.8"
},
{
"type": "my.awesome.property",
"value": "foo"
}
],
"relatedImages": [
{
"name": "",
"image": "registry.redhat.io/awesome/new@sha256:b480e64de68bbe7c271623cf9a9f88067fa7d1dd4dc54f192ca505ecfb25dd17"
}
]
}
}
This can then be applied to a bundle object like below:
{
"schema": "olm.bundle",
"name": "my-awesome-operator",
"package": "awesome-package",
"image": "registry.redhat.io/awesome/operator@sha256:55166d313d270ae4740a260149bfb41df1af8214744e9c38abef07add49c95de",
"properties": [
{
"type": "olm.gvk",
"value": {
"group": "api.awesome.org",
"kind": "APIManager",
"version": "v1alpha1"
}
},
{
"type": "my.awesome.property",
"value": "bar"
},
{
"type": "olm.bundle.object",
"value": {
"data": "eyJhcGlWZXJzaW9uIjoiYXBpZXh0ZW5zaW9ucy5rOHMuaW8vdjFiZXRhMSIsImtpbmQiOiJDdXN0b21SZXNvdXJjZURlZmluaXRpb24iLCJtZXRhZGF0YSI6eyJuYW1lIjoibGltaXRzLmNhcGFiaWxpdGllcy4zc2NhbGUubmV0In0sInNwZWMiOnsiZ3JvdXAiOiJjYXBhYmlsaXRpZXMuM3NjYWxlLm5ldCIsIm5hbWVzIjp7ImtpbmQiOiJMaW1pdCIsImxpc3RLaW5kIjoiTGltaXRMaXN0IiwicGx1cmFsIjoibGltaXRzIiwic2luZ3VsYXIiOiJsaW1pdCJ9LCJzY29wZSI6Ik5hbWVzcGFjZWQiLCJzdWJyZXNvdXJjZXMiOnsic3RhdHVzIjp7fX0sInZhbGlkYXRpb24iOnsib3BlbkFQSVYzU2NoZW1hIjp7InByb3BlcnRpZXMiOnsic3BlYyI6eyJwcm9wZXJ0aWVzIjp7Im1heFZhbHVlIjp7ImZvcm1hdCI6ImludDY0IiwidHlwZSI6ImludGVnZXIifSwibWV0cmljUmVmIjp7InByb3BlcnRpZXMiOnsiYXBpVmVyc2lvbiI6eyJ0eXBlIjoic3RyaW5nIn0sImZpZWxkUGF0aCI6eyJ0eXBlIjoic3RyaW5nIn0sImtpbmQiOnsidHlwZSI6InN0cmluZyJ9LCJuYW1lIjp7InR5cGUiOiJzdHJpbmcifSwibmFtZXNwYWNlIjp7InR5cGUiOiJzdHJpbmcifSwicmVzb3VyY2VWZXJzaW9uIjp7InR5cGUiOiJzdHJpbmcifSwidWlkIjp7InR5cGUiOiJzdHJpbmcifX0sInR5cGUiOiJvYmplY3QifSwicGVyaW9kIjp7InR5cGUiOiJzdHJpbmcifX0sInJlcXVpcmVkIjpbInBlcmlvZCIsIm1heFZhbHVlIiwibWV0cmljUmVmIl0sInR5cGUiOiJvYmplY3QifSwic3RhdHVzIjp7InR5cGUiOiJvYmplY3QifX19fSwidmVyc2lvbiI6InYxYWxwaGExIiwidmVyc2lvbnMiOlt7Im5hbWUiOiJ2MWFscGhhMSIsInNlcnZlZCI6dHJ1ZSwic3RvcmFnZSI6dHJ1ZX1dfX0="
}
},
{
"type": "olm.bundle.object",
"value": {
"data": "eyJhcGlWZXJzaW9uIjoiYXBpZXh0ZW5zaW9ucy5rOHMuaW8vdjFiZXRhMSIsImtpbmQiOiJDdXN0b21SZXNvdXJjZURlZmluaXRpb24iLCJtZXRhZGF0YSI6eyJuYW1lIjoibWFwcGluZ3J1bGVzLmNhcGFiaWxpdGllcy4zc2NhbGUubmV0In0sInNwZWMiOnsiZ3JvdXAiOiJjYXBhYmlsaXRpZXMuM3NjYWxlLm5ldCIsIm5hbWVzIjp7ImtpbmQiOiJNYXBwaW5nUnVsZSIsImxpc3RLaW5kIjoiTWFwcGluZ1J1bGVMaXN0IiwicGx1cmFsIjoibWFwcGluZ3J1bGVzIiwic2luZ3VsYXIiOiJtYXBwaW5ncnVsZSJ9LCJzY29wZSI6Ik5hbWVzcGFjZWQiLCJzdWJyZXNvdXJjZXMiOnsic3RhdHVzIjp7fX0sInZhbGlkYXRpb24iOnsib3BlbkFQSVYzU2NoZW1hIjp7InByb3BlcnRpZXMiOnsic3BlYyI6eyJwcm9wZXJ0aWVzIjp7ImluY3JlbWVudCI6eyJmb3JtYXQiOiJpbnQ2NCIsInR5cGUiOiJpbnRlZ2VyIn0sIm1ldGhvZCI6eyJ0eXBlIjoic3RyaW5nIn0sIm1ldHJpY1JlZiI6eyJwcm9wZXJ0aWVzIjp7ImFwaVZlcnNpb24iOnsidHlwZSI6InN0cmluZyJ9LCJmaWVsZFBhdGgiOnsidHlwZSI6InN0cmluZyJ9LCJraW5kIjp7InR5cGUiOiJzdHJpbmcifSwibmFtZSI6eyJ0eXBlIjoic3RyaW5nIn0sIm5hbWVzcGFjZSI6eyJ0eXBlIjoic3RyaW5nIn0sInJlc291cmNlVmVyc2lvbiI6eyJ0eXBlIjoic3RyaW5nIn0sInVpZCI6eyJ0eXBlIjoic3RyaW5nIn19LCJ0eXBlIjoib2JqZWN0In0sInBhdGgiOnsidHlwZSI6InN0cmluZyJ9fSwicmVxdWlyZWQiOlsicGF0aCIsIm1ldGhvZCIsImluY3JlbWVudCIsIm1ldHJpY1JlZiJdLCJ0eXBlIjoib2JqZWN0In0sInN0YXR1cyI6eyJ0eXBlIjoib2JqZWN0In19fX0sInZlcnNpb24iOiJ2MWFscGhhMSIsInZlcnNpb25zIjpbeyJuYW1lIjoidjFhbHBoYTEiLCJzZXJ2ZWQiOnRydWUsInN0b3JhZ2UiOnRydWV9XX19"
}
}
],
"relatedImages": [
{
"name": "",
"image": "registry.redhat.io/awesome/old@sha256:55166d313d270ae4740a260149bfb41df1af8214744e9c38abef07add49c95de"
}
{
"name": "awesome-operator",
"image": "registry.redhat.io/awesome/operator-api@sha256:bb7dc249eb517b6e3ffd7d4ceb19884ad4af97f16835bf58e1930534ca7e4764"
}
]
}
The output will look like:
{
"schema": "olm.bundle",
"name": "my-awesome-operator",
"package": "awesome-package",
"image": "registry.redhat.io/awesome/operator@sha256:55166d313d270ae4740a260149bfb41df1af8214744e9c38abef07add49c95de",
"properties": [
{
"type": "olm.gvk",
"value": {
"group": "api.awesome.org",
"kind": "APIManager",
"version": "v1alpha1"
}
},
{
"type": "my.awesome.property",
"value": "bar"
},
{
"type": "olm.bundle.object",
"value": {
"data": "eyJhcGlWZXJzaW9uIjoiYXBpZXh0ZW5zaW9ucy5rOHMuaW8vdjFiZXRhMSIsImtpbmQiOiJDdXN0b21SZXNvdXJjZURlZmluaXRpb24iLCJtZXRhZGF0YSI6eyJuYW1lIjoibGltaXRzLmNhcGFiaWxpdGllcy4zc2NhbGUubmV0In0sInNwZWMiOnsiZ3JvdXAiOiJjYXBhYmlsaXRpZXMuM3NjYWxlLm5ldCIsIm5hbWVzIjp7ImtpbmQiOiJMaW1pdCIsImxpc3RLaW5kIjoiTGltaXRMaXN0IiwicGx1cmFsIjoibGltaXRzIiwic2luZ3VsYXIiOiJsaW1pdCJ9LCJzY29wZSI6Ik5hbWVzcGFjZWQiLCJzdWJyZXNvdXJjZXMiOnsic3RhdHVzIjp7fX0sInZhbGlkYXRpb24iOnsib3BlbkFQSVYzU2NoZW1hIjp7InByb3BlcnRpZXMiOnsic3BlYyI6eyJwcm9wZXJ0aWVzIjp7Im1heFZhbHVlIjp7ImZvcm1hdCI6ImludDY0IiwidHlwZSI6ImludGVnZXIifSwibWV0cmljUmVmIjp7InByb3BlcnRpZXMiOnsiYXBpVmVyc2lvbiI6eyJ0eXBlIjoic3RyaW5nIn0sImZpZWxkUGF0aCI6eyJ0eXBlIjoic3RyaW5nIn0sImtpbmQiOnsidHlwZSI6InN0cmluZyJ9LCJuYW1lIjp7InR5cGUiOiJzdHJpbmcifSwibmFtZXNwYWNlIjp7InR5cGUiOiJzdHJpbmcifSwicmVzb3VyY2VWZXJzaW9uIjp7InR5cGUiOiJzdHJpbmcifSwidWlkIjp7InR5cGUiOiJzdHJpbmcifX0sInR5cGUiOiJvYmplY3QifSwicGVyaW9kIjp7InR5cGUiOiJzdHJpbmcifX0sInJlcXVpcmVkIjpbInBlcmlvZCIsIm1heFZhbHVlIiwibWV0cmljUmVmIl0sInR5cGUiOiJvYmplY3QifSwic3RhdHVzIjp7InR5cGUiOiJvYmplY3QifX19fSwidmVyc2lvbiI6InYxYWxwaGExIiwidmVyc2lvbnMiOlt7Im5hbWUiOiJ2MWFscGhhMSIsInNlcnZlZCI6dHJ1ZSwic3RvcmFnZSI6dHJ1ZX1dfX0="
}
},
{
"type": "olm.bundle.object",
"value": {
"data": "eyJhcGlWZXJzaW9uIjoiYXBpZXh0ZW5zaW9ucy5rOHMuaW8vdjFiZXRhMSIsImtpbmQiOiJDdXN0b21SZXNvdXJjZURlZmluaXRpb24iLCJtZXRhZGF0YSI6eyJuYW1lIjoibWFwcGluZ3J1bGVzLmNhcGFiaWxpdGllcy4zc2NhbGUubmV0In0sInNwZWMiOnsiZ3JvdXAiOiJjYXBhYmlsaXRpZXMuM3NjYWxlLm5ldCIsIm5hbWVzIjp7ImtpbmQiOiJNYXBwaW5nUnVsZSIsImxpc3RLaW5kIjoiTWFwcGluZ1J1bGVMaXN0IiwicGx1cmFsIjoibWFwcGluZ3J1bGVzIiwic2luZ3VsYXIiOiJtYXBwaW5ncnVsZSJ9LCJzY29wZSI6Ik5hbWVzcGFjZWQiLCJzdWJyZXNvdXJjZXMiOnsic3RhdHVzIjp7fX0sInZhbGlkYXRpb24iOnsib3BlbkFQSVYzU2NoZW1hIjp7InByb3BlcnRpZXMiOnsic3BlYyI6eyJwcm9wZXJ0aWVzIjp7ImluY3JlbWVudCI6eyJmb3JtYXQiOiJpbnQ2NCIsInR5cGUiOiJpbnRlZ2VyIn0sIm1ldGhvZCI6eyJ0eXBlIjoic3RyaW5nIn0sIm1ldHJpY1JlZiI6eyJwcm9wZXJ0aWVzIjp7ImFwaVZlcnNpb24iOnsidHlwZSI6InN0cmluZyJ9LCJmaWVsZFBhdGgiOnsidHlwZSI6InN0cmluZyJ9LCJraW5kIjp7InR5cGUiOiJzdHJpbmcifSwibmFtZSI6eyJ0eXBlIjoic3RyaW5nIn0sIm5hbWVzcGFjZSI6eyJ0eXBlIjoic3RyaW5nIn0sInJlc291cmNlVmVyc2lvbiI6eyJ0eXBlIjoic3RyaW5nIn0sInVpZCI6eyJ0eXBlIjoic3RyaW5nIn19LCJ0eXBlIjoib2JqZWN0In0sInBhdGgiOnsidHlwZSI6InN0cmluZyJ9fSwicmVxdWlyZWQiOlsicGF0aCIsIm1ldGhvZCIsImluY3JlbWVudCIsIm1ldHJpY1JlZiJdLCJ0eXBlIjoib2JqZWN0In0sInN0YXR1cyI6eyJ0eXBlIjoib2JqZWN0In19fX0sInZlcnNpb24iOiJ2MWFscGhhMSIsInZlcnNpb25zIjpbeyJuYW1lIjoidjFhbHBoYTEiLCJzZXJ2ZWQiOnRydWUsInN0b3JhZ2UiOnRydWV9XX19"
}
},
{
"type": "olm.maxOpenShiftVersion",
"value": "4.8"
},
{
"type": "my.awesome.property",
"value": "foo"
}
],
"relatedImages": [
{
"name": "",
"image": "registry.redhat.io/awesome/new@sha256:b480e64de68bbe7c271623cf9a9f88067fa7d1dd4dc54f192ca505ecfb25dd17"
}
{
"name": "awesome-operator",
"image": "registry.redhat.io/awesome/operator-api@sha256:bb7dc249eb517b6e3ffd7d4ceb19884ad4af97f16835bf58e1930534ca7e4764"
}
]
}
A veneer must be able to accept another veneer's output for processing. This can be done be either having no strong restrictions on fields in the input catalog, or by specifying the expected fields on the input catalogs.
The file based catalog should not require that all bundles/packages use the same set of veneers. Each operator author should be free to have their set of veneers and their orders defined. Veneers are allowed to work on partial catalogs to generate required fields while leaving other parts untouched. Only the final catalog should be validated for correctness.
Given a veneer and a custom api to expand it, opm must be able to use in its veneer expansion operation. For this purpose, the opm veneer expansion must be able to use external scripts/binaries with specified set of arguments to expand veneers.
The final output of a set of veneers must be a usable catalog, performing the same as a low level file based catalog. To take the burden of additional support for veneer generated bundles, all veneer application should be done by the operator authors. The catalog authors and cluster administrators should only ever have to use the fully generated low level file based catalogs.
Operations like adding/removing a specific edge or node from an upgrade graph regardless of the position of the addition/removal must be easy for the operator author. This includes the ability to change channel membership.
To ensure veneers work with minor changes to the file based catalog schema, they are required to allow for unknown fields within an object's schema.
While the veneer api allows easier maintenance of a file based catalog, many of its features can be obtained by simply rearranging objects in a file based catalog based on the package and object. This reduces the effort of parsing the entire catalog to make modifications.
└── awesome-operator
├── .indexignore
├── bundle.v1.0.0.yaml
├── bundle.v1.0.1.yaml
├── bundle-v1.0.2.yaml
├── bundle-v2.0.0.yaml
├── channel.alpha.yaml
├── channel.beta.yaml
└── package.yaml
Ordering the package as above makes it easy to determine the portion of the catalog to be modified to add specific changes.
The addition of the olm.channel
schema also makes it easier to manipulate a channel's upgrade edges compared to the prior channel membership model where it was required to declare channel membership on the bundle object. With this simplified schema, users have an easier time manipulating a package's upgrade paths.
The olm.channel
object has the following schema: