diff --git a/cli/cmd/argocd.go b/cli/cmd/argocd.go index 65156d9d..002aeb3d 100644 --- a/cli/cmd/argocd.go +++ b/cli/cmd/argocd.go @@ -5,10 +5,12 @@ package cmd import ( "fmt" + "os" packageio "github.com/codesphere-cloud/cs-go/pkg/io" "github.com/codesphere-cloud/oms/internal/installer" "github.com/spf13/cobra" + "golang.org/x/term" ) // InstallArgoCDCmd represents the argocd command @@ -19,31 +21,28 @@ type InstallArgoCDCmd struct { type InstallArgoCDOpts struct { *GlobalOptions - Version string - DatacenterId string - GitPassword string - RegistryPassword string - FullInstall bool - ForceConflicts bool - RepoURL string - ValueFiles []string + Version string + DatacenterId string + RegistryURL string + FullInstall bool + ForceConflicts bool + RepoURL string + ValueFiles []string } func (c *InstallArgoCDCmd) RunE(_ *cobra.Command, args []string) error { - if c.Opts.FullInstall { - requiredFlags := map[string]string{ - "git-password": c.Opts.GitPassword, - "registry-password": c.Opts.RegistryPassword, - "dc-id": c.Opts.DatacenterId, - } + var ociPassword, gitPassword string - for flagName, value := range requiredFlags { - if value == "" { - return fmt.Errorf("flag --%s is required when --deploy-dc-config is true", flagName) - } + if c.Opts.FullInstall { + pw, err := resolveOCIPassword() + if err != nil { + return err } + ociPassword = pw + gitPassword = os.Getenv("OMS_GIT_PASSWORD") } - install, err := installer.NewArgoCD(c.Opts.Version, c.Opts.DatacenterId, c.Opts.RegistryPassword, c.Opts.GitPassword, c.Opts.FullInstall, c.Opts.ForceConflicts, c.Opts.RepoURL, c.Opts.ValueFiles) + + install, err := installer.NewArgoCD(c.Opts.Version, c.Opts.DatacenterId, ociPassword, c.Opts.RegistryURL, gitPassword, c.Opts.FullInstall, c.Opts.ForceConflicts, c.Opts.RepoURL, c.Opts.ValueFiles) if err != nil { return fmt.Errorf("failed to initialize ArgoCD installer: %w", err) } @@ -55,23 +54,61 @@ func (c *InstallArgoCDCmd) RunE(_ *cobra.Command, args []string) error { return nil } +// resolveOCIPassword reads the OCI registry password from the OMS_REGISTRY_PASSWORD +// environment variable, or prompts the user interactively if not set. +func resolveOCIPassword() (string, error) { + if pw := os.Getenv("OMS_REGISTRY_PASSWORD"); pw != "" { + return pw, nil + } + + if !term.IsTerminal(int(os.Stdin.Fd())) { + return "", fmt.Errorf("OMS_REGISTRY_PASSWORD must be set in non-interactive environments") + } + + fmt.Print("OCI registry password/token: ") + pw, err := term.ReadPassword(int(os.Stdin.Fd())) + fmt.Println() + if err != nil { + return "", fmt.Errorf("failed to read password: %w", err) + } + if len(pw) == 0 { + return "", fmt.Errorf("password is required; set OMS_REGISTRY_PASSWORD or enter it when prompted") + } + return string(pw), nil +} + func AddArgoCDCmd(parentCmd *cobra.Command, opts *GlobalOptions) { argocd := InstallArgoCDCmd{ cmd: &cobra.Command{ Use: "argocd", Short: "Install an ArgoCD helm release", - Long: packageio.Long(`Install an ArgoCD helm release`), - Example: formatExamples("install ArgoCD", []packageio.Example{ - {Cmd: "", Desc: "Install an ArgoCD helm release of chart https://argoproj.github.io/argo-helm/argo-cd "}, - {Cmd: "--version ", Desc: "Version of the ArgoCD helm chart to install"}, + Long: packageio.Long(`Install or upgrade the ArgoCD helm release. + + When --deploy-dc-config is set, Codesphere-managed resources are applied after + the chart install/upgrade: + - AppProjects (always) + - Helm OCI registry secret (always, requires OMS_REGISTRY_PASSWORD) + - Local cluster secret (only if --dc-id is provided) + - Git repo credentials (only if OMS_GIT_PASSWORD env var is set) + + Use --registry-url to point to a custom or mirrored OCI registry (defaults + to ghcr.io/codesphere-cloud/charts). + + Environment variables: + OMS_REGISTRY_PASSWORD Password/token for the Helm OCI registry (required for --deploy-dc-config) + OMS_GIT_PASSWORD Password/token for git repo access (optional)`), + Example: formatExamples("beta install argocd", []packageio.Example{ + {Cmd: "", Desc: "Install ArgoCD helm chart only"}, + {Cmd: "--version 7.8.0", Desc: "Install a specific chart version"}, + {Cmd: "--deploy-dc-config", Desc: "Install chart and apply Codesphere resources (prompts for OCI password)"}, + {Cmd: "--deploy-dc-config --dc-id 0", Desc: "Also register the local cluster as dc-0"}, }), }, } - argocd.cmd.Flags().StringVar(&argocd.Opts.GitPassword, "git-password", "", "Password/token to read from the git repo where ArgoCD Application manifests are stored") - argocd.cmd.Flags().StringVar(&argocd.Opts.RegistryPassword, "registry-password", "", "Password/token to read from the OCI registry (e.g. ghcr.io) where Helm chart artifacts are stored") - argocd.cmd.Flags().StringVar(&argocd.Opts.DatacenterId, "dc-id", "", "Codesphere Datacenter ID where this ArgoCD is installed") + argocd.cmd.Flags().StringVar(&argocd.Opts.DatacenterId, "dc-id", "", "Codesphere Datacenter ID (optional, registers local cluster in ArgoCD)") + argocd.cmd.Flags().StringVar(&argocd.Opts.RegistryURL, "registry-url", "ghcr.io/codesphere-cloud/charts", "OCI registry URL for the Helm chart repository") argocd.cmd.Flags().StringVarP(&argocd.Opts.Version, "version", "v", "", "Version of the ArgoCD helm chart to install") - argocd.cmd.Flags().BoolVar(&argocd.Opts.FullInstall, "deploy-dc-config", false, "Install Codesphere-managed resources (AppProjects, Repo Creds, ...) after installing the chart") + argocd.cmd.Flags().BoolVar(&argocd.Opts.FullInstall, "deploy-dc-config", false, "Apply Codesphere-managed resources (AppProjects, Repo Creds, ...) after installing the chart") argocd.cmd.Flags().StringArrayVarP(&argocd.Opts.ValueFiles, "values", "f", nil, "Specify values in a YAML file (can be specified multiple times)") argocd.cmd.Flags().BoolVar(&argocd.Opts.ForceConflicts, "force-conflicts", false, "Force field ownership conflicts during upgrade (sets server-side apply ForceConflicts)") argocd.cmd.Flags().StringVar(&argocd.Opts.RepoURL, "repo", "", "Helm chart repository URL; supports HTTP (default: https://argoproj.github.io/argo-helm) and OCI (e.g. oci://ghcr.io/argoproj/argo-helm)") diff --git a/docs/oms_beta_install_argocd.md b/docs/oms_beta_install_argocd.md index 06a8e8d5..d13d4be9 100644 --- a/docs/oms_beta_install_argocd.md +++ b/docs/oms_beta_install_argocd.md @@ -4,7 +4,21 @@ Install an ArgoCD helm release ### Synopsis -Install an ArgoCD helm release +Install or upgrade the ArgoCD helm release. + +When --deploy-dc-config is set, Codesphere-managed resources are applied after +the chart install/upgrade: + - AppProjects (always) + - Helm OCI registry secret (always, requires OMS_REGISTRY_PASSWORD) + - Local cluster secret (only if --dc-id is provided) + - Git repo credentials (only if OMS_GIT_PASSWORD env var is set) + +Use --registry-url to point to a custom or mirrored OCI registry (defaults +to ghcr.io/codesphere-cloud/charts). + +Environment variables: + OMS_REGISTRY_PASSWORD Password/token for the Helm OCI registry (required for --deploy-dc-config) + OMS_GIT_PASSWORD Password/token for git repo access (optional) ``` oms beta install argocd [flags] @@ -13,26 +27,31 @@ oms beta install argocd [flags] ### Examples ``` -# Install an ArgoCD helm release of chart https://argoproj.github.io/argo-helm/argo-cd -$ oms install ArgoCD +# Install ArgoCD helm chart only +$ oms beta install argocd + +# Install a specific chart version +$ oms beta install argocd --version 7.8.0 + +# Install chart and apply Codesphere resources (prompts for OCI password) +$ oms beta install argocd --deploy-dc-config -# Version of the ArgoCD helm chart to install -$ oms install ArgoCD --version +# Also register the local cluster as dc-0 +$ oms beta install argocd --deploy-dc-config --dc-id 0 ``` ### Options ``` - --dc-id string Codesphere Datacenter ID where this ArgoCD is installed - --deploy-dc-config Install Codesphere-managed resources (AppProjects, Repo Creds, ...) after installing the chart - --force-conflicts Force field ownership conflicts during upgrade (sets server-side apply ForceConflicts) - --git-password string Password/token to read from the git repo where ArgoCD Application manifests are stored - -h, --help help for argocd - --registry-password string Password/token to read from the OCI registry (e.g. ghcr.io) where Helm chart artifacts are stored - --repo string Helm chart repository URL; supports HTTP (default: https://argoproj.github.io/argo-helm) and OCI (e.g. oci://ghcr.io/argoproj/argo-helm) - -f, --values stringArray Specify values in a YAML file (can be specified multiple times) - -v, --version string Version of the ArgoCD helm chart to install + --dc-id string Codesphere Datacenter ID (optional, registers local cluster in ArgoCD) + --deploy-dc-config Apply Codesphere-managed resources (AppProjects, Repo Creds, ...) after installing the chart + --force-conflicts Force field ownership conflicts during upgrade (sets server-side apply ForceConflicts) + -h, --help help for argocd + --registry-url string OCI registry URL for the Helm chart repository (default "ghcr.io/codesphere-cloud/charts") + --repo string Helm chart repository URL; supports HTTP (default: https://argoproj.github.io/argo-helm) and OCI (e.g. oci://ghcr.io/argoproj/argo-helm) + -f, --values stringArray Specify values in a YAML file (can be specified multiple times) + -v, --version string Version of the ArgoCD helm chart to install ``` ### SEE ALSO diff --git a/internal/installer/argocd.go b/internal/installer/argocd.go index 9a7b1afb..c76a70ab 100644 --- a/internal/installer/argocd.go +++ b/internal/installer/argocd.go @@ -23,6 +23,7 @@ type ArgoCD struct { Version string DatacenterId string OciPassword string + OciRegistryURL string GitPassword string FullInstall bool ForceConflicts bool @@ -32,13 +33,13 @@ type ArgoCD struct { Resources ArgoCDResources } -func NewArgoCD(version string, dcId string, passwordOCI string, passwordGit string, fullInstall bool, forceConflicts bool, repoURL string, valueFiles []string) (*ArgoCD, error) { +func NewArgoCD(version string, dcId string, passwordOCI string, ociRegistryURL string, passwordGit string, fullInstall bool, forceConflicts bool, repoURL string, valueFiles []string) (*ArgoCD, error) { helm, err := NewHelmClient("argocd") if err != nil { return nil, fmt.Errorf("init helm client failed: %w", err) } - resources, err := NewArgoCDResources(dcId, passwordOCI, passwordGit) + resources, err := NewArgoCDResources(dcId, passwordOCI, ociRegistryURL, passwordGit) if err != nil { return nil, fmt.Errorf("init argocd resources client failed: %w", err) } @@ -46,6 +47,7 @@ func NewArgoCD(version string, dcId string, passwordOCI string, passwordGit stri Version: version, DatacenterId: dcId, OciPassword: passwordOCI, + OciRegistryURL: ociRegistryURL, GitPassword: passwordGit, FullInstall: fullInstall, ForceConflicts: forceConflicts, diff --git a/internal/installer/argocd_resources.go b/internal/installer/argocd_resources.go index 25f8d655..b54284a7 100644 --- a/internal/installer/argocd_resources.go +++ b/internal/installer/argocd_resources.go @@ -6,6 +6,7 @@ package installer import ( "context" _ "embed" + "errors" "fmt" "log" @@ -23,23 +24,25 @@ type argoCDResources struct { clientset kubernetes.Interface dynClient dynamic.Interface - DatacenterId string - OciPassword string - GitPassword string + DatacenterId string + OciPassword string + OciRegistryURL string + GitPassword string } -func NewArgoCDResources(dataCenterId string, ociPassword string, gitPassword string) (ArgoCDResources, error) { +func NewArgoCDResources(dataCenterId string, ociPassword string, ociRegistryURL string, gitPassword string) (ArgoCDResources, error) { clientset, dynClient, err := k8s.NewClients() if err != nil { return nil, fmt.Errorf("creating kubernetes clients: %w", err) } return &argoCDResources{ - clientset: clientset, - dynClient: dynClient, - DatacenterId: dataCenterId, - OciPassword: ociPassword, - GitPassword: gitPassword, + clientset: clientset, + dynClient: dynClient, + DatacenterId: dataCenterId, + OciPassword: ociPassword, + OciRegistryURL: ociRegistryURL, + GitPassword: gitPassword, }, nil } @@ -60,16 +63,24 @@ func (a *argoCDResources) ApplyAll(ctx context.Context) error { return fmt.Errorf("applying app projects: %w", err) } - if err := a.applyLocalCluster(ctx); err != nil { - return fmt.Errorf("applying local cluster secret: %w", err) + if a.DatacenterId != "" { + if err := a.applyLocalCluster(ctx); err != nil { + return fmt.Errorf("applying local cluster secret: %w", err) + } + } + + if a.OciPassword == "" { + return errors.New("OCI registry password is required but not set") } if err := a.applyHelmRegistrySecret(ctx); err != nil { return fmt.Errorf("applying helm registry secret: %w", err) } - if err := a.applyGitRepoSecret(ctx); err != nil { - return fmt.Errorf("applying git repo secret: %w", err) + if a.GitPassword != "" { + if err := a.applyGitRepoSecret(ctx); err != nil { + return fmt.Errorf("applying git repo secret: %w", err) + } } return nil @@ -110,6 +121,7 @@ func (a *argoCDResources) applyHelmRegistrySecret(ctx context.Context) error { log.Println("Applying helm registry secret... ") rendered, err := k8s.RenderTemplate(helmRegistryTpl, map[string]string{ "SECRET_CODESPHERE_OCI_READ": a.OciPassword, + "OCI_REGISTRY_URL": a.OciRegistryURL, }) if err != nil { return fmt.Errorf("rendering helm registry template: %w", err) diff --git a/internal/installer/manifests/argocd/app-projects.yaml b/internal/installer/manifests/argocd/app-projects.yaml index 34c6e47c..f34917eb 100644 --- a/internal/installer/manifests/argocd/app-projects.yaml +++ b/internal/installer/manifests/argocd/app-projects.yaml @@ -72,19 +72,3 @@ spec: groups: - ops@codesphere.com - development@codesphere.com ---- -# yaml-language-server: $schema=https://raw.githubusercontent.com/datreeio/CRDs-catalog/refs/heads/main/argoproj.io/appproject_v1alpha1.json -# The default project has the most permissive settings and needs to be explicitly restricted -# https://argo-cd.readthedocs.io/en/stable/user-guide/projects/#the-default-project -apiVersion: argoproj.io/v1alpha1 -kind: AppProject -metadata: - name: default - namespace: argocd -spec: - sourceRepos: [] - sourceNamespaces: [] - destinations: [] - namespaceResourceBlacklist: - - group: "*" - kind: "*" diff --git a/internal/installer/manifests/argocd/repo-helm-oci.yaml.tpl b/internal/installer/manifests/argocd/repo-helm-oci.yaml.tpl index 7874c30a..2b1c9cb6 100644 --- a/internal/installer/manifests/argocd/repo-helm-oci.yaml.tpl +++ b/internal/installer/manifests/argocd/repo-helm-oci.yaml.tpl @@ -8,7 +8,7 @@ metadata: argocd.argoproj.io/secret-type: repository stringData: name: codesphere-charts - url: ghcr.io/codesphere-cloud/charts + url: "${OCI_REGISTRY_URL}" type: helm username: github password: "${SECRET_CODESPHERE_OCI_READ}"