Quick Reference for "CTT" (OCM Replication Tool)
================================================

The `replicate` command which is exposed from `gardener-ocm` python distribution package, and
`ocm`-OCI-Image is used to replicate OCM-Component-Versions between OCM-Repositories backed by
OCI-Registries. Replication includes OCI-Artefacts declared as OCM-Resources, as well as
blobs inlined into OCM-Component-Descriptor-Artefacts. Other artefacts are replicated by reference.

Name
----

"CTT" (fka: CNUDIE Transport Tool) as named back when OCM was named "CNUDIE". In order to avoid
unnecesary confusion, and to keep in sync w/ package-names, the name "CTT" was kept. Despite the
name, it is compatible to OCM, and would rather be called "OCM Transport Tool", or the like, if
it were named after renaming of CNUDIE -> OCM happened.

Usage
-----

It is assumed `docker` is properly installed and configured.

Use `[docker run] europe-docker.pkg.dev/gardener-project/releases/cicd/ocm:latest` to run commands
mentioned in this guide. You might opt for a versioned tag if version-pinning is desired.

Using `CTT` to replicate OCM-Components is a two-step process. Firstly, a "processing-configuration"
(processing.cfg) needs to be created. Through processing-cfg, replication behaviour can be
controlled. If customisations are configured, processing-cfg should be persisted between
replication-runs.

A simple configuration can be generated using:

```
docker run \
  europe-docker.pkg.dev/gardener-project/releases/cicd/ocm:latest \
  simple-config \
    --target $target_registry [\]
    [--source $source_registry]
```

Where `$target_registry` is the OCI-Registry into which CTT should replicate. The optional
`$source_registry` can be used to instruct CTT to strip `$source_registry` prefix from target
OCI-Image-References, which can be beneficial if target-registry imposes length-restrictions.

Typically, output of this command should be redirected into a file (called `processing.cfg`
in this guide subsequently).

Secondly, the actual replication can be triggered using the `replicate` command. As a pre-requisite,
it is necessary to create a "docker-cfg" (as written by docker to $HOME/.docker/config.json
by default) with read-access to any OCI-Registries _from_ where OCI-Artefacts need to be read
(this will at least be `$source_registry`), and write-access towards `$target_registry`.

This can be achieved by running `docker login <hostname>` for each of the involved OCI-Registries.

Assuming `processing.cfg` resides in $PWD, use the following command to run replication:

```
docker run \
  -v$HOME/.docker/config.json:/docker-cfg.json \
  -v$PWD/processing.cfg:/processing.cfg \
  europe-docker.pkg.dev/gardener-project/releases/cicd/ocm:latest \
  replicate \
    --docker-config /docker-cfg.json \
    --processing-cfg /processing.cfg \
    --src-repo $src_registry \
    --ocm-component $ocm_component
```

Where $ocm_component is the OCM-Component-Version that should be replicated.

Syntax: `<ocm-component-name>:<version>`, e.g. `acme.org/my-component:1.2.3`.

CTT's `replicate`-command will recursively replicate any referenced sub-components. It will by
default parallelise replication using multithreading, and will early-exit replication of
OCI-Artefacts, if already present in replication-target. It is designed to be safe (and
resource-efficient) to be rerun against the same replication-target multiple times.

Uploaders
---------

Uploaders control how OCI-Artefacts are pushed to the target registry and how their references are
recorded in the replicated component-descriptors.  Multiple uploaders may be chained in the
`upload` list of an `image_processing_cfg` entry; they are applied in order, each receiving the
result of the previous one.

### PrependTargetUploader

The most common uploader.  It derives the target image-reference by prepending the target
OCI-Registry to the (mangled) source image-reference path.

Example: replicate everything from `registry.example.com` into `registry.acme.org`:

```yaml
targets:
  acme:
    type: RegistriesTarget
    kwargs:
      registries:
        - registry.acme.org
      ocm_repository: registry.acme.org

processors:
  no-op:
    type: NoOpProcessor

uploaders:
  prepend_target:
    type: PrependTargetUploader
    kwargs:
      remove_prefixes:
        - registry.example.com/

image_processing_cfg:
  - name: default
    filter:
      - type: MatchAllFilter
    processor: no-op
    target: acme
    upload:
      - prepend_target
```

`remove_prefixes` strips the given prefix before prepending the target registry, so
`registry.example.com/myorg/myimage:1.0` is pushed to (and recorded as)
`registry.acme.org/myorg/myimage:1.0` rather than
`registry.acme.org/registry_example_com/myorg/myimage:1.0`.

### DescriptorRefRewriteUploader

Use this when the registry the artefacts are _pushed to_ differs from the registry _clients should
pull from_.  A typical case is a primary regional registry that feeds into a global CDN or
geo-routing endpoint: artefacts are uploaded once to the primary region, but all consumers should
reference the global endpoint so they are served from the closest replica automatically.

`DescriptorRefRewriteUploader` replaces a prefix in the computed target reference and stores the
result as the `imageReference` written into the component-descriptor, without affecting the actual
push destination.  It must be placed _after_ the uploader that sets the push target (e.g.
`PrependTargetUploader`).

Example: push to `registry-primary.example.com` but record `registry-global.example.com`
references in the component-descriptor:

```yaml
targets:
  primary:
    type: RegistriesTarget
    kwargs:
      registries:
        - registry-primary.example.com
      ocm_repository: registry-primary.example.com

processors:
  no-op:
    type: NoOpProcessor

uploaders:
  push_to_primary:
    type: PrependTargetUploader
    kwargs: {}
  rewrite_for_global:
    type: DescriptorRefRewriteUploader
    kwargs:
      src_prefix: registry-primary.example.com/
      tgt_prefix: registry-global.example.com/

image_processing_cfg:
  - name: default
    filter:
      - type: MatchAllFilter
    processor: no-op
    target: primary
    upload:
      - push_to_primary
      - rewrite_for_global
```

With this configuration, an image `src.example.com/myorg/myimage:1.0` is pushed to
`registry-primary.example.com/src_example_com/myorg/myimage:1.0`, while the component-descriptor
records `registry-global.example.com/src_example_com/myorg/myimage:1.0`.
