diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index e28a6f9f..720b5556 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -56,6 +56,7 @@ spec: ✨ - base-branch: integration-test/branch-3/base ✨ - target-branch: integration-test/branch-3/target ✨ - secrets-folder: ./secrets +✨ - preinstall-folder: ./preinstall ✨ - output-folder: ./output ✨ - argocd-namespace: argocd ✨ - repo: dag-andersen/argocd-diff-preview diff --git a/cmd/main.go b/cmd/main.go index c28bbef8..7084171f 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -216,7 +216,7 @@ func run(cfg *Config) error { var argocdInstallationDuration time.Duration if cfg.CreateCluster { // Install Argo CD - duration, err := argocd.Install(cfg.Debug, cfg.SecretsFolder) + duration, err := argocd.Install(cfg.Debug, cfg.PreinstallFolder, cfg.SecretsFolder) if err != nil { log.Error().Msgf("❌ Failed to install Argo CD") return err diff --git a/cmd/options.go b/cmd/options.go index 76d3a648..f3bf7187 100644 --- a/cmd/options.go +++ b/cmd/options.go @@ -54,6 +54,7 @@ var ( DefaultBaseBranch = "main" DefaultOutputFolder = "./output" DefaultSecretsFolder = "./secrets" + DefaultPreinstallFolder = "./preinstall" DefaultCluster = "auto" DefaultClusterName = "argocd-diff-preview" DefaultKindOptions = "" @@ -103,6 +104,7 @@ type RawOptions struct { RepoRegex string `mapstructure:"repo-regex"` OutputFolder string `mapstructure:"output-folder"` SecretsFolder string `mapstructure:"secrets-folder"` + PreinstallFolder string `mapstructure:"preinstall-folder"` CreateCluster bool `mapstructure:"create-cluster"` ClusterType string `mapstructure:"cluster"` ClusterName string `mapstructure:"cluster-name"` @@ -153,6 +155,7 @@ type Config struct { RepoSelector repository.Selector OutputFolder string SecretsFolder string + PreinstallFolder string CreateCluster bool ClusterName string KindOptions string @@ -252,6 +255,7 @@ func Parse() *Config { viper.SetDefault("base-branch", DefaultBaseBranch) viper.SetDefault("output-folder", DefaultOutputFolder) viper.SetDefault("secrets-folder", DefaultSecretsFolder) + viper.SetDefault("preinstall-folder", DefaultPreinstallFolder) viper.SetDefault("create-cluster", DefaultCreateCluster) viper.SetDefault("watch-if-no-watch-pattern-found", DefaultWatchIfNoWatchPatternFound) viper.SetDefault("ignore-invalid-watch-pattern", DefaultIgnoreInvalidWatchPattern) @@ -312,6 +316,7 @@ func Parse() *Config { // Folders rootCmd.Flags().StringP("output-folder", "o", DefaultOutputFolder, "Output folder where the diff will be saved") rootCmd.Flags().StringP("secrets-folder", "s", DefaultSecretsFolder, "Secrets folder where the secrets are read from") + rootCmd.Flags().String("preinstall-folder", DefaultPreinstallFolder, "Folder containing Kubernetes manifests to apply before Argo CD is installed") // Cluster related rootCmd.Flags().Bool("create-cluster", DefaultCreateCluster, "Create a new cluster if it doesn't exist") @@ -397,6 +402,7 @@ func (o *RawOptions) ToConfig() (*Config, error) { TargetBranch: o.TargetBranch, OutputFolder: o.OutputFolder, SecretsFolder: o.SecretsFolder, + PreinstallFolder: o.PreinstallFolder, CreateCluster: o.CreateCluster, ClusterName: o.ClusterName, KindOptions: o.KindOptions, @@ -702,6 +708,7 @@ func (o *Config) LogConfig() { log.Info().Msgf("✨ - base-branch: %s", o.BaseBranch) log.Info().Msgf("✨ - target-branch: %s", o.TargetBranch) log.Info().Msgf("✨ - secrets-folder: %s", o.SecretsFolder) + log.Info().Msgf("✨ - preinstall-folder: %s", o.PreinstallFolder) log.Info().Msgf("✨ - output-folder: %s", o.OutputFolder) log.Info().Msgf("✨ - argocd-namespace: %s", o.ArgocdNamespace) if o.ArgocdConfigPath != DefaultArgocdConfigPath { diff --git a/docs/getting-started/pre-installing-crds.md b/docs/getting-started/pre-installing-crds.md new file mode 100644 index 00000000..93582517 --- /dev/null +++ b/docs/getting-started/pre-installing-crds.md @@ -0,0 +1,78 @@ +# Pre-installing CRDs + +Some applications need certain Custom Resource Definitions (CRDs) to be installed before a proper render can happen. The ephemeral cluster created by `argocd-diff-preview` only contains the Kubernetes APIs provided by the cluster itself and the resources installed with Argo CD. It does not automatically contain the CRDs from your destination cluster. + +Use the preinstall folder to apply CRDs and other cluster prerequisites before Argo CD is installed and Applications are rendered. + +## Prepare the preinstall folder + +Create a directory and place the required CRD manifests in it: + +```bash +mkdir -p preinstall +cp external-secrets-crd.yaml preinstall/ +``` + +Every file directly inside the directory is applied in filename order. Files may contain multiple YAML documents. Subdirectories are not traversed. + +If resources depend on one another, use filename prefixes to control their order: + +```text +preinstall/ +├── 00-external-secrets-crd.yaml +├── 10-cert-manager-crds.yaml +└── 20-other-prerequisites.yaml +``` + +## Docker + +Mount the directory at `/preinstall`: + +```bash +docker run \ + --network=host \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -v $(pwd)/main:/base-branch \ + -v $(pwd)/pull-request:/target-branch \ + -v $(pwd)/preinstall:/preinstall \ + -v $(pwd)/output:/output \ + -e TARGET_BRANCH=refs/pull/123/merge \ + -e REPO=example/repository \ + dagandersen/argocd-diff-preview:latest +``` + +The default preinstall path inside the container is `/preinstall`, so no additional option is required. + +## Standalone binary + +The standalone binary reads manifests from `./preinstall` by default: + +```bash +argocd-diff-preview \ + --repo example/repository \ + --base-branch main \ + --target-branch feature/my-change +``` + +Use `--preinstall-folder` to select a different directory: + +```bash +argocd-diff-preview \ + --repo example/repository \ + --base-branch main \ + --target-branch feature/my-change \ + --preinstall-folder ./cluster-prerequisites +``` + +The equivalent environment variable is `PREINSTALL_FOLDER`. + +## Why install CRDs? + +Installing the CRDs makes their APIs available to rendering and Kubernetes discovery in the ephemeral cluster. This is useful when: + +- A Helm chart checks `.Capabilities.APIVersions` before rendering custom resources. +- Custom resources use the same name in different namespaces. +- The tool needs to determine whether a custom resource is namespaced or cluster-scoped. +- Other resources must exist before Argo CD or an Application can be rendered correctly. + +Without an installed CRD, an unknown custom resource is treated as namespaced. This safe default prevents namespaces from being removed and same-named resources from being silently combined. Pre-installing the CRD still provides the most accurate result because the tool can discover its actual scope. diff --git a/docs/how-it-works.md b/docs/how-it-works.md index 78b1e399..931b01ac 100644 --- a/docs/how-it-works.md +++ b/docs/how-it-works.md @@ -66,8 +66,8 @@ For each Application or ApplicationSet found, it applies the following modificat --- -!!! note "Steps 4-6: Ephemeral Cluster Mode Only" - Steps 4, 5, and 6 only apply when using the default ephemeral cluster mode. If you're connecting to a pre-installed Argo CD instance with `--create-cluster=false`, these steps are skipped and the tool connects directly to your pre-installed Argo CD. +!!! note "Steps 4-7: Ephemeral Cluster Mode Only" + Steps 4, 5, 6, and 7 only apply when using the default ephemeral cluster mode. If you're connecting to a pre-installed Argo CD instance with `--create-cluster=false`, these steps are skipped and the tool connects directly to your pre-installed Argo CD. --- @@ -162,7 +162,26 @@ kubectl apply -f /secrets --- -## Step 7: Generate Applications from ApplicationSets +## Step 7: Apply preinstall manifests + +Some applications need certain Custom Resource Definitions (CRDs) to be installed before a proper render can happen. The ephemeral cluster does not automatically contain the CRDs from your destination cluster. + +Kubernetes manifests in the folder configured by `--preinstall-folder` are applied before Argo CD is installed and before Applications are rendered. The default is `./preinstall` for the standalone binary and `/preinstall` in the container. + +Installing the required CRDs makes their APIs available to rendering and lets the tool discover whether each custom resource is namespaced or cluster-scoped. The folder can also contain other cluster prerequisites needed during rendering. + +See [Pre-installing CRDs](./getting-started/pre-installing-crds.md) for setup instructions, Docker and binary examples, and details about file ordering. + +`argocd-diff-preview` practically performs: + +```bash +kubectl apply -f /preinstall +``` + + +--- + +## Step 8: Generate Applications from ApplicationSets For each ApplicationSet found, it generates the applications using the Argo CD CLI (or API depending on the `--render-method`): @@ -176,7 +195,7 @@ The newly generated applications also go through `Step 2` and `Step 3` (filterin --- -## Step 8: Apply applications to the cluster +## Step 9: Apply applications to the cluster The patched applications are applied to the cluster. @@ -190,7 +209,7 @@ At this point, Argo CD starts processing each application - rendering the applic --- -## Step 9: Wait for Argo CD to render +## Step 10: Wait for Argo CD to render The tool will repeatedly check the status of each application and extract the rendered manifests as they become ready. @@ -204,7 +223,7 @@ It practically just waits for the Application to look like this: --- -## Step 10: Extract rendered manifests +## Step 11: Extract rendered manifests Once applications are ready, the tool extracts the rendered manifests using the Argo CD CLI (or API depending on the `--render-method`): @@ -216,7 +235,7 @@ This returns the exact YAML generated by the applications - fully rendered with --- -## Step 11: Generate the diff +## Step 12: Generate the diff
@@ -233,7 +252,7 @@ The diff respects the `--diff-ignore` option to filter out noisy changes. Docume
---
-## Step 12: Output the results
+## Step 13: Output the results
The tool writes several files to the output folder (`./output/` by default):
diff --git a/docs/options.md b/docs/options.md
index a44f5897..e9db0c66 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -63,6 +63,7 @@ argocd-diff-preview [FLAGS] [OPTIONS] (--repo