github.com/operator-framework/operator-lifecycle-manager@v0.30.0/doc/design/dependency-resolution.md (about)

     1  # Dependency Resolution and Upgrades
     2  
     3  OLM manages the dependency resolution and upgrade lifecycle of running operators. In many ways, these problems OLM faces are similar to other OS package managers like `apt`/`dkpg` and `yum`/`rpm`.
     4  
     5  However, there is one constraint that similar systems don't generally have that OLM does: because operators are always running, OLM attempts to ensure that at no point in time are you left with a set of operators that do not work with each other.
     6  
     7  This means that OLM needs to never:
     8  
     9   - install a set of operators that require APIs that can't be provided
    10   - update an operator in a way that breaks another that depends upon it
    11  
    12  The following examples motivate why OLM's dependency resolution and upgrade strategy works as it does, followed by a description of the current algorithm.
    13  
    14  ## CustomResourceDefinition (CRD) Upgrade
    15  
    16  OLM will upgrade CRD right away if it is owned by singular CSV. If CRD is owned by multiple CSVs, then CRD is upgraded when it is
    17  satisfied all of the following backward compatible conditions:
    18  
    19  - All existing serving versions in current CRD are present in new CRD
    20  - All existing instances (Custom Resource) that are associated with serving versions of CRD are valid when validated against new CRD's validation schema
    21  
    22  ### Add a new version to CRD
    23  
    24  The recommended procedure to add a new version in CRD:
    25  
    26  1. For example, the current CRD has one version `v1alpha1` and you want to add a new version `v1beta1` and mark it as the new storage version:
    27  
    28  ```
    29  versions:
    30    - name: v1alpha1
    31      served: true
    32      storage: false
    33    - name: v1beta1
    34      served: true
    35      storage: true
    36  ```
    37  
    38  Note: In `apiextensions.k8s.io/v1beta1`, there was a version field instead of versions. The version field is deprecated and optional, but if it is not empty, it must match the first item in the versions field.
    39  
    40  ```
    41  version: v1beta1
    42  versions:
    43    - name: v1beta1
    44      served: true
    45      storage: true
    46    - name: v1alpha1
    47      served: true
    48      storage: false
    49  ```
    50  
    51  2. Ensure the referencing version of CRD in CSV is updated if CSV intends to use the new version in `owned` section:
    52  
    53  ```
    54  customresourcedefinitions:
    55    owned:
    56    - name: cluster.example.com
    57      version: v1beta1
    58      kind: cluster
    59      displayName: Cluster
    60  ```
    61  
    62  3. Push the updated CRD and CSV to your bundle
    63  
    64  ### Deprecate/Remove a version of CRD
    65  
    66  OLM will not allow a serving version of CRD to be removed right away. Instead, a deprecated version of CRD should have been disabled first by marking `Served` field in CRD to `false` first. Then, the non-serving version can be removed on the subsequent CRD upgrade.
    67  
    68  The recommended procedure to deprecate and remove a specific version in CRD:
    69  
    70  1. Mark the deprecated version as non-serving to indicate this version is no longer in use and may be removed in subsequent upgrade. For example:
    71  
    72  ```
    73  versions:
    74    - name: v1alpha1
    75      served: false
    76      storage: true
    77  ```
    78  
    79  2. Switch storage version to a serving version if soon-to-deprecated version is currently the storage version.
    80  For example:
    81  
    82  ```
    83  versions:
    84    - name: v1alpha1
    85      served: false
    86      storage: false
    87    - name: v1beta1
    88      served: true
    89      storage: true
    90  ```
    91  
    92  3. Upgrade CRD with above changes.
    93  
    94  4. In subsequent upgrade cycles, the non-serving version can be removed completely from CRD. For example:
    95  
    96  ```
    97  versions:
    98    - name: v1beta1
    99      served: true
   100      storage: true
   101  ```
   102  
   103  Note:
   104  
   105  1. In order to remove a specific version that is or was storage version from CRD, that version needs to be removed from
   106  `storedVersion` in CRD's status. OLM will attempt to do this for you if it detects a stored version no longer exists in new CRD.
   107  
   108  2. Ensure referencing CRD's version in CSV is updated if that version is removed from the CRD.
   109  
   110  # Example: Deprecate dependant API
   111  
   112  A and B are APIs (e.g. CRDs)
   113  
   114  * A's provider depends on B
   115  * B’s provider has a Subscription
   116  * B’s provider updates to provide C but deprecates B
   117  
   118  This results in:
   119  
   120  * B no longer has a provider
   121  * A no longer works
   122  
   123  This is a case we prevent with OLM's upgrade strategy.
   124  
   125  
   126  # Example: Version deadlock
   127  
   128  A and B are APIs
   129  
   130  * A's provider requires B
   131  * B's provider requires A
   132  * A's provider updates to (provide A2, require B2) and deprecate A
   133  * B's provider updates to (provide B2, require A2) and deprecate B
   134  
   135  If we attempt to update A without simultaneously updating B, or vice-versa, we won't be able to progress to new versions of the operators, even though a new compatible set can be found.
   136  
   137  This is another case we prevent with OLM's upgrade strategy.
   138  
   139  
   140  # Dependency resolution
   141  
   142  A Provider is an operator which "Owns" a CRD or APIService.
   143  
   144  This algorithm will result in a successful update of a generation (in which as many operators which can be updated have been):
   145  
   146  ```
   147  Consider the set of operators defined by running operators in a namespace:
   148  
   149    For each subscription in the namespace:
   150       if the subscription hasn't been checked before, find the latest CSV in the source/package/channel
   151          provisionally add the operator to the generation
   152       else
   153          check for a replacement in the source/package/channel
   154  
   155    // Generation resolution
   156    For each required API with no provider in gen:
   157      search through prioritized sources to pick a provider
   158      provisionally add any new operators found to the generation, this could also add to the required APIs without providers
   159  
   160    // Downgrade
   161    if there are still required APIs that can't be satisfied by sources:
   162      downgrade the operator(s) that require the APIs that can't be satisfied
   163  
   164    // Apply
   165    for each new operator required, install it into the cluster. Any newly resolved operator will be given a subscription to the channel/package/source it was discovered in.
   166  ```
   167  
   168  The operator expansion loop is bounded by the total number of provided apis across sources (because a generation may not have multiple providers)
   169  
   170  The downgrade loop will eventually stop, though it may contract back down to the original generation in the namespace. Downgrading an operator means it was in the previous generation. By definition, either its required apis are satisfied, or will be satisfied by the downgrade of another operator.