Skip to content
Merged
91 changes: 64 additions & 27 deletions cli/cmd/argocd.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Comment thread
Jcing95 marked this conversation as resolved.
}
Expand All @@ -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 <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)")
Expand Down
47 changes: 33 additions & 14 deletions docs/oms_beta_install_argocd.md
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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 <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
Expand Down
6 changes: 4 additions & 2 deletions internal/installer/argocd.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type ArgoCD struct {
Version string
DatacenterId string
OciPassword string
OciRegistryURL string
GitPassword string
FullInstall bool
ForceConflicts bool
Expand All @@ -32,20 +33,21 @@ 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)
}
return &ArgoCD{
Version: version,
DatacenterId: dcId,
OciPassword: passwordOCI,
OciRegistryURL: ociRegistryURL,
GitPassword: passwordGit,
FullInstall: fullInstall,
ForceConflicts: forceConflicts,
Expand Down
38 changes: 25 additions & 13 deletions internal/installer/argocd_resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package installer
import (
"context"
_ "embed"
"errors"
"fmt"
"log"

Expand All @@ -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
}

Expand All @@ -60,16 +63,24 @@ func (a *argoCDResources) ApplyAll(ctx context.Context) error {
return fmt.Errorf("applying app projects: %w", err)
Comment thread
Jcing95 marked this conversation as resolved.
}

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 {
Comment thread
schrodit marked this conversation as resolved.
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 {
Comment thread
schrodit marked this conversation as resolved.
return fmt.Errorf("applying helm registry secret: %w", err)
}

Comment thread
Jcing95 marked this conversation as resolved.
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
Expand Down Expand Up @@ -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)
Expand Down
16 changes: 0 additions & 16 deletions internal/installer/manifests/argocd/app-projects.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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: "*"
2 changes: 1 addition & 1 deletion internal/installer/manifests/argocd/repo-helm-oci.yaml.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
Expand Down
Loading