Introduction

Prior to version 2022.1, Gradle Enterprise has been distributed as a Replicated Appliance or Replicated Ship / Kubernetes application.

From 2022.1 onwards, Gradle Enterprise is also distributed and installed using the Helm Kubernetes package manager. This is the preferred method to install Gradle Enterprise, and the previous distribution methods are now considered deprecated and will not be supported past 2022.

Migration options

There are two deployment options:

  • A standalone distribution for installation into a single host, similar to the existing appliance installation.

  • A distribution for installation into a Kubernetes cluster, similar to the existing Kubernetes installation.

Do not attempt to make any other changes at the same time as migrating from Replicated to Helm, such as also changing your deployment type to Kubernetes or migrating to a user-managed database.

This guide covers the following migration paths:

  • If you have an existing Replicated appliance installation, migrate to Helm Standalone.

  • If you have an existing Replicated ship installation, migrate to Helm Kubernetes.

If you are planning to migrate from Standalone to Kubernetes, do it after the migration to Helm.
If you are planning to migrate to a user-managed database, do it after the migration to Helm.

Migration of data

In all cases, data is preserved, including Gradle Enterprise configuration (i.e. from the Administration page). That is, there is no explicit data migration required in order to migrate a Replicated-based installation to a Helm-based one.

Appliance to standalone migration

Prior to starting, please review the Gradle Enterprise Helm Standalone Installation Manual which explains Helm and the Gradle Enterprise standalone distribution.

Prerequisites

  • To minimise downtime, this procedure recommends starting the K3s software while the existing appliance installation is still running. Please ensure that the host has at least 1GB of memory available in addition to its current usage to cover for the overhead.

  • The new Gradle Enterprise distribution mechanism has a new license file format. Please contact Gradle support to get an updated license file.

  • Airgap installations require a specific license entitlement. If this is a requirement, please contact your customer success representative.

Migration steps

Update to the latest Gradle Enterprise version

Upgrade to the latest version by following the guidelines in Gradle Enterprise Administration Manual. This will ensure that the migration is from and to the same Gradle Enterprise version.

For online instances, this can be accomplished by clicking on the Check Now button and installing any updates found.

replicated dashboard

Upload required files to the host

Upload the following files to the Gradle Enterprise host:

  • Your new Gradle Enterprise license file.

  • Any SSL certificates being used.

Create a Helm values file

Helm is configured using a combination of settings stored in a yaml file and standalone files such as the Gradle Enterprise license and SSL certificates.

Please review the Helm configuration section from the installation manual for details. We recommend to start with the example values file included in the Helm distribution. See here for instructions how to retrieve it.

This section walks through translating the existing Replicated appliance configuration into a Helm values file.

Create the values file, and populate it based on the following settings in the Replicated Settings page.

Network section

Translate the existing settings to the values file:

Replicated setting Helm configuration key

Hostname

Set global.hostname. Required.

Use HTTP / Use HTTPS

Set ingress.ssl.enabled to false to disable HTTPS.

Use HTTP with external HTTPS termination

Set ingress.ssl.enabled to false and global.externalSSLTermination to true.

HTTP port / HTTPS port

Set ingress.port.http or ingress.port.https to use non-standard ports.

SSL Private Key / SSL Certificate

Set ingress.ssl.key and ingress.ssl.cert.

Examples:

  • Using HTTP with the default port

    network http default port
    values.yaml
    global:
      hostname: ge.example.com
    
    ingress:
      ssl:
        enabled: false
  • Using HTTP with a custom port

    network http custom port
    values.yaml
    global:
      hostname: ge.example.com
    
    ingress:
      port:
        http: 8180
      ssl:
        enabled: false
  • Using HTTPS with a generated certificate and default ports

    network https default ports
    values.yaml
    global:
      hostname: ge.example.com
  • Using HTTPS with a generated certificate and custom ports

    network https generated certificate
    values.yaml
    global:
      hostname: ge.example.com
    
    ingress:
      port:
        http: 8180
        https: 8543
  • Using HTTPS with custom certificate and ports

    network https custom certificate
    values.yaml
    global:
      hostname: ge.example.com
    
    ingress:
      ssl:
        port:
          http: 8180
          https: 8543
        key: |
          -----BEGIN RSA PRIVATE KEY-----
          MIIEpQIBAAKCAQEA4qV8JlqDMi7y85Ykq8dn7uIsi609D6KuFtlc+UvNYjatz0+u
          QzIr3iw//qf7sM8nx8fhGwuWvUWeCE6zbgKjuxDH82J9NQ0ctf70n0qVTeyW1CKR
          w/koEXyecIZIOA9Jh7KNkShfp/x5T3GZ4QK//A/4oJyhD3IeK/nEXr5mSe2AzGxq
          ...
          XlOfXr/xvkXA66PROgvVxfwpN/GNrLXFi1HvMg7MVZJUZQpNzpAzw5JTk2MbawOl
          G7tI0qQ6F20e5R4tPpEDKCFZykyvgGMhfLzsvVlrgaVW8QbVK4YWNtQ=
          -----END RSA PRIVATE KEY-----
        cert: |
          -----BEGIN CERTIFICATE-----
          MIIDKjCCAhKgAwIBAgIRAPNTIHf6/oUuzMKm3ffGNOgwDQYJKoZIhvcNAQELBQAw
          HDEaMBgGA1UEAxMRYXV0by1nZW5lcmF0ZWQtY2EwHhcNMjExMTMwMTU1NDU5WhcN
          MjExMjAxMTU1NDU5WjAjMSEwHwYDVQQDExhvbmx5LWZvci51bml0LXRlc3QubG9j
          ...
          Cn/3yUirFVTslrSYKAemLw8btLO6FDF9dc/lq1o7tKsYVuhEcjqnTah7puJjEN9h
          z+P5RmRxU/kaaFB+Vuw1pRezbaAtZNorVgXnBwrdseY4zLGyhAcGcR9v+VtCiQ==
          -----END CERTIFICATE-----

    Alternatively, the key and certificate can be provided to the helm command via --set-file parameters. See the installation manual for details.

  • Using HTTP with external HTTPS termination and custom ports

    network http external https termination
    values.yaml
    global:
      hostname: ge.example.com
      externalSSLTermination: true
    
    ingress:
      ssl:
        enabled: false
      port:
        http: 8180
        https: 8543
