# Configure Image Registry on GCP

This guide explains how the OpenShift image registry works on GCP hosted clusters, how to verify it is functioning, and how to configure or disable it.

## Overview

GCP hosted clusters use Workload Identity Federation (WIF) to grant the image registry operator access to a Google Cloud Storage (GCS) bucket for storing container images. No long-lived credentials are stored — the operator uses short-lived tokens issued by the hosted cluster's OIDC provider.

| Component | Name / Location | Purpose |
|-----------|-----------------|---------|
| GCP Service Account | `<infra-id>-image-registry@<project-id>.iam.gserviceaccount.com` | Identity that GCS bucket operations run as |
| WIF credential secret | `installer-cloud-credentials` in `openshift-image-registry` | Federated credential JSON consumed by the registry operator |
| Kubernetes service account | `cluster-image-registry-operator` in `openshift-image-registry` | Issues OIDC tokens exchanged for GCP access tokens |
| GCS bucket | Auto-created by the registry operator | Stores container image layers and manifests |

## Prerequisites

- A running GCP hosted cluster with `oc` access to the guest cluster
- The `image-registry` GCP service account created during IAM setup (see [Create GCP IAM Resources](create-gcp-iam.md))
- The `--image-registry-service-account` flag provided when the cluster was created (see [Create a GCP Hosted Cluster](create-gcp-hosted-cluster.md))

## Default Behavior

When a GCP hosted cluster starts up, the image registry is enabled automatically through a three-step flow:

1. **Credential propagation** — The HyperShift control plane reads the `image-registry` GSA email from the `HostedControlPlane` spec and generates a WIF credential JSON. This credential is written to the `installer-cloud-credentials` secret in the `openshift-image-registry` namespace on the guest cluster.

2. **Bucket creation** — The cluster image registry operator reads the WIF credentials and creates a GCS bucket. The bucket name is chosen automatically based on the cluster's infrastructure ID and region.

3. **Registry available** — Once the bucket exists and the credentials are valid, the registry operator reports `Available=True` and the internal registry endpoint becomes active at `image-registry.openshift-image-registry.svc:5000`.

No manual configuration is required when the cluster is created with the `--image-registry-service-account` flag.

## Verification

### Check ClusterOperator Status

Verify the image registry operator is available:

```bash
KUBECONFIG=hosted-kubeconfig oc get clusteroperator image-registry
```

Expected output:

```text
NAME             VERSION   AVAILABLE   PROGRESSING   DEGRADED   SINCE   MESSAGE
image-registry   4.18.0    True        False         False      5m
```

### Check Registry Configuration

Inspect the registry operator configuration to see the GCS bucket that was created:

```bash
KUBECONFIG=hosted-kubeconfig oc get configs.imageregistry.operator.openshift.io cluster -o jsonpath='{.spec.storage}'
```

Expected output shows the GCS bucket:

```json
{"gcs":{"bucket":"<auto-generated-bucket-name>","region":"<region>"}}
```

### Check WIF Credentials

Verify the WIF credential secret was propagated to the guest cluster:

```bash
KUBECONFIG=hosted-kubeconfig oc get secret installer-cloud-credentials \
  -n openshift-image-registry -o jsonpath='{.data.service_account\.json}' | base64 -d | python3 -m json.tool
```

The decoded JSON should contain `"type": "external_account"` and reference your WIF pool and provider IDs, confirming that short-lived federated tokens are used rather than a service account key.

## Advanced Configuration

### Custom Bucket Name

To use a specific GCS bucket name instead of the auto-generated one, patch the registry operator configuration after cluster creation:

```bash
KUBECONFIG=hosted-kubeconfig oc patch configs.imageregistry.operator.openshift.io cluster \
  --type=merge \
  --patch='{"spec":{"storage":{"gcs":{"bucket":"<your-bucket-name>","region":"<region>"}}}}'
```

!!! note "Bucket must exist"

    The bucket must already exist and the `image-registry` GSA must have `Storage Admin` permissions on it. The registry operator will not create a bucket when one is explicitly specified.

### Using a Pre-Existing Bucket

If your organization requires using a pre-existing GCS bucket (for example, to apply custom lifecycle policies or retention rules):

1. Create the bucket in the hosted cluster GCP project:

    ```bash
    gsutil mb -p <project-id> -l <region> gs://<your-bucket-name>
    ```

2. Grant the `image-registry` GSA `Storage Admin` access:

    ```bash
    gsutil iam ch \
      serviceAccount:<infra-id>-image-registry@<project-id>.iam.gserviceaccount.com:roles/storage.admin \
      gs://<your-bucket-name>
    ```

3. Configure the registry operator to use the bucket:

    ```bash
    KUBECONFIG=hosted-kubeconfig oc patch configs.imageregistry.operator.openshift.io cluster \
      --type=merge \
      --patch='{"spec":{"storage":{"gcs":{"bucket":"<your-bucket-name>","region":"<region>"}}}}'
    ```

