diff --git a/.github/config/en-custom.txt b/.github/config/en-custom.txt index 796554b..955dbdd 100644 --- a/.github/config/en-custom.txt +++ b/.github/config/en-custom.txt @@ -1022,3 +1022,12 @@ pdf pdfs Knative genericized +gapped +npmjs +Sonatype +amd +darwin +Homebrew +Winget +howto +AzureDevOps diff --git a/features/2025-07-10-offline-install-feature-spec.md b/features/2025-07-10-offline-install-feature-spec.md new file mode 100644 index 0000000..a76321e --- /dev/null +++ b/features/2025-07-10-offline-install-feature-spec.md @@ -0,0 +1,459 @@ +# Offline Installation Feature Spec + +[@zachcasper](https://github.com/zachcasper) + +## Summary + +Many large organizations have mature security practices where all software entering the organization's environment are controlled. Typically all software packages are scanned for security vulnerabilities prior to being stored internally. Access to non-authorized software packages are restricted. + +Radius must support these organizations' requirements by making it easy to install, configure, and upgrade Radius when not connected to the internet, or in *air gapped* environments. + +### Goals + +* Enable platform engineers to install and upgrade Radius while not connected to the internet +* Enable platform engineers to control what versions of all required software components are installed +* Modify Terraform from silently being installed in the background to explicitly installed by the platform engineer +* Increase the transparency of what software is required to run Radius + +### Non-Goals (out of scope) + +* Enhancing the Radius installation experience in other ways + +## Inventory of software components + +Radius makes the assumption that the installation environment has full access to the internet and installs many software components on behalf of the user. Below is an inventory: + +| # | Component | As-Is Distribution | To-Be Online Distribution | To-Be Offline Distribution | +| ---- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------ | +| 1a | `rad` CLI binary | [install.sh](https://raw.githubusercontent.com/radius-project/radius/main/deploy/install.sh) script | No change | Enhanced `install.sh` | +| 1b | `rad-bicep` binary | `rad bicep download` | Installed by `install.sh` | Installed by `install.sh` | +| 1c | `bicepconfig.json` | `rad init` | Created by to `install.sh` | Created by to `install.sh` | +| 2 | [Radius Helm chart](https://github.com/project-radius/radius/tree/main/deploy/Chart) | `rad init`, `rad install` or `helm repo add radius` | No change | Chart manually pulled | +| 3 | Radius container images ( `ucpd`, `controller`, `applications-rp`, `dynamic-rp`, `bicep`, `dashboard`) | Downloaded via the Radius Helm chart from GHCR | No change | Manually imported into the user's private OCI registry | +| 4 | Contour container images (`contour`, `envoy`) | Downloaded via the Radius Helm chart from Docker Hub | Short-term: No change; Long-term: Removal of Contour from Radius install | Radius will have an option to not install Contour | +| 5 | `radius` and `aws` Bicep extensions | Downloaded bi `rad-bicep` dynamically from ACR | No change | Manually imported into the user's private OCI registry | +| 6 | `manifest-to-bice-extension` NPM package | Remote code execution from [npmjs.com](https://www.npmjs.com/package/@radius-project/manifest-to-bicep-extension) | This package will be eliminated and implemented in the Go program | Same | +| 7 | Terraform binaries | Dynamically installed by Applications RP from [hashicorp.com](https://releases.hashicorp.com/) | Explicitly installed into the Radius control plane by the platform engineer | Same | + +## Scenario 1 – Installing Radius + +### User Story 1 – Downloading releases + +***As a platform engineer, I need to download the Radius release to an environment not connected to the internet*** + +The platform engineer consults the installation documentation which instructs them to download: + +* Radius CLI binaries (`install.sh`, `rad`, and `rad-bicep`) +* Radius Helm chart +* Radius container images +* Radius and AWS Bicep extensions + +> [!NOTE] +> +> The Contour container images can optionally be included in the Radius container images. For this feature specification, it is assumed that Contour will not be installed. + +**Radius CLI binaries** + +The platform engineer or security engineer imports `install.sh`, `rad`, and `rad-bicep` into their software mirror (for example, Sonatype Nexus Repository mirror) or other file storage location. + +> [!NOTE] +> +> The `rad-bicep` is a legacy of the Bicep fork and is no longer required. However, the `bicep` binary must still be installed. Work is needed to move to `bicep` and deprecate the `rad-bicep` binary. However, this is out of scope for this document. + +**Helm chart** + +If the organization has a Helm chart repository mirror such as Sonatype Nexus, the platform engineer or security engineer can load the Radius chart into the mirror using something similar to: + +```bash +$ helm pull https://github.com/radius-project/radius/tree/main/deploy/Chart +$ helm repo add nexus http:///repository// +$ helm cm-push radius nexus +``` + +If the organization does not have a Helm chart repository mirror, the platform engineer or security engineer can download the Radius chart. + +```bash +$ helm pull https://github.com/radius-project/radius/tree/main/deploy/Chart +``` + +**Container images** + +The platform engineer or security engineer imports the Radius container images into their OCI registry. + +**Radius and AWS Bicep extensions** + +The platform engineer or security engineer imports the Radius Bicep extensions into their OCI registry. + +### User Story 2 – Install CLI + +***As a platform engineer or developer, I need to install the Radius CLI using pre-downloaded binaries*** + +The platform engineer or developer runs the installation script with additional parameters. + +```bash +$ install.sh \ + --version + --binary-install-source \ + --bicep-extension-oci-registry / +``` + +This script performs the following: + +1. Gets the kernel type and system architecture (as-is) +2. Downloads the `rad` binary from Github (as-is) or the binary install source (new) +3. Gets the latest release information if the version is not specified (as-is) +4. Installs the `rad` binary in `/usr/local/bin` or `%LOCALAPPDATA%\radius` (as-is) +5. Installs the `rad-bicep` binary in `/usr/local/bin` or `%LOCALAPPDATA%\radius` (change from as-is) +6. Creates the `bicepconfig.json` in the user's home directory (new) using the `bicep-extension-oci-registry`, if specified (new) + +The key changes from today's `install.sh` include: + +* Today, `rad-bicep` is installed via the `rad bicep download` command. This approach is problematic in that is is not obvious that Radius is downloading a new binary. The command also installs the `rad-bicep` binary in a different directory (`~/.rad/bin`) which is not ideal. The `install.sh` script is enhanced to install the `rad-bicep` binary side-by-side `rad` and the `rad bicep download` command is removed. +* Today, the `bicepconfig.json` file is created by `rad init`. This is problematic since offline installs will not use `rad init`. Also, the contents of `bicepconfig.json` file created by `rad init` is hard-coded. With this change, the `install.sh` script will create the `bicepconfig.json` file based on user input (`--bicep-extension-oci-registry`). + +### User Story 3 – Install Radius via CLI + +***As a platform engineer, I need to install Radius offline using the Radius CLI*** + +The platform engineer installs Radius using the CLI specifying the private OCI registry and to skip installing Contour. + +```bash +rad install kubernetes \ + --set image.repository=/ + --skip-contour-install +``` + +> [!NOTE] +> +> `rad init` is out of scope for offline installations. We expect `rad init` to continue to be optimized for the quick start scenario. + +The `image.repository` is the private OCI registry where the Radius container images were stored in the previous step. + +The `--skip-contour-install` does not install Contour. Therefore the Gateway resource types will not function until extensibility is implemented. + +> [!CAUTION] +> +> `--skip-contour-install` is expected to be removed from Radius in the near future as part of the compute extensibility work. The design for the Gateway recipe no longer uses Contour and Contour is no longer installed by default. + +If a Helm mirror has not been configured via `helm repo add`, the `--chart` argument can be used. + +```bash +rad install kubernetes \ + --chart \ + ... +``` + +### User Story 4 – Install Radius via Helm + +***As a platform engineer, I need to install Radius using Helm*** + +The platform engineer installs Radius using Helm with similar settings. + +```bash +helm upgrade radius radius/radius \ + --install \ + --create-namespace + --namespace radius-system \ + --version 0.48.0 \ + --wait --timeout 15m0s \ + --set image.repository=/ +``` + +## Scenario 2 – Managing Terraform + +### User Story 5 – Install Terraform + +***As a platform engineer, I need to install Terraform into the Radius control plane in my connected environment*** + +**Summary** + +Today, the Application RP container dynamically downloads the Terraform binary from hashicorp.com when a Terraform recipe is executed. This is problematic for several reasons: + +* Terraform is Radius dependency, not a Radius component. So platform engineers need to be in control of what Terraform is being used. They may use a specific version of Terraform within their organization. They may have specific licensing concerns about Terraform. Or they may simply not want Terraform to be running in their environment. +* Many organizations have sophisticated Terraform configurations. They may have Terraform provider mirrors and specific backend (state store) configurations. + +Therefore, Radius will move to a model where the platform engineer explicitly installs Terraform. This is true for offline installations as well as connected installations. + +**User Experience** + +The platform engineer creates a Terraform configuration. + +```bash +$ rad terraform create +No Terraform release specified, installing the latest from releases.hashicorp.com +terraform_1.12.2_linux_amd64 installed in the Radius control plane +``` + +When this command is executed, Radius: + +* Downloads the latest Terraform release from releases.hashicorp.com and validates the checksum +* Installs the Terraform binary in the appropriate location within the Radius control plane +* Performs a `terraform init` and reports any output to the platform engineer + +Changes from today's Radius: + +* Terraform is no longer dynamically installed in Application RP +* `recipeConfig` is removed from the Environment resource + +### User Story 6 – Configure Terraform + +***As a platform engineer, I need to configure Terraform into the Radius control plane in my connected environment*** + +Today, Radius supports configuring two options: (1) private Git repositories, and (2) passing in Radius secrets to Terraform providers. + +**Private Git Repositories** + +To configure a private Git repository the platform engineer first creates a Bicep file with a Secret resource. + +**`terraformGitSecret.bicep`** + +```yaml +resource terraformGitSecret 'Applications.Core/secretStores@2023-10-01-preview' = { + name: 'terraformGitSecret' + properties: { + type: 'generic' + data: { + pat: { + value: + } + } + } +} +``` + +Then deploys the secret in a resource group. + +```bash +$ rad group create terraform-config +creating resource group "terraform-config" in workspace "kind"... + +resource group "terraform-config" created +$ rad deploy terraformGitSecret.bicep --group terraform-config +``` + +Finally, the platform engineer updates the Terraform configuration. + +```bash +$ rad terraform update \ + --git-pat 'github.com: {secret: terraformGitSecret}' +The Terraform configuration has been updated +``` + +**Passing Radius Secrets to Terraform providers** + +To pass a Secret to a Terraform provider, the platform engineer first creates a Bicep file with a Secret resource. + +**`azureDevOpsProviderSecret.bicep`** + +```yaml +resource azureDevOpsProviderSecret 'Applications.Core/secretStores@2023-10-01-preview' = { + name: 'azureDevOpsProviderSecret' + properties: { + type: 'generic' + data: { + pat: { + value: + } + } + } +} +``` + +Then deploys the secret in a resource group. + +```bash +$ rad deploy terraformGitSecret.bicep --group terraform-config +``` + +Finally, the platform engineer updates the Terraform configuration. + +```bash +$ rad terraform update \ + --providers 'azuredevops: {personal_access_token: azureDevOpsProviderSecret} +The Terraform configuration has been updated +``` + +### User Story 7 – Offline Terraform Installation and Configuration + +***As a platform engineer, I need to install configure Terraform in my offline environment*** + +Since the offline installation is more complex and takes many parameters, a Terraform resource type is used. The Terraform resource replaces the `recipeConfig` properties in the Environment today and adds other [terraformrc configuration options](https://developer.hashicorp.com/terraform/cli/config/config-file). + +**`terraform.bicep`** + +```yaml +param token string + +resource terraform 'System.Resources/terraform@2025-08-01-preview' = { + name: 'terraform' + terraformCLI: { + url: 'https:///terraform_1.5.7_linux_amd64.zip' + checksum: 'sha256:37f2d497ea512324d59b50ebf1b58a6fcc2a2828d638a4f6fdb1f41af00140f3' + } + terraformrc: { + provider_installation: { + network_mirror: { + path: 'https:///' + include: ["*"] + } + direct: { + exclude: ["azurerm"] + } + } + // Equivilent to recipeConfig.authentication on Environments today + credentials: { + : { + secret: providerSecret.id + } + } + // Environment variables injected into the Terraform process + // Equivilent to recipeConfig.env on Environments today + env: { + TF_REGISTRY_CLIENT_TIMEOUT: 15 + } + } +} + +resource providerSecret 'Applications.Core/secretStores@2023-10-01-preview' = { + name: 'providerSecret' + properties: { + type: 'generic' + data: { + token: { + value: token + } + } + } +} +``` + +The Terraform resource intentionally does not include these terraformrc properties because they do not apply when using Terraform from within Radius: + +* `credentials_helper` +* `provider_installation.filesystem_mirror` +* `disable_checkpoint` +* `disable_checkpoint_signature` +* `plugin_cache_dir` + +#### Alternatives Considered + +We considered extending the `recipeConfig` property of the Environment resource to include the provider mirror. The challenge with this approach is that the `recipeConfig` is on the Environment. As with Recipe Packs, the Environment resource needs to be as slim as possible because there will be hundreds of environments but only one configuration for Terraform. + +We also considered installing Terraform during Radius installation. The benefit of the proposed approach here are: + +* Enables platform engineers to install Terraform after Radius has been installed +* Enables platform engineers to upgrade Radius without knowing the Terraform installation instructions +* Bundles all Terraform installation and configuration into one user action +* Enables a clear path to future Terraform functionality. In the future, the Terraform resource can be enhanced with the [Terraform backend configuration](https://developer.hashicorp.com/terraform/language/backend). + +### User Story 8 – Upgrading Terraform + +***As a platform engineer, I need to upgrade the version of Terraform used by Radius*** + +The platform engineer modifies the already deployed Terraform resource and redeploys the resource. + +First, the platform engineer gets the existing resource using the new `bicep` output option. + +```bash +$ rad resource list System.Resources/terrform +RESOURCE TYPE GROUP STATE +tf System.Resources/terrform radius-system Succeeded + +$ rad resource show System.Resources/terrform -o bicep > terraform.bicep +``` + +The platform engineer then edits the version and checksum. + +```diff + terraformCLI: { +- url: 'https:///terraform_1.5.7_linux_amd64.zip' ++ url: 'https:///terraform_1.9.1_linux_amd64.zip' +- checksum: 'sha256:37f2d497ea512324d59b50ebf1b58a6fcc2a2828d638a4f6fdb1f41af00140f3' ++ checksum: 'sha256:f1426fccbf2500202b37993ef6b92e1fc60d114dd32c79bfadbc843929b2c7e2' +``` + +Then redeploys the Terraform resource. + +```bash +$ rad deploy terraform.bicep +``` + +As before, when the Terraform resource is deployed, Radius: + +* Downloads the specified Terraform binary and validates it against the provided checksum +* Installs the Terraform binary in the appropriate location within the Radius control plane +* Performs a `terraform init` and reports any output to the platform engineer + +### User Story 9 – Uninstall Terraform + +***As a platform engineer, I need to remove all Terraform installations from my environment, including Radius*** + +Terraform is a third-party solution which the platform engineer installs into Radius. Given this, Radius must enable platform engineers to remove Terraform as well. + +The platform engineer deletes the Terraform resource. + +```bash +$ rad resource delete System.Resources/terraform tf +``` + +Radius deletes the Terraform binary from the Radius control plane. + +## Scenario 3 – Additional offline configurations + +### User Story 10 – Specify trusted certificate authority + +***As a platform engineer, I need provide my organization's certificate authority certificate when installing Radius*** + +The platform engineer installs Radius using the CLI with override for the various settings. + +```bash +rad install kubernetes \ + --set-file global.rootCA.cert= \ + --set global.rootCA.mountPath=/etc/ssl/certs \ + ... +``` + +When specified, the certificate is stored in `/etc/ssl/certs` on the container file system. + +> [!IMPORTANT] +> +> This is existing functionality. It just needs to be tested to ensure the CA certificate is used by Terraform to validate TLS when downloading providers, modules, and configurations. + +## Scenario 3 – Upgrading Radius + +### User Story 11 – Upgrade Radius via CLI + +***As a platform engineer, I need to upgrade Radius offline using the Radius CLI*** + +The platform engineer uses the `rad upgrade` command with similar arguments when installing. + +```bash +rad upgrade kubernetes \ + --chart \ + --set image.repository=/ + --skip-contour-install +``` + +### User Story 12 – Upgrade Radius via Helm + +***As a platform engineer, I need to upgrade Radius using Helm*** + +The platform engineer upgrades Radius using the same `helm upgrade` command as user story 4. + +## Summary of changes +| Priority | Size | Description | +| -------- | ---- | ------------------------------------------------------------ | +| p0 | S | Specifying the `image.repository` in the Radius Helm chart | +| p0 | S | Specifying `--skip-contour-install` | +| p1 | S | Documentation updates for installing the Radius CLI manually | +| p1 | L | Downloading the Terraform binary, validating the checksum, and installing Terraform via a new Terraform resource type | +| p1 | M | Setting the Terraform CLI configuration based on the new Terraform resource | +| p1 | S | New `rad resource show --output bicep` output option | +| p1 | M | Upgrade the Terraform binary when the Terraform resource is modified | +| p1 | M | Uninstall Terraform when the Terraform resource is deleted | +| p1 | M | `install.sh` supports offline installation | +| p1 | M | `install.sh` creates `bicepconfig.json` | +| p1 | M | `install.sh` installs `rad-bicep` | +| p2 | M | `rad bicep download` is removed | +| p2 | M | `rad terraform` commands | +