Storage section
Replicated setting Helm configuration key

Installation directory

Set global.storage.directory. Default is /opt/gradle.

Logs directory

Set global.storage.logs.directory. Default is (global.storage.directory)/logs.

Backup directory

Set global.storage.backup.directory. Default is (global.storage.directory)/backups.

Examples:

  • Defaults

    storage defaults
    values.yaml
    # Default configuration. No values required.
  • Custom locations

    storage custom locations
    values.yaml
    global:
      storage:
        directory: /mnt/big-volume/ge
        logs:
          directory: /var/log/gradle-enterprise
        backup:
          directory: /mnt/vol2/ge-backups
Database section

By default, Gradle Enterprise will use an embedded database. Configuration settings only need to be set when connecting to a user-managed database.

Please review the Database configuration section of the installation manual for details. Different values are required based on which type of credentials are provided.

Replicated setting Helm configuration key

Store data in user-managed database

Set database.location to user-managed. Default is embedded.

Host

Set database.connection.host. Required for a user-managed database.

Port

Set database.connection.port. Default is 5432.

Database name

Set database.connection.databaseName. Required for a user-managed database.

Extra JDBC parameters

Set database.connection.params. Default is no extra paramters.

Database superuser username

Set database.credentials.superuser.username.

Database superuser password

Set database.credentials.superuser.password.

Database application user password

Set database.credentials.app.password.

Database migrator user password

Set database.credentials.migrator.password.

Examples:

  • Store data in embedded database

    database embedded
    values.yaml
    # Default configuration. No values required.
  • Store data in user-managed database and provide database superuser credentials

    database user managed superuser
    values.yaml
    database:
      location: user-managed
      connection:
        host: database.example.com
        port: 5432
        databaseName: gradle_enterprise
        params: "?connectTimeout=60"
      credentials:
        superuser:
          username: postgres
          password: superuserPassword
  • Store data in user-managed database and do not provide database superuser credentials

    database user managed no superuser
    values.yaml
    database:
      location: user-managed
      connection:
        host: database.example.com
        port: 5432
        databaseName: gradle_enterprise
        params: "?connectTimeout=60"
      credentials:
        app:
          password: appUserPassword
        migrator:
          password: migratorUserPassword
Airgap installations

Airgap installation requires downloading a Gradle Enterprise airgap bundle on an internet-connected host and then transferring it to the installation host.

Airgap installations should add the below image pull policy to prevent the app from trying to pull Gradle Enterprise container images from the internet at runtime:

values.yaml
global:
  image:
    imagePullPolicy: Never

Install Gradle Enterprise using Helm

Please follow the instructions for an online installation or airgap installation in the installation manual, with the following modifications:

Installing a different version as part of the migration will make it harder to rollback if you encounter problems.
  • Verify that the version you are installing is the same version as is installed in the existing installation. If it is not, then either the existing installation needs to be upgraded or the installation needs to be re-run with the correct version. For online installation, this involves updating the local Helm repository by running helm repo update. For airgap installation, this involves downloading the correct bundle file.

  • Before running the helm install command, stop the existing instance by following the steps in the following section.

  • See the appendix for extra parameters to the helm install command if you are using unattended installation.

  • After running the helm install command, verify the system status.

Stop the existing instance

At this point, expect Gradle Enterprise to be unavailable for up to 30 minutes.
  • Click the Stop now button in the Replicated console

    replicated dashboard
  • Wait for the application to be completely stopped

    replicated dashboard stopped

Verify system status

Gradle Enterprise service availability

You can inspect the status of the Gradle Enterprise pods by running kubectl get pods:

$ kubectl --namespace gradle-enterprise get pods
NAME                                               READY   STATUS    RESTARTS   AGE
gradle-enterprise-operator-76694c949d-md5dh        1/1     Running   0          84m
gradle-proxy-0                                     1/1     Running   0          83m
gradle-database-65d975cf8-dk7kw                    2/2     Running   0          83m
gradle-enterprise-app-0                            1/1     Running   0          83m
gradle-metrics-cfcd8f7f7-zqds9                     1/1     Running   0          83m
gradle-test-distribution-broker-6fd84c6988-x6jvw   1/1     Running   0          83m
gradle-build-cache-node-57b9bdd46d-2txf5           1/1     Running   0          84m
gradle-keycloak-0                                  1/1     Running   0          83m

