---
tags: fbc
---
# Deprecating channels
This document investigates methods of including channel deprecation metadata in file-based catalogs (FBCs) and the potential side effects of the different approaches. You can track the progress of this investigation on the OLM Jira board: https://issues.redhat.com/browse/OLM-2707. This is meant only for FBC based indices, not for SQLite.
## Background
### Why support Channel Deprecation?
The catalogs used by OLM, whether FBC or SQLite based, have a set of operator packages, each with one or more upgrade paths defined between the operator bundle versions it references. A user on-cluster can create a `Subscription` to a specific channel on a package to automatically upgrade their operator installation for any new version released on that channel.
OLM does not impose channel structure or naming conventions within a package. While this allows operator authors to have unlimited flexibility, the resulting variability between channel structuring in packages makes maintaining their FBC almost entirely manual. Veneers simplify FBC maintenance, but more complex veneers like the semver veneer require the adoption of a standard naming scheme within channels to be useful.
The lack of standardisation also provides for a poor user experience. Currently for EUS to EUS upgrade, users have to move to a separate channel to get upgrades. The adoption of the semver veneer would also address this, but requires a way for operator authors to migrate their old channel structure to one that conforms with a standard naming scheme. Since FBC has no concept of channel aliases, this requires removal of old channels once the new ones are in place.
Removing channels without warning however, can leave a cluster admin with one operator on a broken channel - breaking resolution and the console interface for channel selection. This would then require inspecting all subscriptions in the broken namespace and updating the faulty one manually. This can partly be addressed by having subscriptions with unkown channels fail independenty of resolution, OLM would also not provide any upgrades for the bad subscription.
Channel deprecation is a means to warn users about the future removal of a channel, giving ample time for cluster admins to move off a deprecated channel. Unlike bundle deprecation which was introduced to block installation while still enabling upgrade from deprecated versions, channel deprecation should not stop users from using the deprecated channel: It is meant as a warning. For this warning to be meaningful, both users trying to install from a deprecated channel and those already to a channel that becomes deprecated must be informed. A deprecated channel should also be able to provide additional information, like alternative channels a user could switch to.
### Channel removal today
Removing a channel requires enormous effort in SQLite catalogs as channel information is carried within the bundles themselves: each bundle would have to first be rebuilt to exclude the channel, the index to modify pruned of the package with the removed bundle and then add back all the rebuilt bundles to the catalog. In contrast, this can be done in FBC by removing a single channel object from the catalog's json or yaml. Deprecation is hence discussed in terms of FBC only.
If a channel with an existing subscription is removed from the catalog, this will immediately be noticable in the form of resolution errors like below:
```
'constraints not satisfiable: no operators found in channel deprecated-channel of package example-op in the catalog referenced by subscription example-sub, subscription example-sub exists'
```
This does not affect the function of already installed operators. Since resolution is namespace scoped, all operators in that namespace will not resolve till the subscription is patched or deleted. If an alternate channel is known, the subscription can be switched over to the other channel to resolve the issue.
### About Channels
A channel is an ordered upgrade path through bundle versions that an installed bundle can follow. The same bundle can be present on different channels, and for File Based Catalogs, bundles can have different upgrade edges based on different channels.
In SQLite indexes, channels are stored as sets of (`package name`, `channel name`, `bundle name`, [`replaces`]). This representation does not allow any channel-level properties. The only additional information stored about channels is the channel head and default channel name.
With the introduction of FBC, channels became separate objects, capable of having properties:
```yaml
schema: olm.channel
package: <package name>
name: <channel name>
entries:
- name: <bundle name>
replaces: <replaces bundle name>
skips: [<skipped bundle list>]
skipRange: <skipRange semver>
- ...
properties:
- type: <type string>
value: <value json>
- ...
```
To keep backward compatibility to SQLite indices, OLM and related components don't use channel-level metadata. The way these components interact with channels is shown below:
```mermaid
graph LR
subgraph discovery
package-server --> console
package-server --> k[oc/kubectl]
end
Index -->|bundle list| package-server
console --> Subscription --> Resolver
Index -->|bundle list| Resolver
subgraph resolution
csvs[Installed CSVs] --> Resolver --> ResolveSet
end
```
#### Channel discovery
If a user knows the exact name of the package they want to install, they can create a subscription to install the version of that operator at the default channel head. Installing any other CSV version or subscribing to any other channel requires knowing the exact name of that CSV or channel.
```yaml
apiVersion: operators.coreos.com/v1alpha1
kind: Subscription
metadata:
name: <subscription name>
namespace: <subscription namespace>
spec:
source: <catalogSource to install from>
sourceNamespace: <catalogSource namespace>
name: <package name>
startingCSV: <specific bundle name to install> #if unspecified, installs channel head
channel: <channel for upgrades> #if unspecified, uses default channel
...
status: ...
```
GRPC calls to an index can allow a user to figure out all the packages provided by an index along with what channels each package supports and what their head versions are: This is the role of the OLM `package-server`. It stores this in the form of `PackageManifest` resources on the cluster, which it updates whenever there is a change in a `catalogSource`
```yaml
apiVersion: packages.operators.coreos.com/v1
kind: PackageManifest
metadata:
labels:
<catalogSource, os/arch, CSV labels>
name: <package name>
namespace: <namespace>
spec: {}
status:
packageName: <package name>
defaultChannel: <default channel name>
channels:
- currentCSV: <CSV name>
currentCSVDesc:
annotations: [<annotations list from CSV>]
apiservicedefinitions: {...}
customresourcedefinitions: {...}
displayName: <displayNames>
installModes: [<supported installModes>]
keywords: [<search keywords>]
links: [<external links>]
maintainers: [<maintainers list>]
maturity: <maturity level>
provider:
name: <provider name>
relatedImages: [<relatedImages>]
version: <version>
name: <channel name>
- ...
```
A PackageManifest can provide an overview of all channels along with key fields in their head CSVs. The PackageManifest cache is constructed using a set of GRPC calls to the registry, as detailed below:
```mermaid
graph TD
A(( )) --- ListPackages --> PackageName1
ListPackages --> PackageName2
ListPackages --> B[...]
PackageName1 --- GetPackage
GetPackage --> PackageName
subgraph HeadCSV
CSVName
CSVJSON
end
subgraph package
PackageName
DefaultChannel
subgraph channel
ChannelName1
ChannelName2
C[...]
end
end
ChannelName1 --> D["GetBundleForChannel (deprecated)"] --> CSVName
subgraph PackageManifest
pname[packageName]
pdefault[defaultChannel]
subgraph pHead[channels]
pChan[name]
pCSV[currentCSV]
pJSON[currentCSVDesc]
end
end
PackageName --> pname
DefaultChannel --> pdefault
ChannelName1 --> pChan
CSVName --> pCSV
CSVJSON --> pJSON
classDef grpc fill:#fff,stroke-width:0px;
class ListPackages,GetPackage,D grpc
```
In addition to being accessible via the oc/kubectl, the OpenShift Console also uses PackageManifests for listing the valid channels for installation.
<!--Console exclusively uses package manifests
https://github.com/openshift/console/blob/2ad4e17d76acbe72171407fc1c66ca4596c8aac4/frontend/packages/operator-lifecycle-manager/src/components/index.tsx -->
#### Channels in resolution
Resolution uses a different intermediary store, with a resolver cache of simplified Entries. This cache is generated with the `ListBundles` GRPC call which generates a list of `Bundle` objects which are then transformed into `Entry` Objects.
```go
type Bundle struct {
CsvName string
PackageName string
ChannelName string
CsvJson string
Object []string
BundlePath string
ProvidedApis []*GroupVersionKind
RequiredApis []*GroupVersionKind
Version string
SkipRange string
Dependencies []*Dependency
Properties []*Property
Replaces string
Skips []string
}
```
```go
type Entry struct {
Name string
Replaces string
Skips []string
SkipRange semver.Range
ProvidedAPIs APISet
RequiredAPIs APISet
Version *semver.Version
SourceInfo struct {
Package string
Channel string
StartingCSV string
Catalog SourceKey
DefaultChannel bool
Subscription *v1alpha1.Subscription
}
Properties []*api.Property //Bundle Properties + legacy dependencies + provided/required APIs
BundlePath string
Bundle *api.Bundle
}
```
The resolver uses these Entries along with Subscriptions to resolve what the set of installed operators should be. The `ListBundles` call used in resolution behaves identically for FBC and SQLite indices.
#### Channel Deprecation for new operators
##### Required changes in FBC:
##### 1. channel-level properties
One way to mark a channel as deprecated would be at the channel level, as a channel level property on FBC.
```yaml
schema: olm.channel
package: my-pkg
name: deprecated-channel
entries:
- name: bundle-1
replaces: bundle-2
- name: bundle-2
properties:
- type: olm.deprecated.channel
value: '{"name":"deprecated-channel","message": "custom optional warning to log", "fallback":["channel-1","channel-2"]}'
```
A new GRPC endpoint, like a `GetChannelsForPackage` or `ListPackageChannels` could then be used to provide channel-level metadata in a form similar to the `struct` described below.
```go
type PackageChannel struct {
Package string
Properties []*Property // package-level properties
Channels []*struct{
{
Channel string
Properties []*Property // channel-level properties
ChannelHead string
ChannelHeadJSON string
...
}
}
}
```
To avoid adding another GRPC call, the `Bundle` object returned by both `ListBundles` and `GetBundleForChannel` GRPC calls can be modified to support channel properties, either in a new field or by merging them with bundle properties
```go
type Bundle struct {
CsvName string
PackageName string
ChannelName string
CsvJson string
Object []string
BundlePath string
ProvidedApis []*GroupVersionKind
RequiredApis []*GroupVersionKind
Version string
SkipRange string
Dependencies []*Dependency
Properties []*Property // Bundle+channel properties
Replaces string
Skips []string
}
```
The channel-level properties could also be present on bundles from the start, but a single bundle can belong to a mix of deprecated and non-deprecated channels. Adding channel deprecation to every bundle of a deprecated channel will increase the index size while also being ambiguous unless the channel name being looked at is specified elsewhere, either from another GRPC call or from the subscription. It is also possible to have the channel deprecation property present only on the head of a deprecated channel and achieve the same result without as much of an increase in index size.
##### Changes required for Console
The deprecation information provided by the FBC will need to be understood by console. This can then be used to either hide deprecated channels from new users or to warn them about selecting deprecated channels.
###### Modified package-server API
Once the deprecation property is on the FBC, it has to be made available to the console. Currently, the interface between the two is via the package-server, through the `PackageManifest` API it maintains.
One way to do this with no modifications to the current API whatsoever is by adding an annotation for tracking deprecated channels:
```yaml
annotations:
"olm.deprecated.channel/deprecated-channel-name": '{"message": "Channel x will be deprecated and removed on dd-mm-yyyy, see fallback options for other channels you can switch to. See our announcement at operator-site.org/blog/please-dont-make-us-support-tegacy-code", "fallback":["channel-1","channel-2"]}'
```
This comes with a 253-character kube annotation length limit, which is incidentally broken by the above example.
The `PackageManifest` API can also have a new field to reflect the deprecation.
```yaml
apiVersion: packages.operators.coreos.com/v1alpha1
kind: PackageManifest
metadata:
labels:
<catalogSource, os/arch, CSV labels>
name: my-pkg
namespace: my-ns
spec: {}
status:
packageName: my-pkg
defaultChannel: channel-2
channels:
- currentCSV: bundle-1
currentCSVDesc: ...
deprecated:
message: ""
fallback:
- channel-1
- channel-2
name: deprecated-channel
- currentCSV: bundle-1
currentCSVDesc: ...
name: channel-1
```
Alternatively, the entire PackageManifest API can be deprecated in favor of a new FBC-centric api. Such an API might look similar to the sample below.
```yaml
apiVersion: packages.operators.coreos.com/v2alpha1
kind: PackageManifest
metadata:
creationTimestamp: "YYYY-MM-DDTHH:MM:SSZ"
labels:
catalog: <catalogSource name>
catalog-namespace: <catalogSource namespace>
operatorframework.io/arch.amd64: supported
operatorframework.io/os.linux: supported
provider: <provider>
provider-url: ""
name: <package name>
namespace: <namespace>
spec: {}
status:
catalogSource:
name: <catalogSource name>
displayName: <catalogSource display name>
namespace: <catalogSource namespace>
publisher: <catalogSource publisher>
properties: []
package:
name: <package name>
defaultChannel: <default channel>
icon: <operator icon>
properties:
- provider: <name>,<url>
- maintainers: [<name, url>]
- description: <package description>
- certified: <bool, whether package is certified>
- categories: <search categories>
- keywords: <search keywords>
- capabilities: <operator capabilities>
channels:
- name: <channel name>
properties:
- deprecated: true
- <overrides for any package level properties, channel level properties>
currentCSV:
name: <currentCSV name>
version: <CSV version>
properties:
- createdAt: "YYYY-MM-DDTHH:MM:SSZ"
- installModes: <supported install modes>
- <overrides for any package/channel level properties, bundle level properties>
desc:
apiservicedefinitions:
owned:
- kind: <apiservice kind>
name: <apiservice name>
version: <apiservice version>
description: <apiservice description>
displayName: <apiservice display name>
required:
- kind: <apiservice kind>
name: <apiservice name>
version: <apiservice version>
description: <apiservice description>
displayName: <apiservice display name>
customresourcedefinitions:
owned:
- kind: <crd kind>
name: <crd name>
version: <crd version>
description: <crd description>
displayName: <crd display name>
required:
- kind: <crd kind>
name: <crd name>
version: <crd version>
description: <crd description>
displayName: <crd display name>
```
###### FBC Direct Interface
The modified API presented earlier is a stripped down version of FBC. A direct FBC interface is a simpler solution to this. An interface that allows reading only a specific set of packages can easily take the role of a `PackageManifest`.
A single operator's FBC might look like:
```yaml
---
defaultChannel: kiali-0.46.x
name: kiali-operator
schema: olm.package
icon:
base64data: PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDIyLjAuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA1MTIgNTk1IiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTk1OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzE5MkM0Nzt9Cgkuc3Qxe2ZpbGw6dXJsKCNTVkdJRF8xXyk7fQoJLnN0MntmaWxsOnVybCgjU1ZHSURfMl8pO30KCS5zdDN7ZmlsbDp1cmwoI1NWR0lEXzNfKTt9Cgkuc3Q0e2ZpbGw6dXJsKCNTVkdJRF80Xyk7fQoJLnN0NXtmaWxsOnVybCgjU1ZHSURfNV8pO30KCS5zdDZ7ZmlsbDp1cmwoI1NWR0lEXzZfKTt9Cjwvc3R5bGU+CjxnPgoJPHBvbHlnb24gY2xhc3M9InN0MCIgcG9pbnRzPSIyNTYsNSAxLDE1Mi4yIDEsNDQ2LjcgMjU2LDU5My45IDUxMSw0NDYuNyA1MTEsMTUyLjIgMjU2LDUgCSIvPgoJPGxpbmVhckdyYWRpZW50IGlkPSJTVkdJRF8xXyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSIxMDkuNDk5NiIgeTE9Ijg0Ljk2MjIiIHgyPSI0NDAuOTUxNyIgeTI9Ijc5My44MTAyIj4KCQk8c3RvcCAgb2Zmc2V0PSIwIiBzdHlsZT0ic3RvcC1jb2xvcjojRkZGRkZGIi8+CgkJPHN0b3AgIG9mZnNldD0iMSIgc3R5bGU9InN0b3AtY29sb3I6IzU0QkFEOCIvPgoJPC9saW5lYXJHcmFkaWVudD4KCTxwYXRoIGNsYXNzPSJzdDEiIGQ9Ik0yNTYsNTYxbDIyNi41LTEzMC44di0yNi4zYy0zNy42LTcuMy04NC45LTE0LjMtMTQzLjUtMTkuM2MtMTk5LjItMTcuMi0xNC44LTU2LjYsNjguOS0xMjcuMQoJCVMxMzAsMTY1LjcsMTMwLDE2NS43czE0Ny42LDMxLDEzMi44LDUwYy0xMC41LDEzLjUtMTM0LjMsNDMuNS0yMzMuMyw4OC4xdjEyNi41TDI1Niw1NjF6Ii8+CjwvZz4KPC9zdmc+Cg==
mediatype: image/svg+xml
---
entries:
- name: kiali-cluster-operator.v1.43.0
- name: kiali-cluster-operator.v1.44.1
replaces: kiali-cluster-operator.v1.43.0
properties:
type: olm.deprecated.channel
value: '{"fallback":["kiali-0.46.x"]}'
name: kiali-0.44.x
package: kiali-operator
schema: olm.channel
---
entries:
- name: kiali-cluster-operator.v1.43.0
- name: kiali-cluster-operator.v1.44.1
replaces: kiali-cluster-operator.v1.43.0
- name: kiali-cluster-operator.v1.45.0
replaces: kiali-cluster-operator.v1.44.1
- name: kiali-cluster-operator.v1.46.0
replaces: kiali-cluster-operator.v1.45.0
name: kiali-0.46.x
package: kiali-operator
schema: olm.channel
---
image: quay.io/operatorhubio/kiali:v1.43.0
name: kiali-cluster-operator.v1.43.0
package: kiali-operator
properties:
- type: olm.gvk
value:
group: kiali.io
kind: Kiali
version: v1alpha1
- type: olm.package
value:
packageName: kiali-operator
version: 1.43.0
relatedImages:
- image: quay.io/kiali/kiali-operator:v1.43.0
name: ""
- image: quay.io/operatorhubio/kiali:v1.43.0
name: ""
schema: olm.bundle
---
image: quay.io/ankitathomas/operator:failing-csv
name: kiali-cluster-operator.v1.44.1
package: kiali-operator
properties:
- type: olm.gvk
value:
group: kiali.io
kind: Kiali
version: v1alpha1
- type: olm.package
value:
packageName: kiali-operator
version: 1.44.1
relatedImages:
- image: quay.io/kiali/kiali-operator:v1.44.1
name: ""
- image: quay.io/operatorhubio/kiali:v1.44.1
name: ""
schema: olm.bundle
---
image: quay.io/operatorhubio/kiali:v1.45.0
name: kiali-cluster-operator.v1.45.0
package: kiali-operator
properties:
- type: olm.gvk
value:
group: kiali.io
kind: Kiali
version: v1alpha1
- type: olm.package
value:
packageName: kiali-operator
version: 1.45.0
relatedImages:
- image: quay.io/kiali/kiali-operator:v1.45.0
name: ""
- image: quay.io/operatorhubio/kiali:v1.45.0
name: ""
schema: olm.bundle
---
image: quay.io/operatorhubio/kiali:v1.46.0
name: kiali-cluster-operator.v1.46.0
package: kiali-operator
properties:
- type: olm.gvk
value:
group: kiali.io
kind: Kiali
version: v1alpha1
- type: olm.package
value:
packageName: kiali-operator
version: 1.46.0
- type: olm.bundle.object
value:
data: eyJhcGlWZXJzaW9uIjoiYXBpZXh0ZW5zaW9ucy5rOHMuaW8vdjEiLCJraW5kIjoiQ3VzdG9tUmVzb3VyY...
- type: olm.bundle.object
value:
data: eyJhcGlWZXJzaW9uIjoib3BlcmF0b3JzLmNvcmVvcy5jb20vdjFhbHBoYTEiLCJraW5kIjoiQ2x1c3Rlc...
relatedImages:
- image: quay.io/kiali/kiali-operator:v1.46.0
name: ""
- image: quay.io/operatorhubio/kiali:v1.46.0
name: ""
schema: olm.bundle
```
#### Channel Deprecation for installed operators
Once channel deprecation information is available, olm must also surface a warning to already installed bundles. The easiest place to do so would be on the subscription itself. It could also be emitted as an event or just logged in the olm-operator while reconciling subscriptions.
```mermaid
graph LR
subgraph subscription
channel
status
end
OLM -->|GetBundleForChannel| registry -->|deprecated channel list| OLM
channel --> OLM -->|deprecation condition| status
OLM -->|deprecation warning| event
```
### Other questions:
- How do we handle deprecated dependencies?
- One option is to have an `allow-deprecation` constraint on only the top-level bundles and to disregard deprecated channels for dependencies. An alternative is to also surface deprecated dependencies in subscriptions, but since resolution is per namespace and not per subscription, this isn't viable. Events are a good way to show deprecated dependencies, but this again does not give information about what operator requires the deprecated dependency. Operator developers will need to be careful to track deprecation of their dependencies and diligent in informing their users when their own operator is deprecated
- What if an entire package is deprecated?
- This is especially important for dependencies: if all deprecated channels are blocked for dependencies, this may lead to installs failing where they don't have to. If all deprecated channels are allowed, operators may continue installing dependency versions that exist only on deprecated channels and could face issues later when those channels (and bundles within) are removed.
- What considerations do we need to make when deprecating a default channel?
- a default channel should be no different from any other channel and is mainly useful when a subscription does not specify a channel to upgrade from. This may lead to some confusion where a channel deprecation warning is present on a subscription that has no channel specified, but no major problems overall. The console may need to add special exemptions for the default channel if hiding deprecated channels since the default channel is typically where a user would install an operator from.
- Why not extend bundle deprecation to apply to a whole channel?
- Bundle deprecation allows users on existing deprecated bundles to upgrade from the bundle while preventing new users from installing a deprecated bundle. This can be extended to add a message about deprecation, but presently, OLM does not allow upgrading between deprecated bundles. Changing the bundle deprecation behavior may lead to unforeseen consequences.
- Is channel deprecation reversible?
- Unlike bundle deprecation in SQLite indices, channel deprecation does not remove information from the channel. Depending on how the channel deprecation is implemented, it may be as simple as removing a property from an FBC channel or status from a subscription. Changing FBC will likely still require a new index to be released, but that doesn't stop rolling back a deprecation notice.
See also: https://github.com/operator-framework/enhancements/pull/113/