Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion cli/cmd/argocd.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ type InstallArgoCDOpts struct {
GitPassword string
RegistryPassword string
FullInstall bool
ForceConflicts bool
RepoURL string
ValueFiles []string
}

func (c *InstallArgoCDCmd) RunE(_ *cobra.Command, args []string) error {
Expand All @@ -39,7 +42,7 @@ func (c *InstallArgoCDCmd) RunE(_ *cobra.Command, args []string) error {
}
}
}
install, err := installer.NewArgoCD(c.Opts.Version, c.Opts.DatacenterId, c.Opts.RegistryPassword, c.Opts.GitPassword, c.Opts.FullInstall)
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)
if err != nil {
return fmt.Errorf("failed to initialize ArgoCD installer: %w", err)
}
Expand Down Expand Up @@ -68,6 +71,9 @@ func AddArgoCDCmd(parentCmd *cobra.Command, opts *GlobalOptions) {
argocd.cmd.Flags().StringVar(&argocd.Opts.DatacenterId, "dc-id", "", "Codesphere Datacenter ID where this ArgoCD is installed")
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().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)")
Comment thread
schrodit marked this conversation as resolved.
argocd.cmd.RunE = argocd.RunE

AddCmd(parentCmd, argocd.cmd)
Expand Down
3 changes: 3 additions & 0 deletions docs/oms_beta_install_argocd.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,12 @@ $ oms install ArgoCD --version <version>
```
--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
```

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ require (
require (
github.com/DATA-DOG/go-sqlmock v1.5.2
github.com/google/go-github/v74 v74.0.0
github.com/google/go-github/v88 v88.0.0
github.com/lib/pq v1.12.3
github.com/rook/rook/pkg/apis v0.0.0-20260526191009-8dd1778655f0
)
Expand Down Expand Up @@ -554,6 +553,7 @@ require (
github.com/google/btree v1.1.3 // indirect
github.com/google/gnostic-models v0.7.1 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/go-github/v88 v88.0.0 // indirect
github.com/google/go-licenses/v2 v2.0.1 // indirect
github.com/google/go-querystring v1.2.0 // indirect
github.com/google/licenseclassifier/v2 v2.0.0 // indirect
Expand Down
110 changes: 79 additions & 31 deletions internal/installer/argocd.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,32 @@ import (
"context"
"fmt"
"log"
"os"
"strings"

"github.com/Masterminds/semver/v3"
"helm.sh/helm/v4/pkg/action"
"helm.sh/helm/v4/pkg/chart/common/util"
"helm.sh/helm/v4/pkg/cli"
"helm.sh/helm/v4/pkg/cli/values"
"helm.sh/helm/v4/pkg/getter"
)

const argoCDDefaultRepoURL = "https://argoproj.github.io/argo-helm"

// ArgoCD holds the user-facing configuration for the install/upgrade command.
type ArgoCD struct {
Version string
DatacenterId string
OciPassword string
GitPassword string
FullInstall bool
Helm HelmClient // inject a real or mock client
Resources ArgoCDResources
Version string
DatacenterId string
OciPassword string
GitPassword string
FullInstall bool
ForceConflicts bool
RepoURL string // defaults to argoCDDefaultRepoURL if empty
ValueFiles []string
Helm HelmClient // inject a real or mock client
Resources ArgoCDResources
}

func NewArgoCD(version string, dcId string, passwordOCI string, passwordGit string, fullInstall bool) (*ArgoCD, error) {
settings := cli.New()
actionConfig := new(action.Configuration)
if err := actionConfig.Init(settings.RESTClientGetter(), "argocd", os.Getenv("HELM_DRIVER")); err != nil {
return nil, fmt.Errorf("init helm client failed: %w", err)
}
func NewArgoCD(version string, dcId string, passwordOCI 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)
Expand All @@ -41,19 +43,26 @@ func NewArgoCD(version string, dcId string, passwordOCI string, passwordGit stri
return nil, fmt.Errorf("init argocd resources client failed: %w", err)
}
return &ArgoCD{
Version: version,
DatacenterId: dcId,
OciPassword: passwordOCI,
GitPassword: passwordGit,
FullInstall: fullInstall,
Helm: helm,
Resources: resources,
Version: version,
DatacenterId: dcId,
OciPassword: passwordOCI,
GitPassword: passwordGit,
FullInstall: fullInstall,
ForceConflicts: forceConflicts,
RepoURL: repoURL,
ValueFiles: valueFiles,
Helm: helm,
Resources: resources,
}, nil
}

// Install is the top-level orchestrator. It delegates every Helm interaction
// to the HelmClient interface, keeping this function short and testable.
func (a *ArgoCD) Install() error {
if err := a.validateRepoURL(); err != nil {
return err
}

if a.Version != "" {
log.Printf("Installing/Upgrading ArgoCD helm chart version %s\n", a.Version)
} else {
Expand All @@ -62,18 +71,29 @@ func (a *ArgoCD) Install() error {

ctx := context.Background()

vals, err := (&values.Options{
ValueFiles: a.ValueFiles,
}).MergeValues(getter.All(cli.New()))
Comment thread
schrodit marked this conversation as resolved.
if err != nil {
return fmt.Errorf("loading values files: %w", err)
}

// Apply our defaults underneath the user-provided values so value files can
// override them. MergeTables gives precedence to dst (the user values).
defaults := map[string]any{
"dex": map[string]any{"enabled": false},
}
vals = util.MergeTables(vals, defaults)

chartName, repoURL := a.resolveChartRef("argo-cd")
cfg := ChartConfig{
ReleaseName: "argocd",
ChartName: "argo-cd",
RepoURL: "https://argoproj.github.io/argo-helm",
ChartName: chartName,
RepoURL: repoURL,
Namespace: "argocd",
Version: a.Version,
CreateNamespace: true,
Values: map[string]interface{}{
"dex": map[string]interface{}{
"enabled": false,
},
},
Values: vals,
}

existing, err := a.Helm.FindRelease(cfg.Namespace, cfg.ReleaseName)
Expand Down Expand Up @@ -107,7 +127,7 @@ func (a *ArgoCD) Install() error {
func (a *ArgoCD) install(ctx context.Context, cfg ChartConfig) error {
log.Println("No existing ArgoCD release found, performing fresh install")

if err := a.Helm.InstallChart(ctx, cfg); err != nil {
if err := a.Helm.InstallChart(ctx, cfg, InstallChartOptions{ForceConflicts: a.ForceConflicts}); err != nil {
return err
}

Expand Down Expand Up @@ -145,7 +165,7 @@ func (a *ArgoCD) upgrade(ctx context.Context, cfg ChartConfig, existing *Release
log.Printf("Upgrading ArgoCD from %s to latest\n", existing.InstalledVersion)
}

if err := a.Helm.UpgradeChart(ctx, cfg, UpgradeChartOptions{}); err != nil {
if err := a.Helm.UpgradeChart(ctx, cfg, UpgradeChartOptions{ForceConflicts: a.ForceConflicts}); err != nil {
return err
}

Expand All @@ -157,6 +177,34 @@ func (a *ArgoCD) upgrade(ctx context.Context, cfg ChartConfig, existing *Release
return nil
}

// validateRepoURL ensures a non-empty RepoURL uses a supported scheme.
func (a *ArgoCD) validateRepoURL() error {
if a.RepoURL == "" {
return nil
}
for _, prefix := range []string{"http://", "https://", "oci://"} {
if strings.HasPrefix(a.RepoURL, prefix) {
return nil
}
}
return fmt.Errorf("invalid repo URL %q: must start with http://, https://, or oci://", a.RepoURL)
}

// resolveChartRef returns the (chartName, repoURL) pair to use in ChartConfig.
// For OCI repos the full reference is passed as chartName and repoURL is empty,
// because helm's LocateChart expects "oci://<registry>/<repo>/<chart>" as the
// chart name with no separate RepoURL.
func (a *ArgoCD) resolveChartRef(chartName string) (string, string) {
repoURL := a.RepoURL
if repoURL == "" {
repoURL = argoCDDefaultRepoURL
}
if strings.HasPrefix(repoURL, "oci://") {
return strings.TrimRight(repoURL, "/") + "/" + chartName, ""
}
return chartName, repoURL
}

func (a *ArgoCD) showPostInstallHints() {
log.Println(`To get ArgoCD admin password:`)
log.Println(` kubectl get secrets/argocd-initial-admin-secret -nargocd -ojson | jq -r ".data.password" | base64 -d`)
Expand Down
Loading
Loading