The app will be ready once all pods report as Running.

Service connectivity verification

Please verify that your system is being connected-to correctly. Which services to check will depend on which you use:

  • If authentication is required, can users log in?

  • Are build scans being collected correctly? See /scans.

  • Are build cache nodes connected? See /cache-admin.

  • Are test distribution agents connected? /admin/test-distribution.

Data verification

Please inspect your system to validate that historical data has been migrated. Viewing /scans and ordering by oldest build scan will show the oldest scans in your system.

Support

If any steps do not result in the expected state, please contact Gradle support.

Cleanup

Once you have verified that your system is functional and has migrated configuration and data correctly, you may uninstall previously installed components at your convenience.

  • Uninstall Replicated. It is important to do this prior to a system restart as otherwise Replicated may attempt to start the old installation of Gradle Enterprise.

  • Docker is no longer needed, and may also be uninstalled.

Ship-based Kubernetes to Helm-based Kubernetes cluster migration

Prior to starting, please review the Gradle Enterprise Helm Kubernetes Installation Manual which explains Helm and the Gradle Enterprise distribution.

Prerequisites

  • The new Gradle Enterprise distribution mechanism has a new license file format. Please contact Gradle support to get an updated license file.

  • Typically, the previous Ship-based Gradle Enterprise installation steps will have been executed on a host with access to your Kubernetes cluster. This migration guide assumes that this host is available for execution of commands, and that the previously created gradle-enterprise directory containing installation and support scripts is present. References to "the host" in this guide refer to this host.

  • You will need your current Ship customer ID and installation ID. Please contact Gradle Support if you do not have these recorded.

  • These instructions assume that only one Gradle Enterprise instance is installed in your Kubernetes cluster. For assistance in migrating an instance which shares a Kubernetes cluster with other instances, please contact Gradle Support.

  • In particular, review the installation options in the installation manual and know the installation method and required steps prior to starting steps in this guide. You should know the shape of the helm install command prior to starting.

Migration steps

Update to the latest Gradle Enterprise version

Upgrade to the latest version by following the guidelines in Gradle Enterprise Kubernetes Administration Manual. This will ensure that the migration is from and to the same Gradle Enterprise version.

Upload required files to the host

Upload the following files to the host:

  • Your new Gradle Enterprise license file.

  • Any SSL certificates being used.

Run the Ship app to review current settings

To view the current system configuration, first run the Ship app. It should be run in the gradle-enterprise directory created during the previous installation.

$ cd path/to/gradle-enterprise
$ docker run -p 8800:8800 --rm -it -v `pwd`:/out \
    -v /var/run/docker.sock:/var/run/docker.sock \
    replicated/ship:0.54.1 app \
    --customer-id=<cust-id> \
    --installation-id=<install-id>

This should show the app starting followed by some lines like this:

Please visit the following URL in your browser to continue the installation

        http://localhost:8800

Visit that port on the hostname of your installation host in your web browser:

k8s ship app

The current configuration should be visible. This can be used as a reference when creating a Helm configuration file below.

Create a Helm values file

Helm is configured using a combination of settings stored in a yaml file and standalone files such as the Gradle Enterprise license and SSL certificates.

Please review the Helm configuration section from the installation manual for details. We recommend to start with the example values file included in the Helm distribution. See here for instructions how to retrieve it.

This section walks through translating the existing Replicated ship configuration into a Helm values file.

Create the values file, and populate it based on the following settings from the Ship application settings screen.

Cluster info section

Translate the existing settings to the values file:

Replicated setting Helm configuration key

Kubernetes namespace

Specified as a part of helm install command. Check the installation manual for details.

Installation on Openshift platform

Set global.openshiftInstallation. Default is false.

Airgap cluster

See global.image.registry and global.image.imagePullSecret below.

Examples:

  • Defaults

    k8s cluster info non airgap
    values.yaml
    # Default configuration. No values required.
  • Openshift

    k8s cluster info openshift
    values.yaml
    global:
      openshiftInstallation: true
  • Airgap

    k8s cluster info airgap

    See the docker registry settings below.

Docker registry settings section
Replicated setting Helm configuration key

Docker registry address

Set global.image.registry. Setting this value will enable airgap mode.

Registry is a mirror

Nothing to set in Helm config. You will need to set up your mirror with appropriate credentials to pull images from registry.gradle.com though. Contact Gradle support for guidance prior to starting the migration.

Registry requires authentication

Set global.image.imagePullSecret. Check the installation manual for details.

Examples:

  • Airgap registry setup without credentials

    k8s docker registry settings no auth
    values.yaml
    global:
      image:
        registry: registry.example.com/gradle-enterprise
  • Airgap registry setup with credentials

    k8s docker registry settings auth
    values.yaml
    global:
      image:
        registry: registry.example.com/gradle-enterprise
        imagePullSecret: example-image-pull-secret-name
Network section

Please review the section on exposing Gradle Enterprise outside your cluster in the installation manual. Existing Ship Kubernetes installations will already be routing traffic to the application’s gradle-proxy service in some way. This guide assumes that this will continue and attempts to configure the proxy to accept traffic in the same way.

Replicated setting Helm configuration key

Hostname

Set global.hostname. Required.

Use HTTP / Use HTTPS

Set proxy.ssl.enabled to true to enable HTTPS terminated by the proxy service.