### Disabling the Image Registry

The image registry can be disabled via the `ImageRegistry` capability on the `HostedCluster`. When disabled, the registry operator is not deployed and no GCS bucket is created.

To disable the image registry at cluster creation time, add `--capabilities-disabled=ImageRegistry` to the `hypershift create cluster gcp` command (refer to [Create a GCP Hosted Cluster](create-gcp-hosted-cluster.md) for the full command).

To disable the registry on a running cluster, patch the `HostedCluster` resource on the management cluster:

```bash
oc patch hostedcluster <cluster-name> -n <namespace> \
  --type=merge \
  --patch='{"spec":{"capabilities":{"disabled":["ImageRegistry"]}}}'
```

!!! note

    This merge patch replaces the entire `spec.capabilities.disabled` list. If your cluster already has other capabilities disabled, include them in the patch to avoid re-enabling them.

!!! warning "Data loss"

    Disabling the image registry does not delete the GCS bucket or its contents. However, any images stored in the registry will become inaccessible to the cluster while the registry is disabled.

## Troubleshooting

### Registry Operator Not Available

If `oc get clusteroperator image-registry` shows `Available=False`:

1. Check the registry operator logs:

    ```bash
    KUBECONFIG=hosted-kubeconfig oc logs -n openshift-image-registry \
      deployment/cluster-image-registry-operator
    ```

2. Check the `installer-cloud-credentials` secret exists:

    ```bash
    KUBECONFIG=hosted-kubeconfig oc get secret installer-cloud-credentials \
      -n openshift-image-registry
    ```

    If the secret is missing, check the control plane namespace on the management cluster:

    ```bash
    oc get events -n <namespace>-<cluster-name> | grep image-registry
    ```

### GCS Bucket Creation Fails (403 Forbidden)

A 403 error on bucket creation means the `image-registry` GSA does not have sufficient permissions.

**Check that the GSA has the `Storage Admin` role:**

```bash
gcloud projects get-iam-policy <project-id> \
  --flatten="bindings[].members" \
  --filter="bindings.members:<infra-id>-image-registry@<project-id>.iam.gserviceaccount.com" \
  --format="table(bindings.role)"
```

The output should include `roles/storage.admin`. If it does not, recreate the IAM resources using `hypershift create iam gcp` or grant the role manually:

```bash
gcloud projects add-iam-policy-binding <project-id> \
  --member="serviceAccount:<infra-id>-image-registry@<project-id>.iam.gserviceaccount.com" \
  --role="roles/storage.admin"
```

### WIF Authentication Errors

If the registry operator logs show token exchange errors (e.g., `invalid_grant` or `audience mismatch`):

1. Verify the WIF credential references the correct pool and provider:

    ```bash
    KUBECONFIG=hosted-kubeconfig oc get secret installer-cloud-credentials \
      -n openshift-image-registry -o jsonpath='{.data.service_account\.json}' | base64 -d
    ```

    Confirm the `audience` field matches the WIF provider URL:
    `//iam.googleapis.com/projects/<project-number>/locations/global/workloadIdentityPools/<pool-id>/providers/<provider-id>`

2. Verify the WIF provider trust configuration allows the Kubernetes service account:

    ```bash
    gcloud iam workload-identity-pools providers describe <provider-id> \
      --workload-identity-pool=<pool-id> \
      --project=<project-id> \
      --location=global
    ```

### Bucket Already Exists (409 Conflict)

GCS bucket names are globally unique. If the auto-generated name conflicts with an existing bucket, the registry operator will log a 409 error.

Configure the registry to use a different bucket name:

```bash
KUBECONFIG=hosted-kubeconfig oc patch configs.imageregistry.operator.openshift.io cluster \
  --type=merge \
  --patch='{"spec":{"storage":{"gcs":{"bucket":"<unique-bucket-name>","region":"<region>"}}}}'
```

Then create the bucket and grant permissions as described in [Using a Pre-Existing Bucket](#using-a-pre-existing-bucket).

### Storage Quota Exceeded

If the GCP project has a storage quota that limits GCS bucket creation or capacity, check current quota
usage in the [GCP Console Cloud Storage quotas page](https://console.cloud.google.com/apis/api/storage.googleapis.com/quotas)
and request an increase if needed, or contact your GCP administrator.

## Related Documentation

- [Create GCP IAM Resources](create-gcp-iam.md) — Create the `image-registry` GSA and WIF bindings
- [Create a GCP Hosted Cluster](create-gcp-hosted-cluster.md) — Full cluster creation with the `--image-registry-service-account` flag
- [GCP Workload Identity Federation](https://cloud.google.com/iam/docs/workload-identity-federation) — Upstream GCP WIF documentation
- [OpenShift Image Registry Operator](https://docs.openshift.com/container-platform/latest/registry/index.html) — Upstream registry operator documentation