Use HTTP with external HTTPS termination

Set global.externalSSLTermination to true.

SSL Private Key / SSL Certificate

Set proxy.ssl.key and proxy.ssl.cert. Required when proxy.ssl.enabled=true.

Examples:

  • Using HTTP with the default port

    k8s network http
    values.yaml
    global:
      hostname: ge.example.com
  • Using HTTPS with a custom certificate

    k8s network https
    values.yaml
    global:
      hostname: ge.example.com
    
    proxy:
      ssl:
        enabled: true
        key: |
          -----BEGIN RSA PRIVATE KEY-----
          MIIEpQIBAAKCAQEA4qV8JlqDMi7y85Ykq8dn7uIsi609D6KuFtlc+UvNYjatz0+u
          QzIr3iw//qf7sM8nx8fhGwuWvUWeCE6zbgKjuxDH82J9NQ0ctf70n0qVTeyW1CKR
          w/koEXyecIZIOA9Jh7KNkShfp/x5T3GZ4QK//A/4oJyhD3IeK/nEXr5mSe2AzGxq
          ...
          XlOfXr/xvkXA66PROgvVxfwpN/GNrLXFi1HvMg7MVZJUZQpNzpAzw5JTk2MbawOl
          G7tI0qQ6F20e5R4tPpEDKCFZykyvgGMhfLzsvVlrgaVW8QbVK4YWNtQ=
          -----END RSA PRIVATE KEY-----
        cert: |
          -----BEGIN CERTIFICATE-----
          MIIDKjCCAhKgAwIBAgIRAPNTIHf6/oUuzMKm3ffGNOgwDQYJKoZIhvcNAQELBQAw
          HDEaMBgGA1UEAxMRYXV0by1nZW5lcmF0ZWQtY2EwHhcNMjExMTMwMTU1NDU5WhcN
          MjExMjAxMTU1NDU5WjAjMSEwHwYDVQQDExhvbmx5LWZvci51bml0LXRlc3QubG9j
          ...
          Cn/3yUirFVTslrSYKAemLw8btLO6FDF9dc/lq1o7tKsYVuhEcjqnTah7puJjEN9h
          z+P5RmRxU/kaaFB+Vuw1pRezbaAtZNorVgXnBwrdseY4zLGyhAcGcR9v+VtCiQ==
          -----END CERTIFICATE-----

    Alternatively, the key and certificate can be provided to the helm command via --set-file parameters. See the installation manual for details.

  • Using HTTP with external HTTPS termination

    k8s network https termination
    values.yaml
    global:
      hostname: ge.example.com
      externalSSLTermination: true
Storage section

To ensure that your data is preserved when performing migration steps below, it is important that exactly the same storage class and capacities are used.

Replicated setting Helm configuration key

Storage class

Set all of global.storage.data.class, global.storage.backup.class, and global.storage.logs.class to this storage class name.

Build scans storage capacity

Set database.storage.data.capacity. Append Gi to the previous numeric value.

Build scans backups storage capacity

Set database.storage.backup.capacity. Append Gi to the previous numeric value.

Build cache storage capacity

Set buildCacheNode.storage.data.capacity. Append Gi to the previous numeric value.

Test Distribution file cache storage capacity

Set testDistribution.storage.data.capacity. Append Gi to the previous numeric value.

Example:

k8s storage custom
values.yaml
global:
  storage:
    data:
      class: some-provisioned-io-storage-class
    backup:
      class: some-provisioned-io-storage-class
    logs:
      class: some-provisioned-io-storage-class

database:
  storage:
    data:
      capacity: 500Gi
    backup:
      capacity: 500Gi
buildCacheNode:
  storage:
    data:
      capacity: 50Gi
testDistribution:
  storage:
    data:
      capacity: 30Gi
Horizontal scaling section
Replicated setting Helm configuration key

Number of replicas

Set global.scaling.replicas.

Examples:

  • Horizontal scaling disabled

    k8s horizontal scaling disabled
    values.yaml
    # Default configuration. No values required.
  • Horizontal scaling enabled

    k8s horizontal scaling enabled
    values.yaml
    global:
      scaling:
        replicas: 2
Database section

By default, Gradle Enterprise will use an embedded database. Configuration settings only need to be set when connecting to a user-managed database.

Please review the Database configuration section of the installation manual for details. Different values are required based on which type of credentials are provided.

Replicated setting Helm configuration key

Store data in user-managed database

Set database.location to user-managed. Default is embedded.

Host

Set database.connection.host. Required for a user-managed database.

Port

Set database.connection.port. Default is 5432.

Database name

Set database.connection.databaseName. Required for a user-managed database.

Extra JDBC parameters

Set database.connection.params. Default is no extra paramters.

Database superuser username

Set database.credentials.superuser.username.

Database superuser password

Set database.credentials.superuser.password.

Database application user password

Set database.credentials.app.password.

Database migrator user password

Set database.credentials.migrator.password.

Examples:

  • Store data in embedded database

    k8s database embedded
    values.yaml
    # Default configuration. No values required.
  • Store data in user-managed database and provide database superuser credentials directly

    k8s database superuser direct values
    values.yaml
    database:
      location: user-managed
      connection:
        host: database.example.com
        port: 5432
        databaseName: gradle_enterprise
        params: "?connectTimeout=60"
      credentials:
        superuser:
          username: postgres
          password: superuserPassword
  • Store data in user-managed database and provide database superuser credentials as ConfigMap/Secret

    k8s database superuser configmaps
    values.yaml
    database:
      location: user-managed
      connection:
        configMapName: my-example-db-connection-details
      credentials:
        superuser:
          secretName: my-example-db-superuser-credentials
  • Store data in user-managed database and do not provide database superuser credentials (direct values)

    k8s database no superuser direct creds
    values.yaml
    database:
      location: user-managed
      connection: ...
      credentials:
        app:
          password: app_password
        migrator:
          password: migrator_password
  • Store data in user-managed database and do not provide database superuser credentials (secrets)

    k8s database no superuser configmaps
    values.yaml
    database:
      location: user-managed
      connection: ...
      credentials:
        app:
          secretName: my-example-db-application-credentials
        migrator:
          secretName: my-example-db-migrator-credentials
Terminate the Ship app

Once your Helm values file has been populated from the current settings, you may close the browser window and terminate the Ship app process by issuing a CTRL-C in the terminal in which it is running.

Verify that all persistent volumes used by existing app have Retain reclaim policy.

$ kubectl get pv | grep " gradle-enterprise/"

The output should look something like this:

NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                                                                 STORAGECLASS   REASON   AGE
pvc-4367dd37-233c-43f5-998a-0b6f979626cb   200Mi      RWO            Delete           Bound    gradle-enterprise/gradle-test-distribution-logs-volume                local-path              76m
pvc-49421860-1100-4b8e-8ac6-8711343e1628   200Mi      RWO            Delete           Bound    gradle-enterprise/gradle-database-logs-volume                         local-path              76m
pvc-ed0a52c5-a7a4-4c93-b2a8-3a190f866db7   200Mi      RWO            Delete           Bound    gradle-enterprise/logs-gradle-keycloak-0                              local-path              76m
pvc-c0d91c7b-4f3e-41e1-a379-1673b07f4408   10Gi       RWO            Delete           Bound    gradle-enterprise/gradle-database-backups-volume                      local-path              76m
pvc-f92182bc-8d44-4c41-82e5-3719c1226444   200Mi      RWO            Delete           Bound    gradle-enterprise/gradle-metrics-volume                               local-path              76m
pvc-71fc04d0-b395-43b3-89be-9fbcb55c9301   10Gi       RWO            Delete           Bound    gradle-enterprise/gradle-database-volume                              local-path              76m
pvc-c1f84161-8cc6-4c41-ac5a-c78b3c2dfd41   10Gi       RWO            Delete           Bound    gradle-enterprise/gradle-build-cache-volume                           local-path              76m
pvc-537be141-4be6-4e34-8c1a-d6b161e06ede   200Mi      RWO            Delete           Bound    gradle-enterprise/gradle-build-cache-node-logs-volume                 local-path              76m
pvc-b6eadedf-eddd-460f-9b67-f852559a9627   200Mi      RWO            Delete           Bound    gradle-enterprise/logs-gradle-enterprise-app-0                        local-path              75m
pvc-f46ad5b2-1f2e-429d-88da-88cd33689c16   500Mi      RWO            Delete           Bound    gradle-enterprise/logs-gradle-proxy-0                                 local-path              75m
pvc-2ca92343-2bc9-4946-94f2-de7ca7243284   200Mi      RWO            Delete           Bound    gradle-enterprise/gradle-enterprise-operator-logs-volume              local-path              75m
pvc-d712f0de-324f-43c0-8508-96d5de148f68   10Gi       RWO            Delete           Bound    gradle-enterprise/gradle-test-distribution-broker-file-cache-volume   local-path              75m

If any have a Delete reclaim policy, it is very likely that data will be lost during the upgrade process.

Please review the Kubernetes documentation to understand the difference between Delete and Retain policies, and to see commands to update the policy.

The following steps can be used to update the reclaim policy:

  • All Gradle Enterprise persistent volume claim names start with gradle-enterprise/. If there are volume claim names that do not match the names shown in the output above, please contact Gradle Support so ensure that you do not accidentally change the policy for an incorrect persistent volume.

  • Run the following command to update the reclaim policy of matching volumes:

    $ kubectl get pv \
        | grep " gradle-enterprise/" \
        | awk '{print $1}' \
        | while read v ; do kubectl patch pv $v -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}' ; done

    You should see output like this:

persistentvolume/pvc-ed0a52c5-a7a4-4c93-b2a8-3a190f866db7 patched
persistentvolume/pvc-b6eadedf-eddd-460f-9b67-f852559a9627 patched
persistentvolume/pvc-f46ad5b2-1f2e-429d-88da-88cd33689c16 patched
persistentvolume/pvc-49421860-1100-4b8e-8ac6-8711343e1628 patched
persistentvolume/pvc-c0d91c7b-4f3e-41e1-a379-1673b07f4408 patched
persistentvolume/pvc-f92182bc-8d44-4c41-82e5-3719c1226444 patched
persistentvolume/pvc-71fc04d0-b395-43b3-89be-9fbcb55c9301 patched
persistentvolume/pvc-c1f84161-8cc6-4c41-ac5a-c78b3c2dfd41 patched
persistentvolume/pvc-537be141-4be6-4e34-8c1a-d6b161e06ede patched
persistentvolume/pvc-2ca92343-2bc9-4946-94f2-de7ca7243284 patched
persistentvolume/pvc-d712f0de-324f-43c0-8508-96d5de148f68 patched
persistentvolume/pvc-4367dd37-233c-43f5-998a-0b6f979626cb patched

Rechecking the volume states should show that they all have a Retain policy:

$ kubectl get pv | grep " gradle-enterprise/"
pvc-1da2bfc4-41f8-469a-a8e5-bd4544f42a2a   10Gi       RWO            Retain           Bound    gradle-enterprise/gradle-database-volume                              local-path              13m
pvc-8e13739c-f445-4537-acfb-27cb6f0cea54   200Mi      RWO            Retain           Bound    gradle-enterprise/logs-gradle-keycloak-0                              local-path              13m
pvc-36ce81f7-1e74-407a-84cc-41678bd802d9   200Mi      RWO            Retain           Bound    gradle-enterprise/gradle-database-logs-volume                         local-path              13m
pvc-a004b6d8-94e8-4f9d-9c4c-77f0a67a6015   10Gi       RWO            Retain           Bound    gradle-enterprise/gradle-test-distribution-broker-file-cache-volume   local-path              13m
pvc-0a0e05f1-d8aa-4407-b66a-3d7f275c7000   200Mi      RWO            Retain           Bound    gradle-enterprise/logs-gradle-enterprise-app-0                        local-path              13m
pvc-8df76bb3-7132-4ff9-88d8-27ebce374508   200Mi      RWO            Retain           Bound    gradle-enterprise/gradle-test-distribution-logs-volume                local-path              13m
pvc-83750521-8b28-45c9-9860-ade8add0e08b   500Mi      RWO            Retain           Bound    gradle-enterprise/logs-gradle-proxy-0                                 local-path              13m
pvc-c6d74ff6-f8c9-4366-b07a-ca86c8c48a3f   200Mi      RWO            Retain           Bound    gradle-enterprise/gradle-metrics-volume                               local-path              13m
pvc-342ba3e8-5646-4b71-8c90-cfa92c50e41f   10Gi       RWO            Retain           Bound    gradle-enterprise/gradle-database-backups-volume                      local-path              13m
pvc-6eb4b99e-32db-4ef5-bc5c-e653e63d1098   200Mi      RWO            Retain           Bound    gradle-enterprise/gradle-build-cache-node-logs-volume                 local-path              13m
pvc-6f2df0c5-4e8e-48b3-8896-3983b665b9e3   200Mi      RWO            Retain           Bound    gradle-enterprise/gradle-enterprise-operator-logs-volume              local-path              13m
pvc-aeb436f9-c084-4b41-83a2-050e3a062661   10Gi       RWO            Retain           Bound    gradle-enterprise/gradle-build-cache-volume                           local-path              13m

Install Gradle Enterprise using Helm

Please follow the instructions for an online installation or airgap installation in the installation manual, with the following modifications.

Installing a different version as part of the migration will make it harder to rollback if you encounter problems.
  • Verify that the version you are installing is the same version as is installed in the existing installation. If it is not, then either the existing installation needs to be upgraded or the installation needs to be re-run with the correct version. For online installation, this involves updating the local Helm repository by running helm repo update. For airgap installation, this involves downloading the correct bundle file.

  • Before running the helm install command, uninstall the existing installation and make all persistent volumes available by following the steps in the following sections.

  • See the appendix for extra parameters to the helm install command if you are using unattended installation.

  • After running the helm install command, verify the system status.

Uninstall the existing installation

At this point, expect Gradle Enterprise to be unavailable for up to 30 minutes.

The uninstall script does not remove persistent volumes, and those set to a Retain policy above will remain available until the new installation picks them up.

Run the uninstall script for the current installation:

# from the previously-created grade-enterprise directory
$ ./installer/scripts/uninstall.sh

In addition, some extra persistent volume claims may need to be deleted. Check if any are left (subsequent commands assume the application is installed in the gradle-enterprise namespace - please adjust to your setup):

$ kubectl --namespace gradle-enterprise get pvc
NAME                           STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
logs-gradle-keycloak-0         Bound    pvc-8e13739c-f445-4537-acfb-27cb6f0cea54   200Mi      RWO            local-path     23m
logs-gradle-enterprise-app-0   Bound    pvc-0a0e05f1-d8aa-4407-b66a-3d7f275c7000   200Mi      RWO            local-path     23m
logs-gradle-proxy-0            Bound    pvc-83750521-8b28-45c9-9860-ade8add0e08b   500Mi      RWO            local-path     23m

If any are left, remove them by executing the following command:

$ kubectl --namespace gradle-enterprise delete pvc \
    $(kubectl --namespace gradle-enterprise get pvc -o jsonpath='{.items[*].metadata.name}')
persistentvolumeclaim "logs-gradle-keycloak-0" deleted
persistentvolumeclaim "logs-gradle-enterprise-app-0" deleted
persistentvolumeclaim "logs-gradle-proxy-0" deleted

Make all persistent volumes available

At this point, all persistent volumes should be marked as Released. To confirm:

$ kubectl get pv | grep " gradle-enterprise/"
pvc-1da2bfc4-41f8-469a-a8e5-bd4544f42a2a   10Gi       RWO            Retain           Released   gradle-enterprise/gradle-database-volume                              local-path              27m
pvc-342ba3e8-5646-4b71-8c90-cfa92c50e41f   10Gi       RWO            Retain           Released   gradle-enterprise/gradle-database-backups-volume                      local-path              27m
pvc-36ce81f7-1e74-407a-84cc-41678bd802d9   200Mi      RWO            Retain           Released   gradle-enterprise/gradle-database-logs-volume                         local-path              27m
pvc-aeb436f9-c084-4b41-83a2-050e3a062661   10Gi       RWO            Retain           Released   gradle-enterprise/gradle-build-cache-volume                           local-path              27m
pvc-6eb4b99e-32db-4ef5-bc5c-e653e63d1098   200Mi      RWO            Retain           Released   gradle-enterprise/gradle-build-cache-node-logs-volume                 local-path              27m
pvc-a004b6d8-94e8-4f9d-9c4c-77f0a67a6015   10Gi       RWO            Retain           Released   gradle-enterprise/gradle-test-distribution-broker-file-cache-volume   local-path              27m
pvc-8df76bb3-7132-4ff9-88d8-27ebce374508   200Mi      RWO            Retain           Released   gradle-enterprise/gradle-test-distribution-logs-volume                local-path              27m
pvc-6f2df0c5-4e8e-48b3-8896-3983b665b9e3   200Mi      RWO            Retain           Released   gradle-enterprise/gradle-enterprise-operator-logs-volume              local-path              27m
pvc-c6d74ff6-f8c9-4366-b07a-ca86c8c48a3f   200Mi      RWO            Retain           Released   gradle-enterprise/gradle-metrics-volume                               local-path              27m
pvc-8e13739c-f445-4537-acfb-27cb6f0cea54   200Mi      RWO            Retain           Released   gradle-enterprise/logs-gradle-keycloak-0                              local-path              27m
pvc-0a0e05f1-d8aa-4407-b66a-3d7f275c7000   200Mi      RWO            Retain           Released   gradle-enterprise/logs-gradle-enterprise-app-0                        local-path              27m
pvc-83750521-8b28-45c9-9860-ade8add0e08b   500Mi      RWO            Retain           Released   gradle-enterprise/logs-gradle-proxy-0                                 local-path              27m

To make these available for the new installation to pick up, run the following command

$ kubectl get pv \
    | grep " gradle-enterprise/" \
    | awk '{print $1}' \
    | while read v ; do kubectl patch pv $v --type json -p '[{"op": "remove", "path": "/spec/claimRef/uid"}]' ; done
persistentvolume/pvc-1da2bfc4-41f8-469a-a8e5-bd4544f42a2a patched
persistentvolume/pvc-342ba3e8-5646-4b71-8c90-cfa92c50e41f patched
persistentvolume/pvc-36ce81f7-1e74-407a-84cc-41678bd802d9 patched
persistentvolume/pvc-aeb436f9-c084-4b41-83a2-050e3a062661 patched
persistentvolume/pvc-6eb4b99e-32db-4ef5-bc5c-e653e63d1098 patched
persistentvolume/pvc-a004b6d8-94e8-4f9d-9c4c-77f0a67a6015 patched
persistentvolume/pvc-8df76bb3-7132-4ff9-88d8-27ebce374508 patched
persistentvolume/pvc-6f2df0c5-4e8e-48b3-8896-3983b665b9e3 patched
persistentvolume/pvc-c6d74ff6-f8c9-4366-b07a-ca86c8c48a3f patched
persistentvolume/pvc-8e13739c-f445-4537-acfb-27cb6f0cea54 patched
persistentvolume/pvc-0a0e05f1-d8aa-4407-b66a-3d7f275c7000 patched
persistentvolume/pvc-83750521-8b28-45c9-9860-ade8add0e08b patched

Now all persistent volumes should report as being Available:

$ kubectl get pv | grep " gradle-enterprise/"
pvc-1da2bfc4-41f8-469a-a8e5-bd4544f42a2a   10Gi       RWO            Retain           Available   gradle-enterprise/gradle-database-volume                              local-path              32m
pvc-342ba3e8-5646-4b71-8c90-cfa92c50e41f   10Gi       RWO            Retain           Available   gradle-enterprise/gradle-database-backups-volume                      local-path              32m
pvc-36ce81f7-1e74-407a-84cc-41678bd802d9   200Mi      RWO            Retain           Available   gradle-enterprise/gradle-database-logs-volume                         local-path              32m
pvc-aeb436f9-c084-4b41-83a2-050e3a062661   10Gi       RWO            Retain           Available   gradle-enterprise/gradle-build-cache-volume                           local-path              32m
pvc-6eb4b99e-32db-4ef5-bc5c-e653e63d1098   200Mi      RWO            Retain           Available   gradle-enterprise/gradle-build-cache-node-logs-volume                 local-path              32m
pvc-a004b6d8-94e8-4f9d-9c4c-77f0a67a6015   10Gi       RWO            Retain           Available   gradle-enterprise/gradle-test-distribution-broker-file-cache-volume   local-path              32m
pvc-8df76bb3-7132-4ff9-88d8-27ebce374508   200Mi      RWO            Retain           Available   gradle-enterprise/gradle-test-distribution-logs-volume                local-path              32m
pvc-6f2df0c5-4e8e-48b3-8896-3983b665b9e3   200Mi      RWO            Retain           Available   gradle-enterprise/gradle-enterprise-operator-logs-volume              local-path              32m
pvc-c6d74ff6-f8c9-4366-b07a-ca86c8c48a3f   200Mi      RWO            Retain           Available   gradle-enterprise/gradle-metrics-volume                               local-path              32m
pvc-8e13739c-f445-4537-acfb-27cb6f0cea54   200Mi      RWO            Retain           Available   gradle-enterprise/logs-gradle-keycloak-0                              local-path              32m
pvc-0a0e05f1-d8aa-4407-b66a-3d7f275c7000   200Mi      RWO            Retain           Available   gradle-enterprise/logs-gradle-enterprise-app-0                        local-path              32m
pvc-83750521-8b28-45c9-9860-ade8add0e08b   500Mi      RWO            Retain           Available   gradle-enterprise/logs-gradle-proxy-0                                 local-path              32m

Verify system status

Persistent volume binding

Checking the persistent volumes should show them all bound again:

$ kubectl get pv | grep " gradle-enterprise/"
pvc-a004b6d8-94e8-4f9d-9c4c-77f0a67a6015   10Gi       RWO            Retain           Bound    gradle-enterprise/gradle-test-distribution-broker-file-cache-volume   local-path              57m
pvc-342ba3e8-5646-4b71-8c90-cfa92c50e41f   10Gi       RWO            Retain           Bound    gradle-enterprise/gradle-database-backups-volume                      local-path              57m
pvc-c6d74ff6-f8c9-4366-b07a-ca86c8c48a3f   200Mi      RWO            Retain           Bound    gradle-enterprise/gradle-metrics-volume                               local-path              57m
pvc-6eb4b99e-32db-4ef5-bc5c-e653e63d1098   200Mi      RWO            Retain           Bound    gradle-enterprise/gradle-build-cache-node-logs-volume                 local-path              56m
pvc-36ce81f7-1e74-407a-84cc-41678bd802d9   200Mi      RWO            Retain           Bound    gradle-enterprise/gradle-database-logs-volume                         local-path              57m
pvc-aeb436f9-c084-4b41-83a2-050e3a062661   10Gi       RWO            Retain           Bound    gradle-enterprise/gradle-build-cache-volume                           local-path              56m
pvc-1da2bfc4-41f8-469a-a8e5-bd4544f42a2a   10Gi       RWO            Retain           Bound    gradle-enterprise/gradle-database-volume                              local-path              57m
pvc-6f2df0c5-4e8e-48b3-8896-3983b665b9e3   200Mi      RWO            Retain           Bound    gradle-enterprise/gradle-enterprise-operator-logs-volume              local-path              56m
pvc-8df76bb3-7132-4ff9-88d8-27ebce374508   200Mi      RWO            Retain           Bound    gradle-enterprise/gradle-test-distribution-logs-volume                local-path              57m
pvc-83750521-8b28-45c9-9860-ade8add0e08b   500Mi      RWO            Retain           Bound    gradle-enterprise/logs-gradle-proxy-0                                 local-path              57m
pvc-0a0e05f1-d8aa-4407-b66a-3d7f275c7000   200Mi      RWO            Retain           Bound    gradle-enterprise/logs-gradle-enterprise-app-0                        local-path              57m
pvc-8e13739c-f445-4537-acfb-27cb6f0cea54   200Mi      RWO            Retain           Bound    gradle-enterprise/logs-gradle-keycloak-0                              local-path              57m
Gradle Enterprise service availability

You can inspect the status of the Gradle Enterprise pods by running kubectl get pods:

$ kubectl --namespace gradle-enterprise get pods
NAME                                              READY   STATUS    RESTARTS   AGE
gradle-enterprise-operator-5646785cc5-hk7br       1/1     Running   0          8m39s
gradle-database-5946778f79-5nfbf                  2/2     Running   0          8m39s
gradle-proxy-0                                    1/1     Running   0          8m39s
gradle-metrics-7478b7875b-vp7j9                   1/1     Running   0          8m39s
gradle-enterprise-app-0                           1/1     Running   0          8m39s
gradle-build-cache-node-7bcb768f55-hb7zv          1/1     Running   0          8m39s
gradle-test-distribution-broker-5698459b8-z9skv   1/1     Running   0          8m39s
gradle-keycloak-0                                 1/1     Running   0          8m39s

The app will be ready once all pods report as Running.

Service connectivity verification

Please verify that your system is being connected-to correctly. Which services to check will depend on which you use:

  • If authentication is required, can users log in?

  • Are build scans being collected correctly? See /scans.

  • Are build cache nodes connected? See /cache-admin.

  • Are test distribution agents connected? /admin/test-distribution.

Data verification

Please inspect your system to validate that historical data has been migrated. Viewing /scans and ordering by oldest build scan will show the oldest scans in your system.

Support

If any steps do not result in the expected state, please contact Gradle support.

Appendix A: Unattended installation

If you are configuring Gradle Enterprise using an Unattended installation file, you must add additional parameters to the helm install commands above:

--set-file=global.unattended.configuration=path/to/unattended.yaml
--set-file=global.unattended.key=path/to/unattended.key

Appendix B: Contacting support

Contact Gradle support by creating a support ticket at support.gradle.com, including:

  • Details of the type of migration

  • The version being migrated from/to

  • Steps taken so far

  • Errors or unexpected behavior