From f38e00f948cbd46f9c257348f1c91439eb9df426 Mon Sep 17 00:00:00 2001 From: bachgg Date: Fri, 27 Feb 2026 14:45:59 +0100 Subject: [PATCH 01/33] first successful install --- cli/cmd/beta.go | 1 + cli/cmd/install_argocd.go | 52 +++++++++++++ go.mod | 54 +++++++++++++ go.sum | 147 +++++++++++++++++++++++++++++++++++ internal/installer/argocd.go | 83 ++++++++++++++++++++ 5 files changed, 337 insertions(+) create mode 100644 cli/cmd/install_argocd.go create mode 100644 internal/installer/argocd.go diff --git a/cli/cmd/beta.go b/cli/cmd/beta.go index a280d559..3a7e0061 100644 --- a/cli/cmd/beta.go +++ b/cli/cmd/beta.go @@ -25,4 +25,5 @@ func AddBetaCmd(rootCmd *cobra.Command, opts *GlobalOptions) { AddExtendCmd(beta.cmd, opts) AddBootstrapGcpCmd(beta.cmd, opts) + AddInstallArgoCD(beta.cmd, opts) } diff --git a/cli/cmd/install_argocd.go b/cli/cmd/install_argocd.go new file mode 100644 index 00000000..d311ec92 --- /dev/null +++ b/cli/cmd/install_argocd.go @@ -0,0 +1,52 @@ +// Copyright (c) Codesphere Inc. +// SPDX-License-Identifier: Apache-2.0 + +package cmd + +import ( + "fmt" + + "github.com/codesphere-cloud/cs-go/pkg/io" + packageio "github.com/codesphere-cloud/cs-go/pkg/io" + "github.com/codesphere-cloud/oms/internal/installer" + "github.com/spf13/cobra" +) + +// InstallArgoCD represents the argocd command +type InstallArgoCD struct { + cmd *cobra.Command + Opts InstallArgoCDOpts +} +type InstallArgoCDOpts struct { + *GlobalOptions + Version string + Package string + Config string + Force bool +} + +func (c *InstallArgoCD) RunE(_ *cobra.Command, args []string) error { + argocd := installer.NewArgoCD() + err := argocd.Install() + if err != nil { + return fmt.Errorf("failed to install chart ArgoCD: %w", err) + } + + return nil +} +func AddInstallArgoCD(install *cobra.Command, opts *GlobalOptions) { + argocd := InstallArgoCD{ + cmd: &cobra.Command{ + Use: "argocd", + Short: "Install an ArgoCD helm release", + Long: io.Long(`Install an ArgoCD helm release`), + Example: formatExamplesWithBinary("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"}, + }, "oms-cli"), + }, + } + argocd.cmd.Flags().StringVarP(&argocd.Opts.Version, "version", "v", "", "Version of the ArgoCD helm chart to install") + install.AddCommand(argocd.cmd) + argocd.cmd.RunE = argocd.RunE +} diff --git a/go.mod b/go.mod index ebe78c97..5424b62a 100644 --- a/go.mod +++ b/go.mod @@ -22,6 +22,7 @@ require ( golang.org/x/term v0.40.0 google.golang.org/api v0.269.0 google.golang.org/grpc v1.79.1 + helm.sh/helm/v4 v4.1.1 ) require ( @@ -508,15 +509,29 @@ require ( ) require ( + github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/Masterminds/semver/v3 v3.4.0 // indirect + github.com/Masterminds/squirrel v1.5.4 // indirect + github.com/blang/semver/v4 v4.0.0 // indirect + github.com/chai2010/gettext-go v1.0.2 // indirect github.com/clipperhouse/stringish v0.1.1 // indirect github.com/clipperhouse/uax29/v2 v2.4.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/denis-tingaikin/go-header v0.5.0 // indirect + github.com/dylibso/observe-sdk/go v0.0.0-20240819160327-2d926c5d788a // indirect + github.com/emicklei/go-restful/v3 v3.12.2 // indirect + github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect + github.com/extism/go-sdk v1.7.1 // indirect + github.com/fluxcd/cli-utils v0.37.0-flux.1 // indirect + github.com/fxamacker/cbor/v2 v2.9.0 // indirect + github.com/go-errors/errors v1.5.1 // indirect + github.com/go-gorp/gorp/v3 v3.1.0 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d // indirect + github.com/google/btree v1.1.3 // indirect + github.com/google/gnostic-models v0.7.0 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/go-github/v83 v83.0.0 // indirect github.com/google/go-licenses/v2 v2.0.1 // indirect @@ -524,18 +539,38 @@ require ( github.com/google/licenseclassifier/v2 v2.0.0 // indirect github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83 // indirect github.com/goreleaser/go-shellwords v1.0.13 // indirect + github.com/gosuri/uitable v0.0.4 // indirect + github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect + github.com/ianlancetaylor/demangle v0.0.0-20250417193237-f615e6bd150b // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jmoiron/sqlx v1.4.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect + github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect + github.com/lib/pq v1.11.1 // indirect + github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/mattn/go-runewidth v0.0.19 // indirect + github.com/mitchellh/go-wordwrap v1.0.1 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect + github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/otiai10/copy v1.14.1 // indirect github.com/otiai10/mint v1.6.3 // indirect + github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect + github.com/rubenv/sql-migrate v1.8.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/stretchr/objx v0.5.3 // indirect + github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834 // indirect + github.com/tetratelabs/wazero v1.11.0 // indirect github.com/ulikunitz/xz v0.5.15 // indirect + github.com/x448/float16 v0.8.4 // indirect + github.com/xlab/treeprint v1.2.0 // indirect go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/proto/otlp v1.9.0 // indirect golang.org/x/mod v0.33.0 // indirect golang.org/x/net v0.50.0 // indirect golang.org/x/oauth2 v0.35.0 // indirect @@ -544,8 +579,27 @@ require ( golang.org/x/text v0.34.0 // indirect golang.org/x/tools v0.42.0 // indirect google.golang.org/protobuf v1.36.11 // indirect + gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/api v0.35.1 // indirect + k8s.io/apiextensions-apiserver v0.35.0 // indirect + k8s.io/apimachinery v0.35.1 // indirect + k8s.io/apiserver v0.35.0 // indirect + k8s.io/cli-runtime v0.35.1 // indirect + k8s.io/client-go v0.35.1 // indirect + k8s.io/component-base v0.35.0 // indirect k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e // indirect + k8s.io/kubectl v0.35.0 // indirect + k8s.io/utils v0.0.0-20260106112306-0fe9cd71b2f8 // indirect + oras.land/oras-go/v2 v2.6.0 // indirect + sigs.k8s.io/controller-runtime v0.22.4 // indirect + sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect + sigs.k8s.io/kustomize/api v0.20.1 // indirect + sigs.k8s.io/kustomize/kyaml v0.21.0 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.3.1 // indirect ) tool ( diff --git a/go.sum b/go.sum index d118f257..bc8b3c86 100644 --- a/go.sum +++ b/go.sum @@ -51,6 +51,7 @@ dev.gaijin.team/go/exhaustruct/v4 v4.0.0 h1:873r7aNneqoBB3IaFIzhvt2RFYTuHgmMjoKf dev.gaijin.team/go/exhaustruct/v4 v4.0.0/go.mod h1:aZ/k2o4Y05aMJtiux15x8iXaumE88YdiB0Ai4fXOzPI= dev.gaijin.team/go/golib v0.8.0 h1:BiDNudpoFizoU5VHdQUiabtHSt9fyPX11Fr4OU9PaUQ= dev.gaijin.team/go/golib v0.8.0/go.mod h1:c5fu7t1RSGMxSQgcUYO1sODbzsYnOCXJLmHeNG1Eb+0= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo= filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc= github.com/42wim/httpsig v1.2.3 h1:xb0YyWhkYj57SPtfSttIobJUPJZB9as1nsfo7KWVcEs= @@ -59,6 +60,8 @@ github.com/4meepo/tagalign v1.4.3 h1:Bnu7jGWwbfpAie2vyl63Zup5KuRv21olsPIha53BJr8 github.com/4meepo/tagalign v1.4.3/go.mod h1:00WwRjiuSbrRJnSVeGWPLp2epS5Q/l4UEy0apLLS37c= github.com/Abirdcfly/dupword v0.1.7 h1:2j8sInznrje4I0CMisSL6ipEBkeJUJAmK1/lfoNGWrQ= github.com/Abirdcfly/dupword v0.1.7/go.mod h1:K0DkBeOebJ4VyOICFdppB23Q0YMOgVafM0zYW0n9lF4= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230919221257-8b5d3ce2d11d h1:zjqpY4C7H15HjRPEenkS4SAn3Jy2eRRjkjZbGR30TOg= github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230919221257-8b5d3ce2d11d/go.mod h1:XNqJ7hv2kY++g8XEHREpi+JqZo3+0l+CH2egBVN4yqM= github.com/AdminBenni/iota-mixing v1.0.0 h1:Os6lpjG2dp/AE5fYBPAA1zfa2qMdCAWwPMCgpwKq7wo= @@ -132,6 +135,8 @@ github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= +github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Djarvur/go-err113 v0.1.1 h1:eHfopDqXRwAi+YmCUas75ZE0+hoBHJ2GQNLYRSxao4g= @@ -144,12 +149,16 @@ github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0 github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.54.0/go.mod h1:vB2GH9GAYYJTO3mEn8oYwzEdhlayZIdQz6zdzgUIRvA= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.54.0 h1:s0WlVbf9qpvkh1c/uDAPElam0WrL7fHRIidgZJ7UqZI= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.54.0/go.mod h1:Mf6O40IAyB9zR/1J8nGDDPirZQQPbYJni8Yisy7NTMc= +github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= +github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= +github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= +github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= @@ -274,6 +283,8 @@ github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4 github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/blizzy78/varnamelen v0.8.0 h1:oqSblyuQvFsW1hbBHh1zfwrKe3kcSj0rnXkKzsQ089M= github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkApdHeyESmyR7k= github.com/bluesky-social/indigo v0.0.0-20260106221649-6fcd9317e725 h1:gfrLAhE6PHun4MDypO/5hpnaHPd9Dbe9+JxZL0gC4ic= @@ -288,6 +299,8 @@ github.com/breml/errchkjson v0.4.1 h1:keFSS8D7A2T0haP9kzZTi7o26r7kE3vymjZNeNDRDw github.com/breml/errchkjson v0.4.1/go.mod h1:a23OvR6Qvcl7DG/Z4o0el6BRAjKnaReoPQFciAl9U3s= github.com/brunoga/deep v1.3.1 h1:bSrL6FhAZa6JlVv4vsi7Hg8SLwroDb1kgDERRVipBCo= github.com/brunoga/deep v1.3.1/go.mod h1:GDV6dnXqn80ezsLSZ5Wlv1PdKAWAO4L5PnKYtv2dgaI= +github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70= +github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/butuzov/ireturn v0.4.0 h1:+s76bF/PfeKEdbG8b54aCocxXmi0wvYdOVsWxVO7n8E= @@ -318,6 +331,8 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA github.com/certifi/gocertifi v0.0.0-20180118203423-deb3ae2ef261/go.mod h1:GJKEexRPVJrBSOjoqN5VNOIKJ5Q3RViH6eu3puDRwx4= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= +github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= github.com/charithe/durationcheck v0.0.11 h1:g1/EX1eIiKS57NTWsYtHDZ/APfeXKhye1DidBcABctk= github.com/charithe/durationcheck v0.0.11/go.mod h1:x5iZaixRNl8ctbM+3B2RrPG5t856TxRyVQEnbIEM2X4= github.com/charmbracelet/colorprofile v0.4.1 h1:a1lO03qTrSIRaK8c3JRxJDZOvhvIeSco3ej+ngLk1kk= @@ -376,6 +391,7 @@ github.com/containerd/stargz-snapshotter/estargz v0.18.2 h1:yXkZFYIzz3eoLwlTUZKz github.com/containerd/stargz-snapshotter/estargz v0.18.2/go.mod h1:XyVU5tcJ3PRpkA9XS2T5us6Eg35yM0214Y+wvrZTBrY= github.com/coreos/go-oidc/v3 v3.17.0 h1:hWBGaQfbi0iVviX4ibC7bk8OKT5qNr4klBaCHVNvehc= github.com/coreos/go-oidc/v3 v3.17.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8= +github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= @@ -413,6 +429,8 @@ github.com/dghubble/oauth1 v0.7.3 h1:EkEM/zMDMp3zOsX2DC/ZQ2vnEX3ELK0/l9kb+vs4ptE github.com/dghubble/oauth1 v0.7.3/go.mod h1:oxTe+az9NSMIucDPDCCtzJGsPhciJV33xocHfcR2sVY= github.com/dghubble/sling v1.4.2 h1:vs1HIGBbSl2SEALyU+irpYFLZMfc49Fp+jYryFebQjM= github.com/dghubble/sling v1.4.2/go.mod h1:o0arCOz0HwfqYQJLrRtqunaWOn4X6jxE/6ORKRpVTD4= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo= github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= github.com/digitorus/pkcs7 v0.0.0-20230713084857-e76b763bdc49/go.mod h1:SKVExuS+vpu2l9IoOc0RwqE7NYnb0JlcFHFnEJkVDzc= @@ -438,16 +456,22 @@ github.com/docker/docker-credential-helpers v0.9.4 h1:76ItO69/AP/V4yT9V4uuuItG0B github.com/docker/docker-credential-helpers v0.9.4/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c= github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/dylibso/observe-sdk/go v0.0.0-20240819160327-2d926c5d788a h1:UwSIFv5g5lIvbGgtf3tVwC7Ky9rmMFBp0RMs+6f6YqE= +github.com/dylibso/observe-sdk/go v0.0.0-20240819160327-2d926c5d788a/go.mod h1:C8DzXehI4zAbrdlbtOByKX6pfivJTBiV9Jjqv56Yd9Q= github.com/earthboundkid/versioninfo/v2 v2.24.1 h1:SJTMHaoUx3GzjjnUO1QzP3ZXK6Ee/nbWyCm58eY3oUg= github.com/earthboundkid/versioninfo/v2 v2.24.1/go.mod h1:VcWEooDEuyUJnMfbdTh0uFN4cfEIg+kHMuWB2CDCLjw= github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= +github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU= +github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -466,6 +490,10 @@ github.com/ettle/strcase v0.2.0 h1:fGNiVF21fHXpX1niBgk0aROov1LagYsOwV/xqKDKR/Q= github.com/ettle/strcase v0.2.0/go.mod h1:DajmHElDSaX76ITe3/VHVyMin4LWSJN5Z909Wp+ED1A= github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= +github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f h1:Wl78ApPPB2Wvf/TIe2xdyJxTlb6obmF18d8QdkxNDu4= +github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSYXu++VVOHnXeitef/D8n/6y4QV8uLHSFXX4NeXMGc= +github.com/extism/go-sdk v1.7.1 h1:lWJos6uY+tRFdlIHR+SJjwFDApY7OypS/2nMhiVQ9Sw= +github.com/extism/go-sdk v1.7.1/go.mod h1:IT+Xdg5AZM9hVtpFUA+uZCJMge/hbvshl8bwzLtFyKA= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/fatih/set v0.2.1 h1:nn2CaJyknWE/6txyUDGwysr3G5QC6xWB/PtVjPBbeaA= @@ -478,10 +506,16 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2 github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/firefart/nonamedreturns v1.0.6 h1:vmiBcKV/3EqKY3ZiPxCINmpS431OcE1S47AQUwhrg8E= github.com/firefart/nonamedreturns v1.0.6/go.mod h1:R8NisJnSIpvPWheCq0mNRXJok6D8h7fagJTF8EMEwCo= +github.com/fluxcd/cli-utils v0.37.0-flux.1 h1:k/VvPNT3tGa/l2N+qzHduaQr3GVbgoWS6nw7tGZz16w= +github.com/fluxcd/cli-utils v0.37.0-flux.1/go.mod h1:aND5wX3LuTFtB7eUT7vsWr8mmxRVSPR2Wkvbn0SqPfw= +github.com/foxcpp/go-mockdns v1.2.0 h1:omK3OrHRD1IWJz1FuFBCFquhXslXoF17OvBS6JPzZF0= +github.com/foxcpp/go-mockdns v1.2.0/go.mod h1:IhLeSFGed3mJIAXPH2aiRQB+kqz7oqu8ld2qVbOu7Wk= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= +github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo= github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA= github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw= @@ -503,6 +537,8 @@ github.com/go-chi/chi/v5 v5.2.4 h1:WtFKPHwlywe8Srng8j2BhOD9312j9cGUxG1SP4V2cR4= github.com/go-chi/chi/v5 v5.2.4/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0= github.com/go-critic/go-critic v0.14.3 h1:5R1qH2iFeo4I/RJU8vTezdqs08Egi4u5p6vOESA0pog= github.com/go-critic/go-critic v0.14.3/go.mod h1:xwntfW6SYAd7h1OqDzmN6hBX/JxsEKl5up/Y2bsxgVQ= +github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk= +github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI= github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= @@ -513,6 +549,8 @@ github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMj github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= github.com/go-git/go-git/v5 v5.16.5 h1:mdkuqblwr57kVfXri5TTH+nMFLNUxIj9Z7F5ykFbw5s= github.com/go-git/go-git/v5 v5.16.5/go.mod h1:QOMLpNf1qxuSY4StA/ArOdfFR2TrKEjJiye2kel2m+M= +github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs= +github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs= github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -520,6 +558,8 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= +github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= github.com/go-openapi/analysis v0.24.2 h1:6p7WXEuKy1llDgOH8FooVeO+Uq2za9qoAOq4ZN08B50= github.com/go-openapi/analysis v0.24.2/go.mod h1:x27OOHKANE0lutg2ml4kzYLoHGMKgRm1Cj2ijVOjJuE= github.com/go-openapi/errors v0.22.6 h1:eDxcf89O8odEnohIXwEjY1IB4ph5vmbUsBMsFNwXWPo= @@ -574,6 +614,7 @@ github.com/go-restruct/restruct v1.2.0-alpha h1:2Lp474S/9660+SJjpVxoKuWX09JsXHSr github.com/go-restruct/restruct v1.2.0-alpha/go.mod h1:KqrpKpn4M8OLznErihXTGLlsXFGeLxHUrLRRI/1YjGk= github.com/go-rod/rod v0.116.2 h1:A5t2Ky2A+5eD/ZJQr1EfsQSe5rms5Xof/qj296e+ZqA= github.com/go-rod/rod v0.116.2/go.mod h1:H+CMO9SCNc2TJ2WfrG+pKhITz57uGNYU43qYHh438Mg= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= @@ -664,8 +705,12 @@ github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e h1:ai0EfmVYE2b github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e/go.mod h1:Vrn4B5oR9qRwM+f54koyeH3yzphlecwERs0el27Fr/s= github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e h1:gD6P7NEo7Eqtt0ssnqSJNNndxe69DOQ24A5h7+i3KpM= github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e/go.mod h1:h+wZwLjUTJnm/P2rwlbJdRPZXOzaT36/FwnPnY2inzc= +github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/certificate-transparency-go v1.3.2 h1:9ahSNZF2o7SYMaKaXhAumVEzXB2QaayzII9C8rv7v+A= github.com/google/certificate-transparency-go v1.3.2/go.mod h1:H5FpMUaGa5Ab2+KCYsxg6sELw3Flkl7pGZzWdBoYLXs= +github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= +github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -692,6 +737,7 @@ github.com/google/go-replayers/grpcreplay v1.3.0 h1:1Keyy0m1sIpqstQmgz307zhiJ1pV github.com/google/go-replayers/grpcreplay v1.3.0/go.mod h1:v6NgKtkijC0d3e3RW8il6Sy5sqRVUwoQa4mHOGEy8DI= github.com/google/go-replayers/httpreplay v1.2.0 h1:VM1wEyyjaoU53BwrOnaf9VhAyQQEEioJvFYxYcLRKzk= github.com/google/go-replayers/httpreplay v1.2.0/go.mod h1:WahEFFZZ7a1P4VM1qEeHy+tME4bwyqPcwWbNlUI1Mcg= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/ko v0.18.1 h1:F2WDFIi/eZe5thmFCuk/uH0eVr7ilWCThl+UoTHEKSk= @@ -738,6 +784,8 @@ github.com/goreleaser/nfpm/v2 v2.45.0 h1:rTqqX/vqvln4x7B2HmPL57gh21iIMqyxzP8goI3 github.com/goreleaser/nfpm/v2 v2.45.0/go.mod h1:O0h9bB68D39NTnM9rSOJhVCYxQk7sU8i04q2bzczFdk= github.com/goreleaser/quill v0.0.0-20251224035235-ab943733386f h1:2HQF/pifDK7XnmVhQi3OecdUcHLOaXIKVKscW8qKzCk= github.com/goreleaser/quill v0.0.0-20251224035235-ab943733386f/go.mod h1:Xp6aA14QqdPBg7UHToFag7mrjsV7XaKEpw1t6fDfT6M= +github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= +github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= @@ -754,8 +802,12 @@ github.com/gostaticanalysis/nilerr v0.1.2/go.mod h1:A19UHhoY3y8ahoL7YKz6sdjDtduw github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M= github.com/gostaticanalysis/testutil v0.5.0 h1:Dq4wT1DdTwTGCQQv3rl3IvD5Ld0E6HiY+3Zh0sUGqw8= github.com/gostaticanalysis/testutil v0.5.0/go.mod h1:OLQSbuM6zw2EvCcXTz1lVq5unyoNft372msDY0nY5Hs= +github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= +github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= github.com/graph-gophers/graphql-go v1.8.0 h1:NT05/H+PdH1/PONExlUycnhULYHBy98dxV63WYc0Ng8= github.com/graph-gophers/graphql-go v1.8.0/go.mod h1:23olKZ7duEvHlF/2ELEoSZaY1aNPfShjP782SOoNTyM= +github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= +github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg= @@ -789,6 +841,8 @@ github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bP github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru/arc/v2 v2.0.6 h1:4NU7uP5vSoK6TbaMj3NtY478TTAWLso/vL1gpNrInHg= +github.com/hashicorp/golang-lru/arc/v2 v2.0.6/go.mod h1:cfdDIX05DWvYV6/shsxDfa/OVcRieOt+q4FnM8x+Xno= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.1-vault-7 h1:ag5OxFVy3QYTFTJODRzTKVZ6xvdfLLCA1cy/Y6xGI0I= @@ -801,6 +855,8 @@ github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef h1:A9HsByNhogrvm9cWb github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/ianlancetaylor/demangle v0.0.0-20250417193237-f615e6bd150b h1:ogbOPx86mIhFy764gGkqnkFC8m5PJA7sPzlk9ppLVQA= +github.com/ianlancetaylor/demangle v0.0.0-20250417193237-f615e6bd150b/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= github.com/in-toto/attestation v1.1.2 h1:MBFn6lsMq6dptQZJBhalXTcWMb/aJy3V+GX3VYj/V1E= github.com/in-toto/attestation v1.1.2/go.mod h1:gYFddHMZj3DiQ0b62ltNi1Vj5rC879bTmBbrv9CRHpM= github.com/in-toto/in-toto-golang v0.9.0 h1:tHny7ac4KgtsfrG6ybU8gVOZux2H8jN05AXJ9EBM1XU= @@ -864,8 +920,12 @@ github.com/jjti/go-spancheck v0.6.5 h1:lmi7pKxa37oKYIMScialXUK6hP3iY5F1gu+mLBPgY github.com/jjti/go-spancheck v0.6.5/go.mod h1:aEogkeatBrbYsyW6y5TgDfihCulDYciL1B7rG2vSsrU= github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 h1:liMMTbpW34dhU4az1GN0pTPADwNmvoRSeoZ6PItiqnY= github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= +github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE= github.com/joshdk/go-junit v1.0.0/go.mod h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julz/importas v0.2.0 h1:y+MJN/UdL63QbFJHws9BVC5RpA2iq0kpjrFajTGivjQ= @@ -917,6 +977,10 @@ github.com/kunwardeep/paralleltest v1.0.15 h1:ZMk4Qt306tHIgKISHWFJAO1IDQJLc6uDyJ github.com/kunwardeep/paralleltest v1.0.15/go.mod h1:di4moFqtfz3ToSKxhNjhOZL+696QtJGCFe132CbBLGk= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= +github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= +github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= +github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= github.com/lasiar/canonicalheader v1.1.2 h1:vZ5uqwvDbyJCnMhmFYimgMZnJMjwljN5VGY0VKbMXb4= github.com/lasiar/canonicalheader v1.1.2/go.mod h1:qJCeLFS0G/QlLQ506T+Fk/fWMa2VmBUiEI2cuMK4djI= github.com/ldez/exptostd v0.4.5 h1:kv2ZGUVI6VwRfp/+bcQ6Nbx0ghFWcGIKInkG/oFn1aQ= @@ -935,6 +999,11 @@ github.com/leonklingele/grouper v1.1.2 h1:o1ARBDLOmmasUaNDesWqWCIFH3u7hoFlM84Yrj github.com/leonklingele/grouper v1.1.2/go.mod h1:6D0M/HVkhs2yRKRFZUoGjeDy7EZTfFBE9gl4kjmIGkA= github.com/letsencrypt/boulder v0.20260105.0 h1:P94haPlN1xm8MhIHSXbUu1cA0t0EoMhXQyMz/jLwR34= github.com/letsencrypt/boulder v0.20260105.0/go.mod h1:FWHD4EclPHIQ1y2AKEXyySrM3eKiwEyGzcwcupVEFyE= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.11.1 h1:wuChtj2hfsGmmx3nf1m7xC2XpK6OtelS2shMY+bGMtI= +github.com/lib/pq v1.11.1/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/lithammer/shortuuid v3.0.0+incompatible h1:NcD0xWW/MZYXEHa6ITy6kaXN5nwm/V115vj2YXfhS0w= github.com/lithammer/shortuuid v3.0.0+incompatible/go.mod h1:FR74pbAuElzOUuenUHTK2Tciko1/vKuIKS9dSkDrA4w= github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag= @@ -969,16 +1038,23 @@ github.com/mattn/go-mastodon v0.0.10 h1:wz1d/aCkJOIkz46iv4eAqXHVreUMxydY1xBWrPBd github.com/mattn/go-mastodon v0.0.10/go.mod h1:YBofeqh7G6s787787NQR8erBYz6fKDu+KNMrn5RuD6Y= github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw= github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A= +github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE= github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A= github.com/mgechev/revive v1.14.0 h1:CC2Ulb3kV7JFYt+izwORoS3VT/+Plb8BvslI/l1yZsc= github.com/mgechev/revive v1.14.0/go.mod h1:MvnujelCZBZCaoDv5B3foPo6WWgULSSFxvfxp7GsPfo= +github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA= +github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c h1:cqn374mizHuIWj+OSJCajGr/phAmuMug9qIX3l9CflE= github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= @@ -999,6 +1075,14 @@ github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= github.com/modelcontextprotocol/registry v1.4.1 h1:KLQM2SuNbsl2EpXHWEq9IJoNxXaYTQpqUx8b+RmyvXI= github.com/modelcontextprotocol/registry v1.4.1/go.mod h1:SmF5s9FkPsfBRXh2asdLocIinTmDadLjv2nmUr9UCDY= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= +github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= github.com/moricho/tparallel v0.3.2 h1:odr8aZVFA3NZrNybggMkYO3rgPRcqjeQUlBBFVxKHTI= github.com/moricho/tparallel v0.3.2/go.mod h1:OQ+K3b4Ln3l2TZveGCywybl68glfLEwFGqvnjok8b+U= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= @@ -1069,6 +1153,8 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0= github.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= @@ -1085,6 +1171,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f h1:VXTQfuJj9vKR4TCkEuWIckKvdHFeJH/huIFJ9/cXOB0= github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw= +github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY= +github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjzg= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -1108,6 +1196,12 @@ github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 h1:M8mH9eK4OUR4l github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567/go.mod h1:DWNGW8A4Y+GyBgPuaQJuWiy0XYftx4Xm/y5Jqk9I6VQ= github.com/raeperd/recvcheck v0.2.0 h1:GnU+NsbiCqdC2XX5+vMZzP+jAJC5fht7rcVTAhX74UI= github.com/raeperd/recvcheck v0.2.0/go.mod h1:n04eYkwIR0JbgD73wT8wL4JjPC3wm0nFtzBnWNocnYU= +github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 h1:EaDatTxkdHG+U3Bk4EUr+DZ7fOGwTfezUiUJMaIcaho= +github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5/go.mod h1:fyalQWdtzDBECAQFBJuQe5bzQ02jGd5Qcbgb97Flm7U= +github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 h1:EfpWLLCyXw8PSM2/XNJLjI3Pb27yVE+gIAfeqp8LUCc= +github.com/redis/go-redis/extra/redisotel/v9 v9.0.5/go.mod h1:WZjPDy7VNzn77AAfnAfVjZNvfJTYfPetfZk5yoSTLaQ= +github.com/redis/go-redis/v9 v9.17.2 h1:P2EGsA4qVIM3Pp+aPocCJ7DguDHhqrXNhVcEp4ViluI= +github.com/redis/go-redis/v9 v9.17.2/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -1116,6 +1210,8 @@ github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7 github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= +github.com/rubenv/sql-migrate v1.8.1 h1:EPNwCvjAowHI3TnZ+4fQu3a915OpnQoPAjTXCGOy2U0= +github.com/rubenv/sql-migrate v1.8.1/go.mod h1:BTIKBORjzyxZDS6dzoiw6eAFYJ1iNlGAtjn4LGeVjS8= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -1229,6 +1325,8 @@ github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+Q github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= @@ -1245,6 +1343,10 @@ github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3 h1:f+jULpR github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY= github.com/tetafro/godot v1.5.4 h1:u1ww+gqpRLiIA16yF2PV1CV1n/X3zhyezbNXC3E14Sg= github.com/tetafro/godot v1.5.4/go.mod h1:eOkMrVQurDui411nBY2FA05EYH01r14LuWY/NrVDVcU= +github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834 h1:ZF+QBjOI+tILZjBaFj3HgFonKXUcwgJ4djLb6i42S3Q= +github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834/go.mod h1:m9ymHTgNSEjuxvw8E7WWe4Pl4hZQHXONY8wE6dMLaRk= +github.com/tetratelabs/wazero v1.11.0 h1:+gKemEuKCTevU4d7ZTzlsvgd1uaToIDtlQlmNbwqYhA= +github.com/tetratelabs/wazero v1.11.0/go.mod h1:eV28rsN8Q+xwjogd7f4/Pp4xFxO7uOGbLcD/LzB1wiU= github.com/theupdateframework/go-tuf v0.7.0 h1:CqbQFrWo1ae3/I0UCblSbczevCCbS31Qvs5LdxRWqRI= github.com/theupdateframework/go-tuf v0.7.0/go.mod h1:uEB7WSY+7ZIugK6R1hiBMBjQftaFzn7ZCDJcp1tCUug= github.com/theupdateframework/go-tuf/v2 v2.4.1 h1:K6ewW064rKZCPkRo1W/CTbTtm/+IB4+coG1iNURAGCw= @@ -1304,6 +1406,8 @@ github.com/whyrusleeping/cbor-gen v0.3.1 h1:82ioxmhEYut7LBVGhGq8xoRkXPLElVuh5mV6 github.com/whyrusleeping/cbor-gen v0.3.1/go.mod h1:pM99HXyEbSQHcosHc0iW7YFmwnscr+t9Te4ibko05so= github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -1317,6 +1421,8 @@ github.com/xen0n/gosmopolitan v1.3.0 h1:zAZI1zefvo7gcpbCOrPSHJZJYA9ZgLfJqtKzZ5pH github.com/xen0n/gosmopolitan v1.3.0/go.mod h1:rckfr5T6o4lBtM1ga7mLGKZmLxswUoH1zxHgNXOsEt4= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= +github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= +github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/yagipy/maintidx v1.0.0 h1:h5NvIsCz+nRDapQ0exNv4aJ0yXSI0420omVANTv3GJM= @@ -1635,6 +1741,10 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo= +gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/mail.v2 v2.3.1 h1:WYFn/oANrAGP2C0dcV6/pbkPzv8yGzqTjPmTeO7qoXk= gopkg.in/mail.v2 v2.3.1/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw= gopkg.in/validator.v2 v2.0.1 h1:xF0KWyGWXm/LM2G1TrEjqOu4pa6coO9AlWSf3msVfDY= @@ -1643,27 +1753,64 @@ gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= +helm.sh/helm/v4 v4.1.1 h1:juO/Vack3pNUBCX0emMvHL1RL27CEWwGyCd3HyP3mPA= +helm.sh/helm/v4 v4.1.1/go.mod h1:yH4qpYvTNBTHnkRSenhi1m7oEFKoN6iK3/rYyFJ00IQ= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.7.0 h1:w6WUp1VbkqPEgLz4rkBzH/CSU6HkoqNLp6GstyTx3lU= honnef.co/go/tools v0.7.0/go.mod h1:pm29oPxeP3P82ISxZDgIYeOaf9ta6Pi0EWvCFoLG2vc= +k8s.io/api v0.35.1 h1:0PO/1FhlK/EQNVK5+txc4FuhQibV25VLSdLMmGpDE/Q= +k8s.io/api v0.35.1/go.mod h1:28uR9xlXWml9eT0uaGo6y71xK86JBELShLy4wR1XtxM= +k8s.io/apiextensions-apiserver v0.35.0 h1:3xHk2rTOdWXXJM+RDQZJvdx0yEOgC0FgQ1PlJatA5T4= +k8s.io/apiextensions-apiserver v0.35.0/go.mod h1:E1Ahk9SADaLQ4qtzYFkwUqusXTcaV2uw3l14aqpL2LU= +k8s.io/apimachinery v0.35.1 h1:yxO6gV555P1YV0SANtnTjXYfiivaTPvCTKX6w6qdDsU= +k8s.io/apimachinery v0.35.1/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns= +k8s.io/apiserver v0.35.0 h1:CUGo5o+7hW9GcAEF3x3usT3fX4f9r8xmgQeCBDaOgX4= +k8s.io/apiserver v0.35.0/go.mod h1:QUy1U4+PrzbJaM3XGu2tQ7U9A4udRRo5cyxkFX0GEds= +k8s.io/cli-runtime v0.35.1 h1:uKcXFe8J7AMAM4Gm2JDK4mp198dBEq2nyeYtO+JfGJE= +k8s.io/cli-runtime v0.35.1/go.mod h1:55/hiXIq1C8qIJ3WBrWxEwDLdHQYhBNRdZOz9f7yvTw= +k8s.io/client-go v0.35.1 h1:+eSfZHwuo/I19PaSxqumjqZ9l5XiTEKbIaJ+j1wLcLM= +k8s.io/client-go v0.35.1/go.mod h1:1p1KxDt3a0ruRfc/pG4qT/3oHmUj1AhSHEcxNSGg+OA= +k8s.io/component-base v0.35.0 h1:+yBrOhzri2S1BVqyVSvcM3PtPyx5GUxCK2tinZz1G94= +k8s.io/component-base v0.35.0/go.mod h1:85SCX4UCa6SCFt6p3IKAPej7jSnF3L8EbfSyMZayJR0= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e h1:iW9ChlU0cU16w8MpVYjXk12dqQ4BPFBEgif+ap7/hqQ= +k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= +k8s.io/kubectl v0.35.0 h1:cL/wJKHDe8E8+rP3G7avnymcMg6bH6JEcR5w5uo06wc= +k8s.io/kubectl v0.35.0/go.mod h1:VR5/TSkYyxZwrRwY5I5dDq6l5KXmiCb+9w8IKplk3Qo= +k8s.io/utils v0.0.0-20260106112306-0fe9cd71b2f8 h1:oV4uULAC2QPIdMQwjMaNIwykyhWhnhBwX40yd5h9u3U= +k8s.io/utils v0.0.0-20260106112306-0fe9cd71b2f8/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg= lukechampine.com/blake3 v1.4.1/go.mod h1:QFosUxmjB8mnrWFSNwKmvxHpfY72bmD2tQ0kBMM3kwo= mvdan.cc/gofumpt v0.9.2 h1:zsEMWL8SVKGHNztrx6uZrXdp7AX8r421Vvp23sz7ik4= mvdan.cc/gofumpt v0.9.2/go.mod h1:iB7Hn+ai8lPvofHd9ZFGVg2GOr8sBUw1QUWjNbmIL/s= mvdan.cc/unparam v0.0.0-20251027182757-5beb8c8f8f15 h1:ssMzja7PDPJV8FStj7hq9IKiuiKhgz9ErWw+m68e7DI= mvdan.cc/unparam v0.0.0-20251027182757-5beb8c8f8f15/go.mod h1:4M5MMXl2kW6fivUT6yRGpLLPNfuGtU2Z0cPvFquGDYU= +oras.land/oras-go/v2 v2.6.0 h1:X4ELRsiGkrbeox69+9tzTu492FMUu7zJQW6eJU+I2oc= +oras.land/oras-go/v2 v2.6.0/go.mod h1:magiQDfG6H1O9APp+rOsvCPcW1GD2MM7vgnKY0Y+u1o= +sigs.k8s.io/controller-runtime v0.22.4 h1:GEjV7KV3TY8e+tJ2LCTxUTanW4z/FmNB7l327UfMq9A= +sigs.k8s.io/controller-runtime v0.22.4/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/kind v0.31.0 h1:UcT4nzm+YM7YEbqiAKECk+b6dsvc/HRZZu9U0FolL1g= sigs.k8s.io/kind v0.31.0/go.mod h1:FSqriGaoTPruiXWfRnUXNykF8r2t+fHtK0P0m1AbGF8= +sigs.k8s.io/kustomize/api v0.20.1 h1:iWP1Ydh3/lmldBnH/S5RXgT98vWYMaTUL1ADcr+Sv7I= +sigs.k8s.io/kustomize/api v0.20.1/go.mod h1:t6hUFxO+Ph0VxIk1sKp1WS0dOjbPCtLJ4p8aADLwqjM= +sigs.k8s.io/kustomize/kyaml v0.21.0 h1:7mQAf3dUwf0wBerWJd8rXhVcnkk5Tvn/q91cGkaP6HQ= +sigs.k8s.io/kustomize/kyaml v0.21.0/go.mod h1:hmxADesM3yUN2vbA5z1/YTBnzLJ1dajdqpQonwBL1FQ= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/structured-merge-diff/v6 v6.3.1 h1:JrhdFMqOd/+3ByqlP2I45kTOZmTRLBUm5pvRjeheg7E= +sigs.k8s.io/structured-merge-diff/v6 v6.3.1/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= software.sslmate.com/src/go-pkcs12 v0.7.0 h1:Db8W44cB54TWD7stUFFSWxdfpdn6fZVcDl0w3R4RVM0= diff --git a/internal/installer/argocd.go b/internal/installer/argocd.go new file mode 100644 index 00000000..61d3d8e1 --- /dev/null +++ b/internal/installer/argocd.go @@ -0,0 +1,83 @@ +// Copyright (c) Codesphere Inc. +// SPDX-License-Identifier: Apache-2.0 + +package installer + +import ( + "context" + "fmt" + "log" + "os" + + "helm.sh/helm/v4/pkg/action" + "helm.sh/helm/v4/pkg/chart/loader" + "helm.sh/helm/v4/pkg/cli" +) + +type ArgoCDManager interface { + Install() error +} + +type ArgoCD struct { + Version string +} + +func NewArgoCD() ArgoCDManager { + return &ArgoCD{ + Version: "9.1.4", + } +} + +func (a *ArgoCD) Install() error { + settings := cli.New() + ctx := context.Background() + + actionConfig := new(action.Configuration) + if err := actionConfig.Init(settings.RESTClientGetter(), settings.Namespace(), os.Getenv("HELM_DRIVER")); err != nil { + log.Fatalf("Init failed: %v", err) + } + + // 2. Setup the Install Client + client := action.NewInstall(actionConfig) + client.ReleaseName = "argocd" + client.Namespace = "argocd" + client.CreateNamespace = true + client.DryRunStrategy = "none" + client.WaitStrategy = "watcher" + client.Version = "9.1.4" + // The repo URL must be provided so LocateChart knows where to look + client.ChartPathOptions.RepoURL = "https://argoproj.github.io/argo-helm" + + // 3. Locate and Load Chart + // This replaces the manual downloader/manager logic for standard charts + chartPath, err := client.ChartPathOptions.LocateChart("argo-cd", settings) + if err != nil { + log.Fatalf("LocateChart failed: %v", err) + } + + chartRequested, err := loader.Load(chartPath) + if err != nil { + log.Fatalf("Load failed: %v", err) + } + + // 4. Define Values (Example: enabling the UI) + vals := map[string]interface{}{ + "dex": map[string]interface{}{ + "enabled": false, + }, + "configs": map[string]interface{}{ + "secret": map[string]interface{}{ + "createSecret": true, + }, + }, + } + + // 5. Run Installation + _, err = client.RunWithContext(ctx, chartRequested, vals) + if err != nil { + log.Fatalf("Install failed: %v", err) + } + + fmt.Printf("Successfully installed Argo CD %s (Chart version: %s)\n", client.ReleaseName, client.Version) + return nil +} From 28ddb96d761f2a6270bb5328b1e31346d13035c7 Mon Sep 17 00:00:00 2001 From: bachgg Date: Fri, 27 Feb 2026 17:09:26 +0100 Subject: [PATCH 02/33] install argocd namespace --- cli/cmd/install_argocd.go | 3 +- internal/installer/argocd.go | 74 ++++++++++++++++++++++++++++++++---- 2 files changed, 69 insertions(+), 8 deletions(-) diff --git a/cli/cmd/install_argocd.go b/cli/cmd/install_argocd.go index d311ec92..9440a27b 100644 --- a/cli/cmd/install_argocd.go +++ b/cli/cmd/install_argocd.go @@ -27,7 +27,8 @@ type InstallArgoCDOpts struct { func (c *InstallArgoCD) RunE(_ *cobra.Command, args []string) error { argocd := installer.NewArgoCD() - err := argocd.Install() + err := argocd.PreInstall() + // err := argocd.Install() if err != nil { return fmt.Errorf("failed to install chart ArgoCD: %w", err) } diff --git a/internal/installer/argocd.go b/internal/installer/argocd.go index 61d3d8e1..95f1cfd3 100644 --- a/internal/installer/argocd.go +++ b/internal/installer/argocd.go @@ -8,14 +8,23 @@ import ( "fmt" "log" "os" + "path/filepath" "helm.sh/helm/v4/pkg/action" "helm.sh/helm/v4/pkg/chart/loader" "helm.sh/helm/v4/pkg/cli" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" ) type ArgoCDManager interface { + PreInstall() error Install() error + PostInstall() error } type ArgoCD struct { @@ -28,7 +37,63 @@ func NewArgoCD() ArgoCDManager { } } +func createArgocdNamespace() error { + home, _ := os.UserHomeDir() + kubeconfigPath := filepath.Join(home, ".kube", "config") + + config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( + &clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfigPath}, + &clientcmd.ConfigOverrides{CurrentContext: ""}, // Empty string means the current select context + ).ClientConfig() + + if err != nil { + return fmt.Errorf("Error loading current context: %v", err) + } + + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + return err + } + namespace := "argocd" + _, err = clientset.CoreV1().Namespaces().Get(context.TODO(), namespace, metav1.GetOptions{}) + + if err == nil { + fmt.Printf("Namespace %s already exists\n", namespace) + return nil + } + + if errors.IsNotFound(err) { + ns := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}} + _, err = clientset.CoreV1().Namespaces().Create(context.TODO(), ns, metav1.CreateOptions{}) + + if err != nil { + return fmt.Errorf("Error: %v\n", err) + } else { + log.Println("Created namespace 'argocd' using the active context.") + } + } + return err +} + +// Install resources needed by ArgoCD +func (a *ArgoCD) PreInstall() error { + err := createArgocdNamespace() + if err != nil { + return fmt.Errorf("Error creating namespace argocd: %v", err) + } + + // TODO: argocd secret + return nil +} + +// PostInstall implements ArgoCDManager. +func (a *ArgoCD) PostInstall() error { + panic("unimplemented") +} + +// Install the ArgoCD chart func (a *ArgoCD) Install() error { + log.Println("Installing ArgoCD helm chart version %s", a.Version) settings := cli.New() ctx := context.Background() @@ -37,19 +102,16 @@ func (a *ArgoCD) Install() error { log.Fatalf("Init failed: %v", err) } - // 2. Setup the Install Client client := action.NewInstall(actionConfig) client.ReleaseName = "argocd" client.Namespace = "argocd" client.CreateNamespace = true client.DryRunStrategy = "none" client.WaitStrategy = "watcher" - client.Version = "9.1.4" - // The repo URL must be provided so LocateChart knows where to look + client.Version = a.Version + client.ChartPathOptions.RepoURL = "https://argoproj.github.io/argo-helm" - // 3. Locate and Load Chart - // This replaces the manual downloader/manager logic for standard charts chartPath, err := client.ChartPathOptions.LocateChart("argo-cd", settings) if err != nil { log.Fatalf("LocateChart failed: %v", err) @@ -60,7 +122,6 @@ func (a *ArgoCD) Install() error { log.Fatalf("Load failed: %v", err) } - // 4. Define Values (Example: enabling the UI) vals := map[string]interface{}{ "dex": map[string]interface{}{ "enabled": false, @@ -72,7 +133,6 @@ func (a *ArgoCD) Install() error { }, } - // 5. Run Installation _, err = client.RunWithContext(ctx, chartRequested, vals) if err != nil { log.Fatalf("Install failed: %v", err) From b3937c5bc5a411420543f25a5d3989835ad07fb3 Mon Sep 17 00:00:00 2001 From: bachgg Date: Mon, 2 Mar 2026 11:32:31 +0100 Subject: [PATCH 03/33] add subcommands --- cli/cmd/argocd.go | 87 ++++++++++++++++++++++++++++++++++++ cli/cmd/beta.go | 2 +- cli/cmd/install_argocd.go | 53 ---------------------- internal/installer/argocd.go | 15 +------ 4 files changed, 90 insertions(+), 67 deletions(-) create mode 100644 cli/cmd/argocd.go delete mode 100644 cli/cmd/install_argocd.go diff --git a/cli/cmd/argocd.go b/cli/cmd/argocd.go new file mode 100644 index 00000000..99f1e8ae --- /dev/null +++ b/cli/cmd/argocd.go @@ -0,0 +1,87 @@ +// Copyright (c) Codesphere Inc. +// SPDX-License-Identifier: Apache-2.0 + +package cmd + +import ( + "fmt" + + "github.com/codesphere-cloud/cs-go/pkg/io" + packageio "github.com/codesphere-cloud/cs-go/pkg/io" + "github.com/codesphere-cloud/oms/internal/installer" + "github.com/spf13/cobra" +) + +type ArgoCDCmd struct { + cmd *cobra.Command +} + +// InstallArgoCDCmd represents the argocd command +type InstallArgoCDCmd struct { + cmd *cobra.Command + Opts InstallArgoCDOpts +} + +type InstallArgoCDOpts struct { + *GlobalOptions + Version string + Package string + Config string + Force bool +} + +func (c *InstallArgoCDCmd) RunE(_ *cobra.Command, args []string) error { + argocd := installer.NewArgoCD() + // err := argocd.PreInstall() + err := argocd.Install() + if err != nil { + return fmt.Errorf("failed to install chart ArgoCD: %w", err) + } + + return nil +} + +type GetAdminPasswordCmd struct { + cmd *cobra.Command +} + +func (c *GetAdminPasswordCmd) RunE(_ *cobra.Command, args []string) error { + return nil +} + +func AddArgoCDCmd(parentCmd *cobra.Command, opts *GlobalOptions) { + argocd := ArgoCDCmd{ + cmd: &cobra.Command{ + Use: "argocd", + Short: "Commands to interact with ArgoCD", + }, + } + + // argocd install + install := InstallArgoCDCmd{ + cmd: &cobra.Command{ + Use: "install", + Short: "Install an ArgoCD helm release", + Long: io.Long(`Install an ArgoCD helm release`), + Example: formatExamplesWithBinary("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"}, + }, "oms-cli"), + }, + } + install.cmd.Flags().StringVarP(&install.Opts.Version, "version", "v", "", "Version of the ArgoCD helm chart to install") + install.cmd.RunE = install.RunE + argocd.cmd.AddCommand(install.cmd) + + // argocd get-admin-password + getAdminPassword := GetAdminPasswordCmd{ + cmd: &cobra.Command{ + Use: "get-admin-password", + Short: "Retrieve the initial ArgoCD admin password", + }, + } + getAdminPassword.cmd.RunE = getAdminPassword.RunE + argocd.cmd.AddCommand(getAdminPassword.cmd) + + parentCmd.AddCommand(argocd.cmd) +} diff --git a/cli/cmd/beta.go b/cli/cmd/beta.go index 3a7e0061..b56a5a22 100644 --- a/cli/cmd/beta.go +++ b/cli/cmd/beta.go @@ -25,5 +25,5 @@ func AddBetaCmd(rootCmd *cobra.Command, opts *GlobalOptions) { AddExtendCmd(beta.cmd, opts) AddBootstrapGcpCmd(beta.cmd, opts) - AddInstallArgoCD(beta.cmd, opts) + AddArgoCDCmd(beta.cmd, opts) } diff --git a/cli/cmd/install_argocd.go b/cli/cmd/install_argocd.go deleted file mode 100644 index 9440a27b..00000000 --- a/cli/cmd/install_argocd.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) Codesphere Inc. -// SPDX-License-Identifier: Apache-2.0 - -package cmd - -import ( - "fmt" - - "github.com/codesphere-cloud/cs-go/pkg/io" - packageio "github.com/codesphere-cloud/cs-go/pkg/io" - "github.com/codesphere-cloud/oms/internal/installer" - "github.com/spf13/cobra" -) - -// InstallArgoCD represents the argocd command -type InstallArgoCD struct { - cmd *cobra.Command - Opts InstallArgoCDOpts -} -type InstallArgoCDOpts struct { - *GlobalOptions - Version string - Package string - Config string - Force bool -} - -func (c *InstallArgoCD) RunE(_ *cobra.Command, args []string) error { - argocd := installer.NewArgoCD() - err := argocd.PreInstall() - // err := argocd.Install() - if err != nil { - return fmt.Errorf("failed to install chart ArgoCD: %w", err) - } - - return nil -} -func AddInstallArgoCD(install *cobra.Command, opts *GlobalOptions) { - argocd := InstallArgoCD{ - cmd: &cobra.Command{ - Use: "argocd", - Short: "Install an ArgoCD helm release", - Long: io.Long(`Install an ArgoCD helm release`), - Example: formatExamplesWithBinary("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"}, - }, "oms-cli"), - }, - } - argocd.cmd.Flags().StringVarP(&argocd.Opts.Version, "version", "v", "", "Version of the ArgoCD helm chart to install") - install.AddCommand(argocd.cmd) - argocd.cmd.RunE = argocd.RunE -} diff --git a/internal/installer/argocd.go b/internal/installer/argocd.go index 95f1cfd3..b3223d52 100644 --- a/internal/installer/argocd.go +++ b/internal/installer/argocd.go @@ -77,13 +77,7 @@ func createArgocdNamespace() error { // Install resources needed by ArgoCD func (a *ArgoCD) PreInstall() error { - err := createArgocdNamespace() - if err != nil { - return fmt.Errorf("Error creating namespace argocd: %v", err) - } - - // TODO: argocd secret - return nil + panic("unimplemented") } // PostInstall implements ArgoCDManager. @@ -93,7 +87,7 @@ func (a *ArgoCD) PostInstall() error { // Install the ArgoCD chart func (a *ArgoCD) Install() error { - log.Println("Installing ArgoCD helm chart version %s", a.Version) + log.Printf("Installing ArgoCD helm chart version %s\n", a.Version) settings := cli.New() ctx := context.Background() @@ -126,11 +120,6 @@ func (a *ArgoCD) Install() error { "dex": map[string]interface{}{ "enabled": false, }, - "configs": map[string]interface{}{ - "secret": map[string]interface{}{ - "createSecret": true, - }, - }, } _, err = client.RunWithContext(ctx, chartRequested, vals) From b49cbc301ef9118b656c7b89f73ae6f911ca48c8 Mon Sep 17 00:00:00 2001 From: bachgg Date: Mon, 2 Mar 2026 12:10:38 +0100 Subject: [PATCH 04/33] prevent downgrading --- cli/cmd/argocd.go | 6 +- internal/installer/argocd.go | 136 +++++++++++++++++++++++++++++------ 2 files changed, 117 insertions(+), 25 deletions(-) diff --git a/cli/cmd/argocd.go b/cli/cmd/argocd.go index 99f1e8ae..810b38d7 100644 --- a/cli/cmd/argocd.go +++ b/cli/cmd/argocd.go @@ -5,6 +5,7 @@ package cmd import ( "fmt" + "log" "github.com/codesphere-cloud/cs-go/pkg/io" packageio "github.com/codesphere-cloud/cs-go/pkg/io" @@ -31,9 +32,9 @@ type InstallArgoCDOpts struct { } func (c *InstallArgoCDCmd) RunE(_ *cobra.Command, args []string) error { - argocd := installer.NewArgoCD() + install := installer.NewArgoCD(c.Opts.Version) // err := argocd.PreInstall() - err := argocd.Install() + err := install.Install() if err != nil { return fmt.Errorf("failed to install chart ArgoCD: %w", err) } @@ -46,6 +47,7 @@ type GetAdminPasswordCmd struct { } func (c *GetAdminPasswordCmd) RunE(_ *cobra.Command, args []string) error { + log.Println("Not implemented") return nil } diff --git a/internal/installer/argocd.go b/internal/installer/argocd.go index b3223d52..76b35e44 100644 --- a/internal/installer/argocd.go +++ b/internal/installer/argocd.go @@ -10,9 +10,12 @@ import ( "os" "path/filepath" + "github.com/Masterminds/semver/v3" "helm.sh/helm/v4/pkg/action" + "helm.sh/helm/v4/pkg/chart" "helm.sh/helm/v4/pkg/chart/loader" "helm.sh/helm/v4/pkg/cli" + "helm.sh/helm/v4/pkg/release" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" @@ -31,9 +34,9 @@ type ArgoCD struct { Version string } -func NewArgoCD() ArgoCDManager { +func NewArgoCD(version string) ArgoCDManager { return &ArgoCD{ - Version: "9.1.4", + Version: version, } } @@ -87,46 +90,133 @@ func (a *ArgoCD) PostInstall() error { // Install the ArgoCD chart func (a *ArgoCD) Install() error { - log.Printf("Installing ArgoCD helm chart version %s\n", a.Version) + if a.Version == "" { + a.Version = "9.1.4" + } + log.Printf("Installing/Upgrading ArgoCD helm chart version %s\n", a.Version) + settings := cli.New() ctx := context.Background() actionConfig := new(action.Configuration) - if err := actionConfig.Init(settings.RESTClientGetter(), settings.Namespace(), os.Getenv("HELM_DRIVER")); err != nil { - log.Fatalf("Init failed: %v", err) + if err := actionConfig.Init(settings.RESTClientGetter(), "argocd", os.Getenv("HELM_DRIVER")); err != nil { + return fmt.Errorf("init failed: %w", err) } - client := action.NewInstall(actionConfig) - client.ReleaseName = "argocd" - client.Namespace = "argocd" - client.CreateNamespace = true - client.DryRunStrategy = "none" - client.WaitStrategy = "watcher" - client.Version = a.Version - - client.ChartPathOptions.RepoURL = "https://argoproj.github.io/argo-helm" + // Check if a release already exists + listClient := action.NewList(actionConfig) + listClient.Filter = "^argocd$" + listClient.Deployed = true + listClient.SetStateMask() - chartPath, err := client.ChartPathOptions.LocateChart("argo-cd", settings) + releases, err := listClient.Run() if err != nil { - log.Fatalf("LocateChart failed: %v", err) + return fmt.Errorf("list releases failed: %w", err) } - chartRequested, err := loader.Load(chartPath) - if err != nil { - log.Fatalf("Load failed: %v", err) + // Find existing "argocd" release using the Accessor interface + var existingAccessor release.Accessor + + for _, r := range releases { + acc, err := release.NewAccessor(r) + if err != nil { + continue + } + if acc.Name() == "argocd" { + existingAccessor = acc + break + } } + chartName := "argo-cd" + repoURL := "https://argoproj.github.io/argo-helm" vals := map[string]interface{}{ "dex": map[string]interface{}{ "enabled": false, }, } - _, err = client.RunWithContext(ctx, chartRequested, vals) - if err != nil { - log.Fatalf("Install failed: %v", err) + if existingAccessor != nil { + // A release already exists — compare versions using the chart accessor + chartAcc, err := chart.NewAccessor(existingAccessor.Chart()) + if err != nil { + return fmt.Errorf("failed to access chart metadata: %w", err) + } + metadata := chartAcc.MetadataAsMap() + installedVersion, _ := metadata["Version"].(string) + log.Printf("Found existing ArgoCD release with chart version %s\n", installedVersion) + + installedSemver, err := semver.NewVersion(installedVersion) + if err != nil { + return fmt.Errorf("failed to parse installed version %q: %w", installedVersion, err) + } + requestedSemver, err := semver.NewVersion(a.Version) + if err != nil { + return fmt.Errorf("failed to parse requested version %q: %w", a.Version, err) + } + + if requestedSemver.LessThan(installedSemver) { + return fmt.Errorf( + "requested version %s is older than installed version %s; downgrade is not allowed", + a.Version, installedVersion, + ) + } + + // Version is equal or larger — perform an upgrade + log.Printf("Upgrading ArgoCD from %s to %s\n", installedVersion, a.Version) + + upgradeClient := action.NewUpgrade(actionConfig) + upgradeClient.Namespace = "argocd" + upgradeClient.WaitStrategy = "watcher" + upgradeClient.Version = a.Version + upgradeClient.ChartPathOptions.RepoURL = repoURL + + chartPath, err := upgradeClient.ChartPathOptions.LocateChart(chartName, settings) + if err != nil { + return fmt.Errorf("LocateChart failed: %w", err) + } + + chartRequested, err := loader.Load(chartPath) + if err != nil { + return fmt.Errorf("load failed: %w", err) + } + + _, err = upgradeClient.RunWithContext(ctx, existingAccessor.Name(), chartRequested, vals) + if err != nil { + return fmt.Errorf("upgrade failed: %w", err) + } + + fmt.Printf("Successfully upgraded Argo CD to chart version %s\n", a.Version) + } else { + // No existing release — perform a fresh install + log.Println("No existing ArgoCD release found, performing fresh install") + + installClient := action.NewInstall(actionConfig) + installClient.ReleaseName = "argocd" + installClient.Namespace = "argocd" + installClient.CreateNamespace = true + installClient.DryRunStrategy = "none" + installClient.WaitStrategy = "watcher" + installClient.Version = a.Version + installClient.ChartPathOptions.RepoURL = repoURL + + chartPath, err := installClient.ChartPathOptions.LocateChart(chartName, settings) + if err != nil { + return fmt.Errorf("LocateChart failed: %w", err) + } + + chartRequested, err := loader.Load(chartPath) + if err != nil { + return fmt.Errorf("load failed: %w", err) + } + + _, err = installClient.RunWithContext(ctx, chartRequested, vals) + if err != nil { + return fmt.Errorf("install failed: %w", err) + } + + fmt.Printf("Successfully installed Argo CD (chart version: %s)\n", a.Version) } - fmt.Printf("Successfully installed Argo CD %s (Chart version: %s)\n", client.ReleaseName, client.Version) return nil } From fb9ba979ec2366533e0bec38bde9c6d65381a290 Mon Sep 17 00:00:00 2001 From: bachgg Date: Mon, 2 Mar 2026 12:44:56 +0100 Subject: [PATCH 05/33] cleane up --- cli/cmd/argocd.go | 4 +++ internal/installer/argocd.go | 57 ------------------------------------ 2 files changed, 4 insertions(+), 57 deletions(-) diff --git a/cli/cmd/argocd.go b/cli/cmd/argocd.go index 810b38d7..e6e68752 100644 --- a/cli/cmd/argocd.go +++ b/cli/cmd/argocd.go @@ -51,6 +51,10 @@ func (c *GetAdminPasswordCmd) RunE(_ *cobra.Command, args []string) error { return nil } +type Config struct { + cmd *cobra.Command +} + func AddArgoCDCmd(parentCmd *cobra.Command, opts *GlobalOptions) { argocd := ArgoCDCmd{ cmd: &cobra.Command{ diff --git a/internal/installer/argocd.go b/internal/installer/argocd.go index 76b35e44..9ad8e0de 100644 --- a/internal/installer/argocd.go +++ b/internal/installer/argocd.go @@ -8,7 +8,6 @@ import ( "fmt" "log" "os" - "path/filepath" "github.com/Masterminds/semver/v3" "helm.sh/helm/v4/pkg/action" @@ -16,18 +15,10 @@ import ( "helm.sh/helm/v4/pkg/chart/loader" "helm.sh/helm/v4/pkg/cli" "helm.sh/helm/v4/pkg/release" - - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/clientcmd" ) type ArgoCDManager interface { - PreInstall() error Install() error - PostInstall() error } type ArgoCD struct { @@ -40,54 +31,6 @@ func NewArgoCD(version string) ArgoCDManager { } } -func createArgocdNamespace() error { - home, _ := os.UserHomeDir() - kubeconfigPath := filepath.Join(home, ".kube", "config") - - config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( - &clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfigPath}, - &clientcmd.ConfigOverrides{CurrentContext: ""}, // Empty string means the current select context - ).ClientConfig() - - if err != nil { - return fmt.Errorf("Error loading current context: %v", err) - } - - clientset, err := kubernetes.NewForConfig(config) - if err != nil { - return err - } - namespace := "argocd" - _, err = clientset.CoreV1().Namespaces().Get(context.TODO(), namespace, metav1.GetOptions{}) - - if err == nil { - fmt.Printf("Namespace %s already exists\n", namespace) - return nil - } - - if errors.IsNotFound(err) { - ns := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}} - _, err = clientset.CoreV1().Namespaces().Create(context.TODO(), ns, metav1.CreateOptions{}) - - if err != nil { - return fmt.Errorf("Error: %v\n", err) - } else { - log.Println("Created namespace 'argocd' using the active context.") - } - } - return err -} - -// Install resources needed by ArgoCD -func (a *ArgoCD) PreInstall() error { - panic("unimplemented") -} - -// PostInstall implements ArgoCDManager. -func (a *ArgoCD) PostInstall() error { - panic("unimplemented") -} - // Install the ArgoCD chart func (a *ArgoCD) Install() error { if a.Version == "" { From 37834da2c91e27d87d8b809e09907dccb933bfac Mon Sep 17 00:00:00 2001 From: bachgg Date: Wed, 4 Mar 2026 14:53:34 +0100 Subject: [PATCH 06/33] add yaml resources --- internal/installer/argocd.go | 16 ++++ .../installer/argocd/once-app-projects.yaml | 83 +++++++++++++++++++ .../argocd/once-argocd-resources.yaml.tpl | 17 ++++ .../once-secret-argocd-cluster-local.yaml.tpl | 19 +++++ ...once-secret-codesphere-repos-read.yaml.tpl | 13 +++ .../argocd/once-secret-ghcr-oci.yaml.tpl | 15 ++++ 6 files changed, 163 insertions(+) create mode 100644 internal/installer/argocd/once-app-projects.yaml create mode 100644 internal/installer/argocd/once-argocd-resources.yaml.tpl create mode 100644 internal/installer/argocd/once-secret-argocd-cluster-local.yaml.tpl create mode 100644 internal/installer/argocd/once-secret-codesphere-repos-read.yaml.tpl create mode 100644 internal/installer/argocd/once-secret-ghcr-oci.yaml.tpl diff --git a/internal/installer/argocd.go b/internal/installer/argocd.go index 9ad8e0de..23b64f41 100644 --- a/internal/installer/argocd.go +++ b/internal/installer/argocd.go @@ -31,6 +31,22 @@ func NewArgoCD(version string) ArgoCDManager { } } +func applyAppProjects() { + +} + +func applyLocalCluster() { + +} + +func applyHelmRegistrySecret() { + +} + +func applyGitRepoSecret() { + +} + // Install the ArgoCD chart func (a *ArgoCD) Install() error { if a.Version == "" { diff --git a/internal/installer/argocd/once-app-projects.yaml b/internal/installer/argocd/once-app-projects.yaml new file mode 100644 index 00000000..2a3d1e90 --- /dev/null +++ b/internal/installer/argocd/once-app-projects.yaml @@ -0,0 +1,83 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/datreeio/CRDs-catalog/refs/heads/main/argoproj.io/appproject_v1alpha1.json +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: prod + namespace: argocd + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + description: Project containing production apps + clusterResourceWhitelist: + - group: "*" + kind: "*" + destinations: + - namespace: "*" + name: dc-0 + - namespace: "*" + name: dc-2 + - namespace: "*" + name: dc-4 + - namespace: "*" + name: dc-6 + sourceRepos: + - "https://github.com/codesphere-cloud/charts.git" + - "ghcr.io/codesphere-cloud/charts" + roles: + - name: admin + description: Admin privileges to prod + policies: + - p, proj:prod:admin, *, *, */*, allow + groups: + - ops@codesphere.com + - name: read-only + description: Read-only privileges to prod + policies: + - p, proj:prod:read-only, *, get, prod/*, allow + groups: + - development@codesphere.com +--- +# yaml-language-server: $schema=https://raw.githubusercontent.com/datreeio/CRDs-catalog/refs/heads/main/argoproj.io/appproject_v1alpha1.json +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: dev + namespace: argocd + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + description: Project containing dev apps + clusterResourceWhitelist: + - group: "*" + kind: "*" + destinations: + - namespace: "codesphere-dev" + name: "*" + - namespace: "*" + name: dc-5 + sourceRepos: + - "*" + roles: + - name: admin + description: Admin privileges to dev + policies: + - p, proj:dev:admin, *, *, */*, allow + 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/argocd/once-argocd-resources.yaml.tpl b/internal/installer/argocd/once-argocd-resources.yaml.tpl new file mode 100644 index 00000000..6286b1f3 --- /dev/null +++ b/internal/installer/argocd/once-argocd-resources.yaml.tpl @@ -0,0 +1,17 @@ +# ArgoCD +apiVersion: v1 +kind: Namespace +metadata: + name: argocd +--- +apiVersion: v1 +stringData: + admin.password: "${ARGOCD_ADMIN_PASSWORD_HASH}" +kind: Secret +metadata: + labels: + app.kubernetes.io/name: argocd-secret + app.kubernetes.io/part-of: argocd + name: argocd-secret + namespace: argocd +type: Opaque diff --git a/internal/installer/argocd/once-secret-argocd-cluster-local.yaml.tpl b/internal/installer/argocd/once-secret-argocd-cluster-local.yaml.tpl new file mode 100644 index 00000000..22d5b3a1 --- /dev/null +++ b/internal/installer/argocd/once-secret-argocd-cluster-local.yaml.tpl @@ -0,0 +1,19 @@ +# ArgoCD cluster config for this cluster, where ArgoCD is running +# Additional clusters can be added for ArgoCD to deploy apps to by adding similar secrets +# Read more https://argo-cd.readthedocs.io/en/stable/operator-manual/declarative-setup/#clusters +apiVersion: v1 +kind: Secret +metadata: + name: "argocd-cluster-dc-${DC_NUMBER}" + namespace: argocd + labels: + argocd.argoproj.io/secret-type: cluster +stringData: + name: "dc-${DC_NUMBER}" + server: https://kubernetes.default.svc # This is a local url because it is the same cluster where ArgoCD is running + config: | + { + "tlsClientConfig":{ + "insecure": false + } + } diff --git a/internal/installer/argocd/once-secret-codesphere-repos-read.yaml.tpl b/internal/installer/argocd/once-secret-codesphere-repos-read.yaml.tpl new file mode 100644 index 00000000..9ac34376 --- /dev/null +++ b/internal/installer/argocd/once-secret-codesphere-repos-read.yaml.tpl @@ -0,0 +1,13 @@ +# For ArgoCD to read content of this repo +apiVersion: v1 +kind: Secret +metadata: + name: argocd-codesphere-repos-read + namespace: argocd + labels: + argocd.argoproj.io/secret-type: repo-creds +stringData: + url: https://github.com/codesphere-cloud + type: git + username: github + password: "${SECRET_CODESPHERE_REPOS_READ}" diff --git a/internal/installer/argocd/once-secret-ghcr-oci.yaml.tpl b/internal/installer/argocd/once-secret-ghcr-oci.yaml.tpl new file mode 100644 index 00000000..a7450e01 --- /dev/null +++ b/internal/installer/argocd/once-secret-ghcr-oci.yaml.tpl @@ -0,0 +1,15 @@ +# Credential for ArgoCD to pull helm charts from Codesphere GHCR +apiVersion: v1 +kind: Secret +metadata: + name: argocd-codesphere-oci-read + namespace: argocd + labels: + argocd.argoproj.io/secret-type: repository +stringData: + name: codesphere-charts + url: ghcr.io/codesphere-cloud/charts + type: helm + username: github + password: "${SECRET_CODESPHERE_OCI_READ}" + enableOCI: "true" From db46c28b8a9a8d66ca629aeaa729de63b3625bda Mon Sep 17 00:00:00 2001 From: bachgg Date: Wed, 4 Mar 2026 15:29:43 +0100 Subject: [PATCH 07/33] put resources into proper places --- internal/installer/argocd.go | 38 ++++-- .../argocd/once-argocd-resources.yaml.tpl | 17 --- internal/installer/argocd_resources.go | 76 +++++++++++ internal/installer/k8s.go | 118 ++++++++++++++++++ .../argocd/app-projects.yaml} | 2 + .../argocd/cluster-local.yaml.tpl} | 1 + .../argocd/repo-creds-git.yaml.tpl} | 1 + .../argocd/repo-helm-oci.yaml.tpl} | 1 + 8 files changed, 230 insertions(+), 24 deletions(-) delete mode 100644 internal/installer/argocd/once-argocd-resources.yaml.tpl create mode 100644 internal/installer/argocd_resources.go create mode 100644 internal/installer/k8s.go rename internal/installer/{argocd/once-app-projects.yaml => manifests/argocd/app-projects.yaml} (98%) rename internal/installer/{argocd/once-secret-argocd-cluster-local.yaml.tpl => manifests/argocd/cluster-local.yaml.tpl} (99%) rename internal/installer/{argocd/once-secret-codesphere-repos-read.yaml.tpl => manifests/argocd/repo-creds-git.yaml.tpl} (99%) rename internal/installer/{argocd/once-secret-ghcr-oci.yaml.tpl => manifests/argocd/repo-helm-oci.yaml.tpl} (99%) diff --git a/internal/installer/argocd.go b/internal/installer/argocd.go index 23b64f41..288eb125 100644 --- a/internal/installer/argocd.go +++ b/internal/installer/argocd.go @@ -15,6 +15,9 @@ import ( "helm.sh/helm/v4/pkg/chart/loader" "helm.sh/helm/v4/pkg/cli" "helm.sh/helm/v4/pkg/release" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" ) type ArgoCDManager interface { @@ -31,20 +34,41 @@ func NewArgoCD(version string) ArgoCDManager { } } -func applyAppProjects() { +func applyPostInstallResources() error { + cfg, err := rest.InClusterConfig() + if err != nil { + return fmt.Errorf("getting in-cluster config: %w", err) + } -} + clientset, err := kubernetes.NewForConfig(cfg) + if err != nil { + return fmt.Errorf("creating kubernetes clientset: %w", err) + } -func applyLocalCluster() { + dynClient, err := dynamic.NewForConfig(cfg) + if err != nil { + return fmt.Errorf("creating dynamic client: %w", err) + } -} + ctx := context.TODO() -func applyHelmRegistrySecret() { + if err := applyAppProjects(ctx, dynClient); err != nil { + return fmt.Errorf("applying app projects: %w", err) + } -} + if err := applyLocalCluster(ctx, clientset, os.Getenv("DC_NUMBER")); err != nil { + return fmt.Errorf("applying local cluster secret: %w", err) + } -func applyGitRepoSecret() { + if err := applyHelmRegistrySecret(ctx, clientset, os.Getenv("SECRET_CODESPHERE_OCI_READ")); err != nil { + return fmt.Errorf("applying helm registry secret: %w", err) + } + if err := applyGitRepoSecret(ctx, clientset, os.Getenv("SECRET_CODESPHERE_REPOS_READ")); err != nil { + return fmt.Errorf("applying git repo secret: %w", err) + } + + return nil } // Install the ArgoCD chart diff --git a/internal/installer/argocd/once-argocd-resources.yaml.tpl b/internal/installer/argocd/once-argocd-resources.yaml.tpl deleted file mode 100644 index 6286b1f3..00000000 --- a/internal/installer/argocd/once-argocd-resources.yaml.tpl +++ /dev/null @@ -1,17 +0,0 @@ -# ArgoCD -apiVersion: v1 -kind: Namespace -metadata: - name: argocd ---- -apiVersion: v1 -stringData: - admin.password: "${ARGOCD_ADMIN_PASSWORD_HASH}" -kind: Secret -metadata: - labels: - app.kubernetes.io/name: argocd-secret - app.kubernetes.io/part-of: argocd - name: argocd-secret - namespace: argocd -type: Opaque diff --git a/internal/installer/argocd_resources.go b/internal/installer/argocd_resources.go new file mode 100644 index 00000000..440e7e1b --- /dev/null +++ b/internal/installer/argocd_resources.go @@ -0,0 +1,76 @@ +// Copyright (c) Codesphere Inc. +// SPDX-License-Identifier: Apache-2.0 + +package installer + +import ( + "context" + _ "embed" + "fmt" + + "k8s.io/client-go/dynamic" + "k8s.io/client-go/kubernetes" +) + +//go:embed manifests/argocd/app-projects.yaml +var appProjectsYAML []byte + +//go:embed manifests/argocd/cluster-local.yaml.tpl +var localClusterTpl []byte + +//go:embed manifests/argocd/repo-helm-oci.yaml.tpl +var helmRegistryTpl []byte + +//go:embed manifests/argocd/repo-creds-git.yaml.tpl +var gitRepoTpl []byte + +func applyAppProjects(ctx context.Context, dynClient dynamic.Interface) error { + objects, err := decodeMultiDocYAML(appProjectsYAML) + if err != nil { + return fmt.Errorf("decoding app projects yaml: %w", err) + } + + for _, obj := range objects { + gvr, err := gvrForUnstructured(obj) + if err != nil { + return err + } + if err := applyUnstructured(ctx, dynClient, gvr, obj); err != nil { + return fmt.Errorf("applying app project %q: %w", obj.GetName(), err) + } + } + return nil +} + +func applyLocalCluster(ctx context.Context, clientset kubernetes.Interface, dcNumber string) error { + rendered, err := renderTemplate(localClusterTpl, map[string]string{ + "DC_NUMBER": dcNumber, + }) + if err != nil { + return fmt.Errorf("rendering local cluster template: %w", err) + } + + return applySecretFromYAML(ctx, clientset, rendered) +} + +func applyHelmRegistrySecret(ctx context.Context, clientset kubernetes.Interface, ociReadPassword string) error { + rendered, err := renderTemplate(helmRegistryTpl, map[string]string{ + "SECRET_CODESPHERE_OCI_READ": ociReadPassword, + }) + if err != nil { + return fmt.Errorf("rendering helm registry template: %w", err) + } + + return applySecretFromYAML(ctx, clientset, rendered) +} + +func applyGitRepoSecret(ctx context.Context, clientset kubernetes.Interface, reposReadPassword string) error { + rendered, err := renderTemplate(gitRepoTpl, map[string]string{ + "SECRET_CODESPHERE_REPOS_READ": reposReadPassword, + }) + if err != nil { + return fmt.Errorf("rendering git repo template: %w", err) + } + + return applySecretFromYAML(ctx, clientset, rendered) +} diff --git a/internal/installer/k8s.go b/internal/installer/k8s.go new file mode 100644 index 00000000..6844680d --- /dev/null +++ b/internal/installer/k8s.go @@ -0,0 +1,118 @@ +// Copyright (c) Codesphere Inc. +// SPDX-License-Identifier: Apache-2.0 + +package installer + +import ( + "bytes" + "context" + "fmt" + "io" + "strings" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/yaml" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/kubernetes" + sigyaml "sigs.k8s.io/yaml" +) + +// decodeMultiDocYAML splits a multi-document YAML byte slice into +// individual unstructured objects. This handles the "---" separators. +func decodeMultiDocYAML(data []byte) ([]*unstructured.Unstructured, error) { + var objects []*unstructured.Unstructured + + reader := yaml.NewYAMLOrJSONDecoder(bytes.NewReader(data), 4096) + for { + obj := &unstructured.Unstructured{} + if err := reader.Decode(obj); err != nil { + if err == io.EOF { + break + } + return nil, fmt.Errorf("decoding yaml document: %w", err) + } + if obj.Object == nil { + continue + } + objects = append(objects, obj) + } + + return objects, nil +} + +// renderTemplate performs simple ${VAR} substitution on a raw byte slice. +func renderTemplate(raw []byte, vars map[string]string) ([]byte, error) { + content := string(raw) + for key, val := range vars { + content = strings.ReplaceAll(content, "${"+key+"}", val) + } + return []byte(content), nil +} + +// gvrForUnstructured maps an unstructured object's GVK to the appropriate GVR. +func gvrForUnstructured(obj *unstructured.Unstructured) (schema.GroupVersionResource, error) { + gvk := obj.GroupVersionKind() + + switch gvk.Kind { + case "AppProject": + return schema.GroupVersionResource{ + Group: gvk.Group, + Version: gvk.Version, + Resource: "appprojects", + }, nil + default: + return schema.GroupVersionResource{}, fmt.Errorf("no GVR mapping for %s", gvk) + } +} + +// applyUnstructured creates or updates an unstructured resource using the dynamic client. +func applyUnstructured(ctx context.Context, dynClient dynamic.Interface, gvr schema.GroupVersionResource, obj *unstructured.Unstructured) error { + ns := obj.GetNamespace() + name := obj.GetName() + resource := dynClient.Resource(gvr).Namespace(ns) + + existing, err := resource.Get(ctx, name, metav1.GetOptions{}) + if err != nil { + _, err = resource.Create(ctx, obj, metav1.CreateOptions{}) + if err != nil { + return fmt.Errorf("creating %s %s/%s: %w", gvr.Resource, ns, name, err) + } + return nil + } + + obj.SetResourceVersion(existing.GetResourceVersion()) + _, err = resource.Update(ctx, obj, metav1.UpdateOptions{}) + if err != nil { + return fmt.Errorf("updating %s %s/%s: %w", gvr.Resource, ns, name, err) + } + return nil +} + +// applySecretFromYAML creates or updates a corev1.Secret parsed from YAML bytes. +func applySecretFromYAML(ctx context.Context, clientset kubernetes.Interface, data []byte) error { + secret := &corev1.Secret{} + if err := sigyaml.Unmarshal(data, secret); err != nil { + return fmt.Errorf("unmarshaling secret yaml: %w", err) + } + + secretsClient := clientset.CoreV1().Secrets(secret.Namespace) + + existing, err := secretsClient.Get(ctx, secret.Name, metav1.GetOptions{}) + if err != nil { + _, err = secretsClient.Create(ctx, secret, metav1.CreateOptions{}) + if err != nil { + return fmt.Errorf("creating secret %s/%s: %w", secret.Namespace, secret.Name, err) + } + return nil + } + + secret.ResourceVersion = existing.ResourceVersion + _, err = secretsClient.Update(ctx, secret, metav1.UpdateOptions{}) + if err != nil { + return fmt.Errorf("updating secret %s/%s: %w", secret.Namespace, secret.Name, err) + } + return nil +} diff --git a/internal/installer/argocd/once-app-projects.yaml b/internal/installer/manifests/argocd/app-projects.yaml similarity index 98% rename from internal/installer/argocd/once-app-projects.yaml rename to internal/installer/manifests/argocd/app-projects.yaml index 2a3d1e90..4fa5e1ee 100644 --- a/internal/installer/argocd/once-app-projects.yaml +++ b/internal/installer/manifests/argocd/app-projects.yaml @@ -14,6 +14,8 @@ spec: destinations: - namespace: "*" name: dc-0 + - namespace: "*" + name: dc-1 - namespace: "*" name: dc-2 - namespace: "*" diff --git a/internal/installer/argocd/once-secret-argocd-cluster-local.yaml.tpl b/internal/installer/manifests/argocd/cluster-local.yaml.tpl similarity index 99% rename from internal/installer/argocd/once-secret-argocd-cluster-local.yaml.tpl rename to internal/installer/manifests/argocd/cluster-local.yaml.tpl index 22d5b3a1..83410b79 100644 --- a/internal/installer/argocd/once-secret-argocd-cluster-local.yaml.tpl +++ b/internal/installer/manifests/argocd/cluster-local.yaml.tpl @@ -17,3 +17,4 @@ stringData: "insecure": false } } + diff --git a/internal/installer/argocd/once-secret-codesphere-repos-read.yaml.tpl b/internal/installer/manifests/argocd/repo-creds-git.yaml.tpl similarity index 99% rename from internal/installer/argocd/once-secret-codesphere-repos-read.yaml.tpl rename to internal/installer/manifests/argocd/repo-creds-git.yaml.tpl index 9ac34376..591ee0e8 100644 --- a/internal/installer/argocd/once-secret-codesphere-repos-read.yaml.tpl +++ b/internal/installer/manifests/argocd/repo-creds-git.yaml.tpl @@ -11,3 +11,4 @@ stringData: type: git username: github password: "${SECRET_CODESPHERE_REPOS_READ}" + diff --git a/internal/installer/argocd/once-secret-ghcr-oci.yaml.tpl b/internal/installer/manifests/argocd/repo-helm-oci.yaml.tpl similarity index 99% rename from internal/installer/argocd/once-secret-ghcr-oci.yaml.tpl rename to internal/installer/manifests/argocd/repo-helm-oci.yaml.tpl index a7450e01..7874c30a 100644 --- a/internal/installer/argocd/once-secret-ghcr-oci.yaml.tpl +++ b/internal/installer/manifests/argocd/repo-helm-oci.yaml.tpl @@ -13,3 +13,4 @@ stringData: username: github password: "${SECRET_CODESPHERE_OCI_READ}" enableOCI: "true" + From 48d9c14edda633011cc7464ec96e79e1d4228e30 Mon Sep 17 00:00:00 2001 From: bachgg Date: Wed, 4 Mar 2026 16:27:13 +0100 Subject: [PATCH 08/33] some basic testing --- internal/installer/argocd_resources_test.go | 168 ++++++++++++++++++++ internal/installer/k8s_test.go | 156 ++++++++++++++++++ 2 files changed, 324 insertions(+) create mode 100644 internal/installer/argocd_resources_test.go create mode 100644 internal/installer/k8s_test.go diff --git a/internal/installer/argocd_resources_test.go b/internal/installer/argocd_resources_test.go new file mode 100644 index 00000000..449711fc --- /dev/null +++ b/internal/installer/argocd_resources_test.go @@ -0,0 +1,168 @@ +// Copyright (c) Codesphere Inc. +// SPDX-License-Identifier: Apache-2.0 + +package installer + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + corev1 "k8s.io/api/core/v1" + sigyaml "sigs.k8s.io/yaml" +) + +var _ = Describe("Embedded ArgoCD YAML manifests", func() { + Describe("appProjectsYAML", func() { + It("is not empty", func() { + Expect(appProjectsYAML).ToNot(BeEmpty()) + }) + + It("decodes into three AppProject objects", func() { + objects, err := decodeMultiDocYAML(appProjectsYAML) + Expect(err).ToNot(HaveOccurred()) + Expect(objects).To(HaveLen(3)) + }) + + It("contains the prod project with correct metadata", func() { + objects, err := decodeMultiDocYAML(appProjectsYAML) + Expect(err).ToNot(HaveOccurred()) + + prod := objects[0] + Expect(prod.GetName()).To(Equal("prod")) + Expect(prod.GetNamespace()).To(Equal("argocd")) + Expect(prod.GetKind()).To(Equal("AppProject")) + Expect(prod.GetAPIVersion()).To(Equal("argoproj.io/v1alpha1")) + Expect(prod.GetFinalizers()).To(ContainElement("resources-finalizer.argocd.argoproj.io")) + }) + + It("contains the dev project with correct metadata", func() { + objects, err := decodeMultiDocYAML(appProjectsYAML) + Expect(err).ToNot(HaveOccurred()) + + dev := objects[1] + Expect(dev.GetName()).To(Equal("dev")) + Expect(dev.GetNamespace()).To(Equal("argocd")) + Expect(dev.GetFinalizers()).To(ContainElement("resources-finalizer.argocd.argoproj.io")) + }) + + It("contains the default project with restricted spec", func() { + objects, err := decodeMultiDocYAML(appProjectsYAML) + Expect(err).ToNot(HaveOccurred()) + + def := objects[2] + Expect(def.GetName()).To(Equal("default")) + Expect(def.GetNamespace()).To(Equal("argocd")) + + spec, ok := def.Object["spec"].(map[string]interface{}) + Expect(ok).To(BeTrue()) + + sourceRepos, ok := spec["sourceRepos"].([]interface{}) + Expect(ok).To(BeTrue()) + Expect(sourceRepos).To(BeEmpty()) + + destinations, ok := spec["destinations"].([]interface{}) + Expect(ok).To(BeTrue()) + Expect(destinations).To(BeEmpty()) + }) + + It("maps all projects to a valid GVR", func() { + objects, err := decodeMultiDocYAML(appProjectsYAML) + Expect(err).ToNot(HaveOccurred()) + + for _, obj := range objects { + gvr, err := gvrForUnstructured(obj) + Expect(err).ToNot(HaveOccurred()) + Expect(gvr.Resource).To(Equal("appprojects")) + } + }) + }) + + Describe("localClusterTpl", func() { + It("is not empty", func() { + Expect(localClusterTpl).ToNot(BeEmpty()) + }) + + It("renders and parses into a valid Secret", func() { + rendered, err := renderTemplate(localClusterTpl, map[string]string{ + "DC_NUMBER": "3", + }) + Expect(err).ToNot(HaveOccurred()) + + secret := &corev1.Secret{} + err = sigyaml.Unmarshal(rendered, secret) + Expect(err).ToNot(HaveOccurred()) + + Expect(secret.Name).To(Equal("argocd-cluster-dc-3")) + Expect(secret.Namespace).To(Equal("argocd")) + Expect(secret.Labels).To(HaveKeyWithValue("argocd.argoproj.io/secret-type", "cluster")) + Expect(secret.StringData).To(HaveKeyWithValue("name", "dc-3")) + Expect(secret.StringData).To(HaveKeyWithValue("server", "https://kubernetes.default.svc")) + Expect(secret.StringData).To(HaveKey("config")) + }) + + It("substitutes the DC_NUMBER in all required fields", func() { + rendered, err := renderTemplate(localClusterTpl, map[string]string{ + "DC_NUMBER": "7", + }) + Expect(err).ToNot(HaveOccurred()) + + secret := &corev1.Secret{} + err = sigyaml.Unmarshal(rendered, secret) + Expect(err).ToNot(HaveOccurred()) + + Expect(secret.Name).To(Equal("argocd-cluster-dc-7")) + Expect(secret.StringData["name"]).To(Equal("dc-7")) + }) + }) + + Describe("helmRegistryTpl", func() { + It("is not empty", func() { + Expect(helmRegistryTpl).ToNot(BeEmpty()) + }) + + It("renders and parses into a valid OCI repository Secret", func() { + rendered, err := renderTemplate(helmRegistryTpl, map[string]string{ + "SECRET_CODESPHERE_OCI_READ": "ghp_testtoken123", + }) + Expect(err).ToNot(HaveOccurred()) + + secret := &corev1.Secret{} + err = sigyaml.Unmarshal(rendered, secret) + Expect(err).ToNot(HaveOccurred()) + + Expect(secret.Name).To(Equal("argocd-codesphere-oci-read")) + Expect(secret.Namespace).To(Equal("argocd")) + Expect(secret.Labels).To(HaveKeyWithValue("argocd.argoproj.io/secret-type", "repository")) + Expect(secret.StringData).To(HaveKeyWithValue("type", "helm")) + Expect(secret.StringData).To(HaveKeyWithValue("url", "ghcr.io/codesphere-cloud/charts")) + Expect(secret.StringData).To(HaveKeyWithValue("username", "github")) + Expect(secret.StringData).To(HaveKeyWithValue("password", "ghp_testtoken123")) + Expect(secret.StringData).To(HaveKeyWithValue("enableOCI", "true")) + }) + }) + + Describe("gitRepoTpl", func() { + It("is not empty", func() { + Expect(gitRepoTpl).ToNot(BeEmpty()) + }) + + It("renders and parses into a valid repo-creds Secret", func() { + rendered, err := renderTemplate(gitRepoTpl, map[string]string{ + "SECRET_CODESPHERE_REPOS_READ": "ghp_repotoken456", + }) + Expect(err).ToNot(HaveOccurred()) + + secret := &corev1.Secret{} + err = sigyaml.Unmarshal(rendered, secret) + Expect(err).ToNot(HaveOccurred()) + + Expect(secret.Name).To(Equal("argocd-codesphere-repos-read")) + Expect(secret.Namespace).To(Equal("argocd")) + Expect(secret.Labels).To(HaveKeyWithValue("argocd.argoproj.io/secret-type", "repo-creds")) + Expect(secret.StringData).To(HaveKeyWithValue("type", "git")) + Expect(secret.StringData).To(HaveKeyWithValue("url", "https://github.com/codesphere-cloud")) + Expect(secret.StringData).To(HaveKeyWithValue("username", "github")) + Expect(secret.StringData).To(HaveKeyWithValue("password", "ghp_repotoken456")) + }) + }) +}) diff --git a/internal/installer/k8s_test.go b/internal/installer/k8s_test.go new file mode 100644 index 00000000..48424cb6 --- /dev/null +++ b/internal/installer/k8s_test.go @@ -0,0 +1,156 @@ +// Copyright (c) Codesphere Inc. +// SPDX-License-Identifier: Apache-2.0 + +package installer + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +var _ = Describe("decodeMultiDocYAML", func() { + It("decodes a single YAML document", func() { + yaml := []byte(` +apiVersion: v1 +kind: Secret +metadata: + name: my-secret + namespace: argocd +`) + objects, err := decodeMultiDocYAML(yaml) + Expect(err).ToNot(HaveOccurred()) + Expect(objects).To(HaveLen(1)) + Expect(objects[0].GetName()).To(Equal("my-secret")) + Expect(objects[0].GetNamespace()).To(Equal("argocd")) + Expect(objects[0].GetKind()).To(Equal("Secret")) + }) + + It("decodes multiple YAML documents separated by ---", func() { + yaml := []byte(` +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: prod + namespace: argocd +--- +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: dev + namespace: argocd +--- +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: default + namespace: argocd +`) + objects, err := decodeMultiDocYAML(yaml) + Expect(err).ToNot(HaveOccurred()) + Expect(objects).To(HaveLen(3)) + Expect(objects[0].GetName()).To(Equal("prod")) + Expect(objects[1].GetName()).To(Equal("dev")) + Expect(objects[2].GetName()).To(Equal("default")) + }) + + It("skips empty documents", func() { + yaml := []byte(` +--- +apiVersion: v1 +kind: Secret +metadata: + name: only-one + namespace: argocd +--- +`) + objects, err := decodeMultiDocYAML(yaml) + Expect(err).ToNot(HaveOccurred()) + Expect(objects).To(HaveLen(1)) + Expect(objects[0].GetName()).To(Equal("only-one")) + }) + + It("returns empty slice for empty input", func() { + objects, err := decodeMultiDocYAML([]byte("")) + Expect(err).ToNot(HaveOccurred()) + Expect(objects).To(BeEmpty()) + }) + + It("returns an error for invalid YAML", func() { + yaml := []byte(`not: valid: yaml: [`) + _, err := decodeMultiDocYAML(yaml) + Expect(err).To(HaveOccurred()) + }) +}) + +var _ = Describe("renderTemplate", func() { + It("replaces a single variable", func() { + tpl := []byte(`name: "dc-${DC_NUMBER}"`) + rendered, err := renderTemplate(tpl, map[string]string{ + "DC_NUMBER": "5", + }) + Expect(err).ToNot(HaveOccurred()) + Expect(string(rendered)).To(Equal(`name: "dc-5"`)) + }) + + It("replaces multiple variables", func() { + tpl := []byte(`server: ${HOST}, port: ${PORT}`) + rendered, err := renderTemplate(tpl, map[string]string{ + "HOST": "localhost", + "PORT": "8080", + }) + Expect(err).ToNot(HaveOccurred()) + Expect(string(rendered)).To(Equal(`server: localhost, port: 8080`)) + }) + + It("replaces multiple occurrences of the same variable", func() { + tpl := []byte(`a: ${VAR}, b: ${VAR}`) + rendered, err := renderTemplate(tpl, map[string]string{ + "VAR": "value", + }) + Expect(err).ToNot(HaveOccurred()) + Expect(string(rendered)).To(Equal(`a: value, b: value`)) + }) + + It("leaves unmatched placeholders intact", func() { + tpl := []byte(`name: ${KNOWN}, secret: ${UNKNOWN}`) + rendered, err := renderTemplate(tpl, map[string]string{ + "KNOWN": "replaced", + }) + Expect(err).ToNot(HaveOccurred()) + Expect(string(rendered)).To(ContainSubstring("replaced")) + Expect(string(rendered)).To(ContainSubstring("${UNKNOWN}")) + }) + + It("returns input unchanged when vars map is empty", func() { + tpl := []byte(`nothing: to replace`) + rendered, err := renderTemplate(tpl, map[string]string{}) + Expect(err).ToNot(HaveOccurred()) + Expect(string(rendered)).To(Equal(`nothing: to replace`)) + }) +}) + +var _ = Describe("gvrForUnstructured", func() { + It("returns the correct GVR for AppProject", func() { + obj := &unstructured.Unstructured{} + obj.SetAPIVersion("argoproj.io/v1alpha1") + obj.SetKind("AppProject") + + gvr, err := gvrForUnstructured(obj) + Expect(err).ToNot(HaveOccurred()) + Expect(gvr.Group).To(Equal("argoproj.io")) + Expect(gvr.Version).To(Equal("v1alpha1")) + Expect(gvr.Resource).To(Equal("appprojects")) + }) + + It("returns an error for an unknown kind", func() { + obj := &unstructured.Unstructured{} + obj.SetAPIVersion("v1") + obj.SetKind("ConfigMap") + + _, err := gvrForUnstructured(obj) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("no GVR mapping")) + }) +}) From bc209e85fddc622544f112b923638f0d862254ef Mon Sep 17 00:00:00 2001 From: bachgg Date: Wed, 4 Mar 2026 16:51:24 +0100 Subject: [PATCH 09/33] use current kubecontext to apply resource yaml --- internal/installer/argocd.go | 29 +++++++++++------------------ internal/installer/k8s.go | 27 +++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/internal/installer/argocd.go b/internal/installer/argocd.go index 288eb125..001a212f 100644 --- a/internal/installer/argocd.go +++ b/internal/installer/argocd.go @@ -15,9 +15,6 @@ import ( "helm.sh/helm/v4/pkg/chart/loader" "helm.sh/helm/v4/pkg/cli" "helm.sh/helm/v4/pkg/release" - "k8s.io/client-go/dynamic" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" ) type ArgoCDManager interface { @@ -35,19 +32,9 @@ func NewArgoCD(version string) ArgoCDManager { } func applyPostInstallResources() error { - cfg, err := rest.InClusterConfig() + clientset, dynClient, err := newClients() if err != nil { - return fmt.Errorf("getting in-cluster config: %w", err) - } - - clientset, err := kubernetes.NewForConfig(cfg) - if err != nil { - return fmt.Errorf("creating kubernetes clientset: %w", err) - } - - dynClient, err := dynamic.NewForConfig(cfg) - if err != nil { - return fmt.Errorf("creating dynamic client: %w", err) + return fmt.Errorf("creating kubernetes clients: %w", err) } ctx := context.TODO() @@ -56,15 +43,21 @@ func applyPostInstallResources() error { return fmt.Errorf("applying app projects: %w", err) } - if err := applyLocalCluster(ctx, clientset, os.Getenv("DC_NUMBER")); err != nil { + dcNumber := "1" + + if err := applyLocalCluster(ctx, clientset, dcNumber); err != nil { return fmt.Errorf("applying local cluster secret: %w", err) } - if err := applyHelmRegistrySecret(ctx, clientset, os.Getenv("SECRET_CODESPHERE_OCI_READ")); err != nil { + secretOci := "abc" + + if err := applyHelmRegistrySecret(ctx, clientset, secretOci); err != nil { return fmt.Errorf("applying helm registry secret: %w", err) } - if err := applyGitRepoSecret(ctx, clientset, os.Getenv("SECRET_CODESPHERE_REPOS_READ")); err != nil { + secretGit := "xyz" + + if err := applyGitRepoSecret(ctx, clientset, secretGit); err != nil { return fmt.Errorf("applying git repo secret: %w", err) } diff --git a/internal/installer/k8s.go b/internal/installer/k8s.go index 6844680d..7b3bca40 100644 --- a/internal/installer/k8s.go +++ b/internal/installer/k8s.go @@ -17,6 +17,7 @@ import ( "k8s.io/apimachinery/pkg/util/yaml" "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" sigyaml "sigs.k8s.io/yaml" ) @@ -116,3 +117,29 @@ func applySecretFromYAML(ctx context.Context, clientset kubernetes.Interface, da } return nil } + +// newClients creates both a typed and dynamic Kubernetes client +// using the current kubeconfig context (respects KUBECONFIG env var +// and defaults to ~/.kube/config). +func newClients() (kubernetes.Interface, dynamic.Interface, error) { + loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() + configOverrides := &clientcmd.ConfigOverrides{} + kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides) + + cfg, err := kubeConfig.ClientConfig() + if err != nil { + return nil, nil, fmt.Errorf("loading kubeconfig: %w", err) + } + + clientset, err := kubernetes.NewForConfig(cfg) + if err != nil { + return nil, nil, fmt.Errorf("creating kubernetes clientset: %w", err) + } + + dynClient, err := dynamic.NewForConfig(cfg) + if err != nil { + return nil, nil, fmt.Errorf("creating dynamic client: %w", err) + } + + return clientset, dynClient, nil +} From 473380a30b23252f5666970b6658ae5583043a45 Mon Sep 17 00:00:00 2001 From: bachgg Date: Thu, 5 Mar 2026 11:14:42 +0100 Subject: [PATCH 10/33] apply post-install resources and add some logging --- internal/installer/argocd.go | 5 +++++ internal/installer/argocd_resources.go | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/internal/installer/argocd.go b/internal/installer/argocd.go index 001a212f..2ab057ef 100644 --- a/internal/installer/argocd.go +++ b/internal/installer/argocd.go @@ -194,5 +194,10 @@ func (a *ArgoCD) Install() error { fmt.Printf("Successfully installed Argo CD (chart version: %s)\n", a.Version) } + err = applyPostInstallResources() + if err != nil { + return fmt.Errorf("failed apply post chart install resources: %v", err) + } + return nil } diff --git a/internal/installer/argocd_resources.go b/internal/installer/argocd_resources.go index 440e7e1b..d4ed01ee 100644 --- a/internal/installer/argocd_resources.go +++ b/internal/installer/argocd_resources.go @@ -7,6 +7,7 @@ import ( "context" _ "embed" "fmt" + "log" "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" @@ -25,6 +26,7 @@ var helmRegistryTpl []byte var gitRepoTpl []byte func applyAppProjects(ctx context.Context, dynClient dynamic.Interface) error { + log.Println("Applying AppProjects... ") objects, err := decodeMultiDocYAML(appProjectsYAML) if err != nil { return fmt.Errorf("decoding app projects yaml: %w", err) @@ -43,6 +45,7 @@ func applyAppProjects(ctx context.Context, dynClient dynamic.Interface) error { } func applyLocalCluster(ctx context.Context, clientset kubernetes.Interface, dcNumber string) error { + log.Println("Applying local cluster secret... ") rendered, err := renderTemplate(localClusterTpl, map[string]string{ "DC_NUMBER": dcNumber, }) @@ -54,6 +57,7 @@ func applyLocalCluster(ctx context.Context, clientset kubernetes.Interface, dcNu } func applyHelmRegistrySecret(ctx context.Context, clientset kubernetes.Interface, ociReadPassword string) error { + log.Println("Applying helm registry secret... ") rendered, err := renderTemplate(helmRegistryTpl, map[string]string{ "SECRET_CODESPHERE_OCI_READ": ociReadPassword, }) @@ -65,6 +69,7 @@ func applyHelmRegistrySecret(ctx context.Context, clientset kubernetes.Interface } func applyGitRepoSecret(ctx context.Context, clientset kubernetes.Interface, reposReadPassword string) error { + log.Println("Applying git repo secret... ") rendered, err := renderTemplate(gitRepoTpl, map[string]string{ "SECRET_CODESPHERE_REPOS_READ": reposReadPassword, }) From 5eebf558327bd99b925d5dfdd46bfeb1f5edfd6f Mon Sep 17 00:00:00 2001 From: bachgg Date: Fri, 6 Mar 2026 11:50:56 +0100 Subject: [PATCH 11/33] pass everything into cli args for simplicity --- cli/cmd/argocd.go | 17 +++++++++++------ internal/installer/argocd.go | 16 +++++++++++----- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/cli/cmd/argocd.go b/cli/cmd/argocd.go index e6e68752..5c1a6950 100644 --- a/cli/cmd/argocd.go +++ b/cli/cmd/argocd.go @@ -25,15 +25,14 @@ type InstallArgoCDCmd struct { type InstallArgoCDOpts struct { *GlobalOptions - Version string - Package string - Config string - Force bool + Version string + DatacenterId string + GitPassword string + RegistryPassword string } func (c *InstallArgoCDCmd) RunE(_ *cobra.Command, args []string) error { - install := installer.NewArgoCD(c.Opts.Version) - // err := argocd.PreInstall() + install := installer.NewArgoCD(c.Opts.Version, c.Opts.DatacenterId, c.Opts.RegistryPassword, c.Opts.GitPassword) err := install.Install() if err != nil { return fmt.Errorf("failed to install chart ArgoCD: %w", err) @@ -75,6 +74,12 @@ func AddArgoCDCmd(parentCmd *cobra.Command, opts *GlobalOptions) { }, "oms-cli"), }, } + install.cmd.Flags().StringVarP(&install.Opts.GitPassword, "git-password", "c", "", "Password/token to read from the git repo where ArgoCD Application manifests are stored") + install.cmd.MarkFlagRequired("git-password") + install.cmd.Flags().StringVar(&install.Opts.RegistryPassword, "registry-password", "", "Password/token to read from the OCI registry (e.g. ghcr.io) where Helm chart artifacts are stored") + install.cmd.MarkFlagRequired("registry-password") + install.cmd.Flags().StringVar(&install.Opts.DatacenterId, "dc-id", "", "Codesphere Datacenter ID where this ArgoCD is installed") + install.cmd.MarkFlagRequired("dc-id") install.cmd.Flags().StringVarP(&install.Opts.Version, "version", "v", "", "Version of the ArgoCD helm chart to install") install.cmd.RunE = install.RunE argocd.cmd.AddCommand(install.cmd) diff --git a/internal/installer/argocd.go b/internal/installer/argocd.go index 2ab057ef..74f158b8 100644 --- a/internal/installer/argocd.go +++ b/internal/installer/argocd.go @@ -22,16 +22,22 @@ type ArgoCDManager interface { } type ArgoCD struct { - Version string + Version string + DcNumber string + PasswordOCI string + PasswordGit string } -func NewArgoCD(version string) ArgoCDManager { +func NewArgoCD(version string, dcId string, passwordOCI string, passwordGit string) ArgoCDManager { return &ArgoCD{ - Version: version, + Version: version, + DcNumber: dcId, + PasswordOCI: passwordOCI, + PasswordGit: passwordGit, } } -func applyPostInstallResources() error { +func (a *ArgoCD) applyPostInstallResources() error { clientset, dynClient, err := newClients() if err != nil { return fmt.Errorf("creating kubernetes clients: %w", err) @@ -194,7 +200,7 @@ func (a *ArgoCD) Install() error { fmt.Printf("Successfully installed Argo CD (chart version: %s)\n", a.Version) } - err = applyPostInstallResources() + err = a.applyPostInstallResources() if err != nil { return fmt.Errorf("failed apply post chart install resources: %v", err) } From 00cac5a7bd48cb1104e02ca00faa91fdb7d8ecc7 Mon Sep 17 00:00:00 2001 From: bachgg <82871810+bachgg@users.noreply.github.com> Date: Fri, 6 Mar 2026 10:54:54 +0000 Subject: [PATCH 12/33] chore(docs): Auto-update docs and licenses Signed-off-by: bachgg <82871810+bachgg@users.noreply.github.com> --- NOTICE | 632 +++++++++++++++++- docs/oms-cli_beta.md | 1 + docs/oms-cli_beta_argocd.md | 16 + .../oms-cli_beta_argocd_get-admin-password.md | 18 + docs/oms-cli_beta_argocd_install.md | 37 + .../manifests/argocd/app-projects.yaml | 3 + internal/installer/mocks.go | 71 ++ internal/tmpl/NOTICE | 632 +++++++++++++++++- 8 files changed, 1408 insertions(+), 2 deletions(-) create mode 100644 docs/oms-cli_beta_argocd.md create mode 100644 docs/oms-cli_beta_argocd_get-admin-password.md create mode 100644 docs/oms-cli_beta_argocd_install.md diff --git a/NOTICE b/NOTICE index 6fcd73a7..1fadc113 100644 --- a/NOTICE +++ b/NOTICE @@ -63,24 +63,84 @@ Version: v0.23.2 License: MIT License URL: https://gitea.com/gitea/go-sdk/src/tag/gitea/v0.23.2/gitea/LICENSE +---------- +Module: dario.cat/mergo +Version: v1.0.2 +License: BSD-3-Clause +License URL: https://github.com/imdario/mergo/blob/v1.0.2/LICENSE + ---------- Module: github.com/42wim/httpsig Version: v1.2.3 License: BSD-3-Clause License URL: https://github.com/42wim/httpsig/blob/v1.2.3/LICENSE +---------- +Module: github.com/BurntSushi/toml +Version: v1.6.0 +License: MIT +License URL: https://github.com/BurntSushi/toml/blob/v1.6.0/COPYING + +---------- +Module: github.com/MakeNowJust/heredoc +Version: v1.0.0 +License: MIT +License URL: https://github.com/MakeNowJust/heredoc/blob/v1.0.0/LICENSE + +---------- +Module: github.com/Masterminds/goutils +Version: v1.1.1 +License: Apache-2.0 +License URL: https://github.com/Masterminds/goutils/blob/v1.1.1/LICENSE.txt + ---------- Module: github.com/Masterminds/semver/v3 Version: v3.4.0 License: MIT License URL: https://github.com/Masterminds/semver/blob/v3.4.0/LICENSE.txt +---------- +Module: github.com/Masterminds/sprig/v3 +Version: v3.3.0 +License: MIT +License URL: https://github.com/Masterminds/sprig/blob/v3.3.0/LICENSE.txt + +---------- +Module: github.com/Masterminds/squirrel +Version: v1.5.4 +License: MIT +License URL: https://github.com/Masterminds/squirrel/blob/v1.5.4/LICENSE + +---------- +Module: github.com/ProtonMail/go-crypto +Version: v1.3.0 +License: BSD-3-Clause +License URL: https://github.com/ProtonMail/go-crypto/blob/v1.3.0/LICENSE + +---------- +Module: github.com/asaskevich/govalidator +Version: v0.0.0-20230301143203-a9d515a09cc2 +License: MIT +License URL: https://github.com/asaskevich/govalidator/blob/a9d515a09cc2/LICENSE + +---------- +Module: github.com/blang/semver/v4 +Version: v4.0.0 +License: MIT +License URL: https://github.com/blang/semver/blob/v4.0.0/v4/LICENSE + ---------- Module: github.com/cespare/xxhash/v2 Version: v2.3.0 License: MIT License URL: https://github.com/cespare/xxhash/blob/v2.3.0/LICENSE.txt +---------- +Module: github.com/chai2010/gettext-go +Version: v1.0.2 +License: BSD-3-Clause +License URL: https://github.com/chai2010/gettext-go/blob/v1.0.2/LICENSE + ---------- Module: github.com/clipperhouse/stringish Version: v0.1.1 @@ -93,6 +153,12 @@ Version: v2.4.0 License: MIT License URL: https://github.com/clipperhouse/uax29/blob/v2.4.0/LICENSE +---------- +Module: github.com/cloudflare/circl +Version: v1.6.3 +License: BSD-3-Clause +License URL: https://github.com/cloudflare/circl/blob/v1.6.3/LICENSE + ---------- Module: github.com/codesphere-cloud/cs-go Version: v0.17.2 @@ -117,24 +183,96 @@ Version: v1.5.2 License: MIT License URL: https://github.com/creativeprojects/go-selfupdate/blob/v1.5.2/LICENSE +---------- +Module: github.com/cyphar/filepath-securejoin +Version: v0.6.1 +License: MPL-2.0 +License URL: https://github.com/cyphar/filepath-securejoin/blob/v0.6.1/COPYING.md + +---------- +Module: github.com/cyphar/filepath-securejoin +Version: v0.6.1 +License: BSD-3-Clause +License URL: https://github.com/cyphar/filepath-securejoin/blob/v0.6.1/COPYING.md + ---------- Module: github.com/davecgh/go-spew/spew Version: v1.1.2-0.20180830191138-d8f796af33cc License: ISC License URL: https://github.com/davecgh/go-spew/blob/d8f796af33cc/LICENSE +---------- +Module: github.com/dylibso/observe-sdk/go +Version: v0.0.0-20240819160327-2d926c5d788a +License: Apache-2.0 +License URL: https://github.com/dylibso/observe-sdk/blob/2d926c5d788a/go/LICENSE + +---------- +Module: github.com/emicklei/go-restful/v3 +Version: v3.12.2 +License: MIT +License URL: https://github.com/emicklei/go-restful/blob/v3.12.2/LICENSE + +---------- +Module: github.com/evanphx/json-patch/v5 +Version: v5.9.11 +License: BSD-3-Clause +License URL: https://github.com/evanphx/json-patch/blob/v5.9.11/v5/LICENSE + +---------- +Module: github.com/exponent-io/jsonpath +Version: v0.0.0-20210407135951-1de76d718b3f +License: MIT +License URL: https://github.com/exponent-io/jsonpath/blob/1de76d718b3f/LICENSE + +---------- +Module: github.com/extism/go-sdk +Version: v1.7.1 +License: BSD-3-Clause +License URL: https://github.com/extism/go-sdk/blob/v1.7.1/LICENSE + +---------- +Module: github.com/fatih/color +Version: v1.18.0 +License: MIT +License URL: https://github.com/fatih/color/blob/v1.18.0/LICENSE.md + ---------- Module: github.com/felixge/httpsnoop Version: v1.0.4 License: MIT License URL: https://github.com/felixge/httpsnoop/blob/v1.0.4/LICENSE.txt +---------- +Module: github.com/fluxcd/cli-utils/pkg +Version: v0.37.0-flux.1 +License: Apache-2.0 +License URL: https://github.com/fluxcd/cli-utils/blob/v0.37.0-flux.1/LICENSE + +---------- +Module: github.com/fxamacker/cbor/v2 +Version: v2.9.0 +License: MIT +License URL: https://github.com/fxamacker/cbor/blob/v2.9.0/LICENSE + +---------- +Module: github.com/go-errors/errors +Version: v1.5.1 +License: MIT +License URL: https://github.com/go-errors/errors/blob/v1.5.1/LICENSE.MIT + ---------- Module: github.com/go-fed/httpsig Version: v1.1.0 License: BSD-3-Clause License URL: https://github.com/go-fed/httpsig/blob/v1.1.0/LICENSE +---------- +Module: github.com/go-gorp/gorp/v3 +Version: v3.1.0 +License: MIT +License URL: https://github.com/go-gorp/gorp/blob/v3.1.0/LICENSE + ---------- Module: github.com/go-logr/logr Version: v1.4.3 @@ -147,6 +285,108 @@ Version: v1.2.2 License: Apache-2.0 License URL: https://github.com/go-logr/stdr/blob/v1.2.2/LICENSE +---------- +Module: github.com/go-openapi/jsonpointer +Version: v0.22.4 +License: Apache-2.0 +License URL: https://github.com/go-openapi/jsonpointer/blob/v0.22.4/LICENSE + +---------- +Module: github.com/go-openapi/jsonreference +Version: v0.21.4 +License: Apache-2.0 +License URL: https://github.com/go-openapi/jsonreference/blob/v0.21.4/LICENSE + +---------- +Module: github.com/go-openapi/swag +Version: v0.25.4 +License: Apache-2.0 +License URL: https://github.com/go-openapi/swag/blob/v0.25.4/LICENSE + +---------- +Module: github.com/go-openapi/swag/cmdutils +Version: v0.25.4 +License: Apache-2.0 +License URL: https://github.com/go-openapi/swag/blob/cmdutils/v0.25.4/cmdutils/LICENSE + +---------- +Module: github.com/go-openapi/swag/conv +Version: v0.25.4 +License: Apache-2.0 +License URL: https://github.com/go-openapi/swag/blob/conv/v0.25.4/conv/LICENSE + +---------- +Module: github.com/go-openapi/swag/fileutils +Version: v0.25.4 +License: Apache-2.0 +License URL: https://github.com/go-openapi/swag/blob/fileutils/v0.25.4/fileutils/LICENSE + +---------- +Module: github.com/go-openapi/swag/jsonname +Version: v0.25.4 +License: Apache-2.0 +License URL: https://github.com/go-openapi/swag/blob/jsonname/v0.25.4/jsonname/LICENSE + +---------- +Module: github.com/go-openapi/swag/jsonutils +Version: v0.25.4 +License: Apache-2.0 +License URL: https://github.com/go-openapi/swag/blob/jsonutils/v0.25.4/jsonutils/LICENSE + +---------- +Module: github.com/go-openapi/swag/loading +Version: v0.25.4 +License: Apache-2.0 +License URL: https://github.com/go-openapi/swag/blob/loading/v0.25.4/loading/LICENSE + +---------- +Module: github.com/go-openapi/swag/mangling +Version: v0.25.4 +License: Apache-2.0 +License URL: https://github.com/go-openapi/swag/blob/mangling/v0.25.4/mangling/LICENSE + +---------- +Module: github.com/go-openapi/swag/netutils +Version: v0.25.4 +License: Apache-2.0 +License URL: https://github.com/go-openapi/swag/blob/netutils/v0.25.4/netutils/LICENSE + +---------- +Module: github.com/go-openapi/swag/stringutils +Version: v0.25.4 +License: Apache-2.0 +License URL: https://github.com/go-openapi/swag/blob/stringutils/v0.25.4/stringutils/LICENSE + +---------- +Module: github.com/go-openapi/swag/typeutils +Version: v0.25.4 +License: Apache-2.0 +License URL: https://github.com/go-openapi/swag/blob/typeutils/v0.25.4/typeutils/LICENSE + +---------- +Module: github.com/go-openapi/swag/yamlutils +Version: v0.25.4 +License: Apache-2.0 +License URL: https://github.com/go-openapi/swag/blob/yamlutils/v0.25.4/yamlutils/LICENSE + +---------- +Module: github.com/gobwas/glob +Version: v0.2.3 +License: MIT +License URL: https://github.com/gobwas/glob/blob/v0.2.3/LICENSE + +---------- +Module: github.com/google/btree +Version: v1.1.3 +License: Apache-2.0 +License URL: https://github.com/google/btree/blob/v1.1.3/LICENSE + +---------- +Module: github.com/google/gnostic-models +Version: v0.7.0 +License: Apache-2.0 +License URL: https://github.com/google/gnostic-models/blob/v0.7.0/LICENSE + ---------- Module: github.com/google/go-github/v74/github Version: v74.0.0 @@ -183,6 +423,24 @@ Version: v2.17.0 License: BSD-3-Clause License URL: https://github.com/googleapis/gax-go/blob/v2.17.0/v2/LICENSE +---------- +Module: github.com/gosuri/uitable +Version: v0.0.4 +License: MIT +License URL: https://github.com/gosuri/uitable/blob/v0.0.4/LICENSE + +---------- +Module: github.com/gosuri/uitable/util/wordwrap +Version: v0.0.4 +License: MIT +License URL: https://github.com/gosuri/uitable/blob/v0.0.4/util/wordwrap/LICENSE.md + +---------- +Module: github.com/gregjones/httpcache +Version: v0.0.0-20190611155906-901d90724c79 +License: MIT +License URL: https://github.com/gregjones/httpcache/blob/901d90724c79/LICENSE.txt + ---------- Module: github.com/hashicorp/go-cleanhttp Version: v0.5.2 @@ -201,30 +459,156 @@ Version: v1.8.0 License: MPL-2.0 License URL: https://github.com/hashicorp/go-version/blob/v1.8.0/LICENSE +---------- +Module: github.com/huandu/xstrings +Version: v1.5.0 +License: MIT +License URL: https://github.com/huandu/xstrings/blob/v1.5.0/LICENSE + +---------- +Module: github.com/ianlancetaylor/demangle +Version: v0.0.0-20250417193237-f615e6bd150b +License: BSD-3-Clause +License URL: https://github.com/ianlancetaylor/demangle/blob/f615e6bd150b/LICENSE + ---------- Module: github.com/jedib0t/go-pretty/v6 Version: v6.7.8 License: MIT License URL: https://github.com/jedib0t/go-pretty/blob/v6.7.8/LICENSE +---------- +Module: github.com/jmoiron/sqlx +Version: v1.4.0 +License: MIT +License URL: https://github.com/jmoiron/sqlx/blob/v1.4.0/LICENSE + +---------- +Module: github.com/json-iterator/go +Version: v1.1.12 +License: MIT +License URL: https://github.com/json-iterator/go/blob/v1.1.12/LICENSE + ---------- Module: github.com/kr/fs Version: v0.1.0 License: BSD-3-Clause License URL: https://github.com/kr/fs/blob/v0.1.0/LICENSE +---------- +Module: github.com/lann/builder +Version: v0.0.0-20180802200727-47ae307949d0 +License: MIT +License URL: https://github.com/lann/builder/blob/47ae307949d0/LICENSE + +---------- +Module: github.com/lann/ps +Version: v0.0.0-20150810152359-62de8c46ede0 +License: MIT +License URL: https://github.com/lann/ps/blob/62de8c46ede0/LICENSE + +---------- +Module: github.com/lib/pq +Version: v1.11.1 +License: MIT +License URL: https://github.com/lib/pq/blob/v1.11.1/LICENSE + +---------- +Module: github.com/liggitt/tabwriter +Version: v0.0.0-20181228230101-89fcab3d43de +License: BSD-3-Clause +License URL: https://github.com/liggitt/tabwriter/blob/89fcab3d43de/LICENSE + ---------- Module: github.com/lithammer/shortuuid Version: v3.0.0 License: MIT License URL: https://github.com/lithammer/shortuuid/blob/v3.0.0/LICENSE +---------- +Module: github.com/mattn/go-colorable +Version: v0.1.14 +License: MIT +License URL: https://github.com/mattn/go-colorable/blob/v0.1.14/LICENSE + +---------- +Module: github.com/mattn/go-isatty +Version: v0.0.20 +License: MIT +License URL: https://github.com/mattn/go-isatty/blob/v0.0.20/LICENSE + ---------- Module: github.com/mattn/go-runewidth Version: v0.0.19 License: MIT License URL: https://github.com/mattn/go-runewidth/blob/v0.0.19/LICENSE +---------- +Module: github.com/mitchellh/copystructure +Version: v1.2.0 +License: MIT +License URL: https://github.com/mitchellh/copystructure/blob/v1.2.0/LICENSE + +---------- +Module: github.com/mitchellh/go-wordwrap +Version: v1.0.1 +License: MIT +License URL: https://github.com/mitchellh/go-wordwrap/blob/v1.0.1/LICENSE.md + +---------- +Module: github.com/mitchellh/reflectwalk +Version: v1.0.2 +License: MIT +License URL: https://github.com/mitchellh/reflectwalk/blob/v1.0.2/LICENSE + +---------- +Module: github.com/moby/term +Version: v0.5.2 +License: Apache-2.0 +License URL: https://github.com/moby/term/blob/v0.5.2/LICENSE + +---------- +Module: github.com/modern-go/concurrent +Version: v0.0.0-20180306012644-bacd9c7ef1dd +License: Apache-2.0 +License URL: https://github.com/modern-go/concurrent/blob/bacd9c7ef1dd/LICENSE + +---------- +Module: github.com/modern-go/reflect2 +Version: v1.0.3-0.20250322232337-35a7c28c31ee +License: Apache-2.0 +License URL: https://github.com/modern-go/reflect2/blob/35a7c28c31ee/LICENSE + +---------- +Module: github.com/monochromegane/go-gitignore +Version: v0.0.0-20200626010858-205db1a8cc00 +License: MIT +License URL: https://github.com/monochromegane/go-gitignore/blob/205db1a8cc00/LICENSE + +---------- +Module: github.com/munnerz/goautoneg +Version: v0.0.0-20191010083416-a7dc8b61c822 +License: BSD-3-Clause +License URL: https://github.com/munnerz/goautoneg/blob/a7dc8b61c822/LICENSE + +---------- +Module: github.com/opencontainers/go-digest +Version: v1.0.0 +License: Apache-2.0 +License URL: https://github.com/opencontainers/go-digest/blob/v1.0.0/LICENSE + +---------- +Module: github.com/opencontainers/image-spec/specs-go +Version: v1.1.1 +License: Apache-2.0 +License URL: https://github.com/opencontainers/image-spec/blob/v1.1.1/LICENSE + +---------- +Module: github.com/peterbourgon/diskv +Version: v2.0.1 +License: MIT +License URL: https://github.com/peterbourgon/diskv/blob/v2.0.1/LICENSE + ---------- Module: github.com/pkg/sftp Version: v1.13.10 @@ -237,12 +621,42 @@ Version: v1.0.1-0.20181226105442-5d4384ee4fb2 License: BSD-3-Clause License URL: https://github.com/pmezard/go-difflib/blob/5d4384ee4fb2/LICENSE +---------- +Module: github.com/rubenv/sql-migrate +Version: v1.8.1 +License: MIT +License URL: https://github.com/rubenv/sql-migrate/blob/v1.8.1/LICENSE + +---------- +Module: github.com/rubenv/sql-migrate/sqlparse +Version: v1.8.1 +License: MIT +License URL: https://github.com/rubenv/sql-migrate/blob/v1.8.1/sqlparse/LICENSE + ---------- Module: github.com/russross/blackfriday/v2 Version: v2.1.0 License: BSD-2-Clause License URL: https://github.com/russross/blackfriday/blob/v2.1.0/LICENSE.txt +---------- +Module: github.com/santhosh-tekuri/jsonschema/v6 +Version: v6.0.2 +License: Apache-2.0 +License URL: https://github.com/santhosh-tekuri/jsonschema/blob/v6.0.2/LICENSE + +---------- +Module: github.com/shopspring/decimal +Version: v1.4.0 +License: MIT +License URL: https://github.com/shopspring/decimal/blob/v1.4.0/LICENSE + +---------- +Module: github.com/spf13/cast +Version: v1.10.0 +License: MIT +License URL: https://github.com/spf13/cast/blob/v1.10.0/LICENSE + ---------- Module: github.com/spf13/cobra Version: v1.10.2 @@ -267,12 +681,36 @@ Version: v1.11.1 License: MIT License URL: https://github.com/stretchr/testify/blob/v1.11.1/LICENSE +---------- +Module: github.com/tetratelabs/wabin +Version: v0.0.0-20230304001439-f6f874872834 +License: Apache-2.0 +License URL: https://github.com/tetratelabs/wabin/blob/f6f874872834/LICENSE + +---------- +Module: github.com/tetratelabs/wazero +Version: v1.11.0 +License: Apache-2.0 +License URL: https://github.com/tetratelabs/wazero/blob/v1.11.0/LICENSE + ---------- Module: github.com/ulikunitz/xz Version: v0.5.15 License: BSD-3-Clause License URL: https://github.com/ulikunitz/xz/blob/v0.5.15/LICENSE +---------- +Module: github.com/x448/float16 +Version: v0.8.4 +License: MIT +License URL: https://github.com/x448/float16/blob/v0.8.4/LICENSE + +---------- +Module: github.com/xlab/treeprint +Version: v1.2.0 +License: MIT +License URL: https://github.com/xlab/treeprint/blob/v1.2.0/LICENSE + ---------- Module: gitlab.com/gitlab-org/api/client-go Version: v1.39.0 @@ -345,6 +783,18 @@ Version: v1.40.0 License: BSD-3-Clause License URL: https://github.com/open-telemetry/opentelemetry-go/blob/trace/v1.40.0/trace/LICENSE +---------- +Module: go.opentelemetry.io/proto/otlp +Version: v1.9.0 +License: Apache-2.0 +License URL: https://github.com/open-telemetry/opentelemetry-proto-go/blob/otlp/v1.9.0/otlp/LICENSE + +---------- +Module: go.yaml.in/yaml/v2 +Version: v2.4.3 +License: Apache-2.0 +License URL: https://github.com/yaml/go-yaml/blob/v2.4.3/LICENSE + ---------- Module: go.yaml.in/yaml/v3 Version: v3.0.4 @@ -370,7 +820,7 @@ License: BSD-3-Clause License URL: https://cs.opensource.google/go/x/oauth2/+/v0.35.0:LICENSE ---------- -Module: golang.org/x/sync/semaphore +Module: golang.org/x/sync Version: v0.19.0 License: BSD-3-Clause License URL: https://cs.opensource.google/go/x/sync/+/v0.19.0:LICENSE @@ -441,6 +891,18 @@ Version: v1.36.11 License: BSD-3-Clause License URL: https://github.com/protocolbuffers/protobuf-go/blob/v1.36.11/LICENSE +---------- +Module: gopkg.in/evanphx/json-patch.v4 +Version: v4.13.0 +License: BSD-3-Clause +License URL: https://github.com/evanphx/json-patch/blob/v4.13.0/LICENSE + +---------- +Module: gopkg.in/inf.v0 +Version: v0.9.1 +License: BSD-3-Clause +License URL: https://github.com/go-inf/inf/blob/v0.9.1/LICENSE + ---------- Module: gopkg.in/validator.v2 Version: v2.0.1 @@ -452,5 +914,173 @@ Module: gopkg.in/yaml.v3 Version: v3.0.1 License: MIT License URL: https://github.com/go-yaml/yaml/blob/v3.0.1/LICENSE + +---------- +Module: helm.sh/helm/v4 +Version: v4.1.1 +License: Apache-2.0 +License URL: https://github.com/helm/helm/blob/v4.1.1/LICENSE + +---------- +Module: k8s.io/api +Version: v0.35.1 +License: Apache-2.0 +License URL: https://github.com/kubernetes/api/blob/v0.35.1/LICENSE + +---------- +Module: k8s.io/apiextensions-apiserver/pkg/apis/apiextensions +Version: v0.35.0 +License: Apache-2.0 +License URL: https://github.com/kubernetes/apiextensions-apiserver/blob/v0.35.0/LICENSE + +---------- +Module: k8s.io/apimachinery/pkg +Version: v0.35.1 +License: Apache-2.0 +License URL: https://github.com/kubernetes/apimachinery/blob/v0.35.1/LICENSE + +---------- +Module: k8s.io/apimachinery/third_party/forked/golang +Version: v0.35.1 +License: BSD-3-Clause +License URL: https://github.com/kubernetes/apimachinery/blob/v0.35.1/third_party/forked/golang/LICENSE + +---------- +Module: k8s.io/apiserver/pkg/endpoints/deprecation +Version: v0.35.0 +License: Apache-2.0 +License URL: https://github.com/kubernetes/apiserver/blob/v0.35.0/LICENSE + +---------- +Module: k8s.io/cli-runtime/pkg +Version: v0.35.1 +License: Apache-2.0 +License URL: https://github.com/kubernetes/cli-runtime/blob/v0.35.1/LICENSE + +---------- +Module: k8s.io/client-go +Version: v0.35.1 +License: Apache-2.0 +License URL: https://github.com/kubernetes/client-go/blob/v0.35.1/LICENSE + +---------- +Module: k8s.io/client-go/third_party/forked/golang/template +Version: v0.35.1 +License: BSD-3-Clause +License URL: https://github.com/kubernetes/client-go/blob/v0.35.1/third_party/forked/golang/LICENSE + +---------- +Module: k8s.io/component-base/version +Version: v0.35.0 +License: Apache-2.0 +License URL: https://github.com/kubernetes/component-base/blob/v0.35.0/LICENSE + +---------- +Module: k8s.io/klog/v2 +Version: v2.130.1 +License: Apache-2.0 +License URL: https://github.com/kubernetes/klog/blob/v2.130.1/LICENSE + +---------- +Module: k8s.io/kube-openapi/pkg +Version: v0.0.0-20251125145642-4e65d59e963e +License: Apache-2.0 +License URL: https://github.com/kubernetes/kube-openapi/blob/4e65d59e963e/LICENSE + +---------- +Module: k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json +Version: v0.0.0-20251125145642-4e65d59e963e +License: BSD-3-Clause +License URL: https://github.com/kubernetes/kube-openapi/blob/4e65d59e963e/pkg/internal/third_party/go-json-experiment/json/LICENSE + +---------- +Module: k8s.io/kube-openapi/pkg/validation/spec +Version: v0.0.0-20251125145642-4e65d59e963e +License: Apache-2.0 +License URL: https://github.com/kubernetes/kube-openapi/blob/4e65d59e963e/pkg/validation/spec/LICENSE + +---------- +Module: k8s.io/kubectl/pkg +Version: v0.35.0 +License: Apache-2.0 +License URL: https://github.com/kubernetes/kubectl/blob/v0.35.0/LICENSE + +---------- +Module: k8s.io/utils +Version: v0.0.0-20260106112306-0fe9cd71b2f8 +License: Apache-2.0 +License URL: https://github.com/kubernetes/utils/blob/0fe9cd71b2f8/LICENSE + +---------- +Module: k8s.io/utils/internal/third_party/forked/golang/net +Version: v0.0.0-20260106112306-0fe9cd71b2f8 +License: BSD-3-Clause +License URL: https://github.com/kubernetes/utils/blob/0fe9cd71b2f8/internal/third_party/forked/golang/LICENSE + +---------- +Module: oras.land/oras-go/v2 +Version: v2.6.0 +License: Apache-2.0 +License URL: https://github.com/oras-project/oras-go/blob/v2.6.0/LICENSE + +---------- +Module: sigs.k8s.io/controller-runtime/pkg +Version: v0.22.4 +License: Apache-2.0 +License URL: https://github.com/kubernetes-sigs/controller-runtime/blob/v0.22.4/LICENSE + +---------- +Module: sigs.k8s.io/json +Version: v0.0.0-20250730193827-2d320260d730 +License: Apache-2.0 +License URL: https://github.com/kubernetes-sigs/json/blob/2d320260d730/LICENSE + +---------- +Module: sigs.k8s.io/json +Version: v0.0.0-20250730193827-2d320260d730 +License: BSD-3-Clause +License URL: https://github.com/kubernetes-sigs/json/blob/2d320260d730/LICENSE + +---------- +Module: sigs.k8s.io/kustomize/api +Version: v0.20.1 +License: Apache-2.0 +License URL: https://github.com/kubernetes-sigs/kustomize/blob/api/v0.20.1/api/LICENSE + +---------- +Module: sigs.k8s.io/kustomize/kyaml +Version: v0.21.0 +License: Apache-2.0 +License URL: https://github.com/kubernetes-sigs/kustomize/blob/kyaml/v0.21.0/kyaml/LICENSE + +---------- +Module: sigs.k8s.io/randfill +Version: v1.0.0 +License: Apache-2.0 +License URL: https://github.com/kubernetes-sigs/randfill/blob/v1.0.0/LICENSE + +---------- +Module: sigs.k8s.io/structured-merge-diff/v6 +Version: v6.3.1 +License: Apache-2.0 +License URL: https://github.com/kubernetes-sigs/structured-merge-diff/blob/v6.3.1/LICENSE + +---------- +Module: sigs.k8s.io/yaml +Version: v1.6.0 +License: MIT +License URL: https://github.com/kubernetes-sigs/yaml/blob/v1.6.0/LICENSE + +---------- +Module: sigs.k8s.io/yaml +Version: v1.6.0 +License: Apache-2.0 +License URL: https://github.com/kubernetes-sigs/yaml/blob/v1.6.0/LICENSE + +---------- +Module: sigs.k8s.io/yaml +Version: v1.6.0 +License: BSD-3-Clause +License URL: https://github.com/kubernetes-sigs/yaml/blob/v1.6.0/LICENSE diff --git a/docs/oms-cli_beta.md b/docs/oms-cli_beta.md index e3969f8d..3844f398 100644 --- a/docs/oms-cli_beta.md +++ b/docs/oms-cli_beta.md @@ -16,6 +16,7 @@ Be aware that that usage and behavior may change as the features are developed. ### SEE ALSO * [oms-cli](oms-cli.md) - Codesphere Operations Management System (OMS) +* [oms-cli beta argocd](oms-cli_beta_argocd.md) - Commands to interact with ArgoCD * [oms-cli beta bootstrap-gcp](oms-cli_beta_bootstrap-gcp.md) - Bootstrap GCP infrastructure for Codesphere * [oms-cli beta extend](oms-cli_beta_extend.md) - Extend Codesphere ressources such as base images. diff --git a/docs/oms-cli_beta_argocd.md b/docs/oms-cli_beta_argocd.md new file mode 100644 index 00000000..31f2a89a --- /dev/null +++ b/docs/oms-cli_beta_argocd.md @@ -0,0 +1,16 @@ +## oms-cli beta argocd + +Commands to interact with ArgoCD + +### Options + +``` + -h, --help help for argocd +``` + +### SEE ALSO + +* [oms-cli beta](oms-cli_beta.md) - Commands for early testing +* [oms-cli beta argocd get-admin-password](oms-cli_beta_argocd_get-admin-password.md) - Retrieve the initial ArgoCD admin password +* [oms-cli beta argocd install](oms-cli_beta_argocd_install.md) - Install an ArgoCD helm release + diff --git a/docs/oms-cli_beta_argocd_get-admin-password.md b/docs/oms-cli_beta_argocd_get-admin-password.md new file mode 100644 index 00000000..7e5ca89d --- /dev/null +++ b/docs/oms-cli_beta_argocd_get-admin-password.md @@ -0,0 +1,18 @@ +## oms-cli beta argocd get-admin-password + +Retrieve the initial ArgoCD admin password + +``` +oms-cli beta argocd get-admin-password [flags] +``` + +### Options + +``` + -h, --help help for get-admin-password +``` + +### SEE ALSO + +* [oms-cli beta argocd](oms-cli_beta_argocd.md) - Commands to interact with ArgoCD + diff --git a/docs/oms-cli_beta_argocd_install.md b/docs/oms-cli_beta_argocd_install.md new file mode 100644 index 00000000..ee6e1dca --- /dev/null +++ b/docs/oms-cli_beta_argocd_install.md @@ -0,0 +1,37 @@ +## oms-cli beta argocd install + +Install an ArgoCD helm release + +### Synopsis + +Install an ArgoCD helm release + +``` +oms-cli beta argocd install [flags] +``` + +### Examples + +``` +# Install an ArgoCD helm release of chart https://argoproj.github.io/argo-helm/argo-cd +$ oms-cli install ArgoCD + +# Version of the ArgoCD helm chart to install +$ oms-cli install ArgoCD --version + +``` + +### Options + +``` + --dc-id string Codesphere Datacenter ID where this ArgoCD is installed + -c, --git-password string Password/token to read from the git repo where ArgoCD Application manifests are stored + -h, --help help for install + --registry-password string Password/token to read from the OCI registry (e.g. ghcr.io) where Helm chart artifacts are stored + -v, --version string Version of the ArgoCD helm chart to install +``` + +### SEE ALSO + +* [oms-cli beta argocd](oms-cli_beta_argocd.md) - Commands to interact with ArgoCD + diff --git a/internal/installer/manifests/argocd/app-projects.yaml b/internal/installer/manifests/argocd/app-projects.yaml index 4fa5e1ee..e573d965 100644 --- a/internal/installer/manifests/argocd/app-projects.yaml +++ b/internal/installer/manifests/argocd/app-projects.yaml @@ -1,3 +1,6 @@ +# Copyright (c) Codesphere Inc. +# SPDX-License-Identifier: Apache-2.0 + # yaml-language-server: $schema=https://raw.githubusercontent.com/datreeio/CRDs-catalog/refs/heads/main/argoproj.io/appproject_v1alpha1.json apiVersion: argoproj.io/v1alpha1 kind: AppProject diff --git a/internal/installer/mocks.go b/internal/installer/mocks.go index 79d4d7af..61a1f4dc 100644 --- a/internal/installer/mocks.go +++ b/internal/installer/mocks.go @@ -10,6 +10,77 @@ import ( mock "github.com/stretchr/testify/mock" ) +// NewMockArgoCDManager creates a new instance of MockArgoCDManager. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockArgoCDManager(t interface { + mock.TestingT + Cleanup(func()) +}) *MockArgoCDManager { + mock := &MockArgoCDManager{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// MockArgoCDManager is an autogenerated mock type for the ArgoCDManager type +type MockArgoCDManager struct { + mock.Mock +} + +type MockArgoCDManager_Expecter struct { + mock *mock.Mock +} + +func (_m *MockArgoCDManager) EXPECT() *MockArgoCDManager_Expecter { + return &MockArgoCDManager_Expecter{mock: &_m.Mock} +} + +// Install provides a mock function for the type MockArgoCDManager +func (_mock *MockArgoCDManager) Install() error { + ret := _mock.Called() + + if len(ret) == 0 { + panic("no return value specified for Install") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func() error); ok { + r0 = returnFunc() + } else { + r0 = ret.Error(0) + } + return r0 +} + +// MockArgoCDManager_Install_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Install' +type MockArgoCDManager_Install_Call struct { + *mock.Call +} + +// Install is a helper method to define mock.On call +func (_e *MockArgoCDManager_Expecter) Install() *MockArgoCDManager_Install_Call { + return &MockArgoCDManager_Install_Call{Call: _e.mock.On("Install")} +} + +func (_c *MockArgoCDManager_Install_Call) Run(run func()) *MockArgoCDManager_Install_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockArgoCDManager_Install_Call) Return(err error) *MockArgoCDManager_Install_Call { + _c.Call.Return(err) + return _c +} + +func (_c *MockArgoCDManager_Install_Call) RunAndReturn(run func() error) *MockArgoCDManager_Install_Call { + _c.Call.Return(run) + return _c +} + // NewMockConfigManager creates a new instance of MockConfigManager. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewMockConfigManager(t interface { diff --git a/internal/tmpl/NOTICE b/internal/tmpl/NOTICE index 6fcd73a7..1fadc113 100644 --- a/internal/tmpl/NOTICE +++ b/internal/tmpl/NOTICE @@ -63,24 +63,84 @@ Version: v0.23.2 License: MIT License URL: https://gitea.com/gitea/go-sdk/src/tag/gitea/v0.23.2/gitea/LICENSE +---------- +Module: dario.cat/mergo +Version: v1.0.2 +License: BSD-3-Clause +License URL: https://github.com/imdario/mergo/blob/v1.0.2/LICENSE + ---------- Module: github.com/42wim/httpsig Version: v1.2.3 License: BSD-3-Clause License URL: https://github.com/42wim/httpsig/blob/v1.2.3/LICENSE +---------- +Module: github.com/BurntSushi/toml +Version: v1.6.0 +License: MIT +License URL: https://github.com/BurntSushi/toml/blob/v1.6.0/COPYING + +---------- +Module: github.com/MakeNowJust/heredoc +Version: v1.0.0 +License: MIT +License URL: https://github.com/MakeNowJust/heredoc/blob/v1.0.0/LICENSE + +---------- +Module: github.com/Masterminds/goutils +Version: v1.1.1 +License: Apache-2.0 +License URL: https://github.com/Masterminds/goutils/blob/v1.1.1/LICENSE.txt + ---------- Module: github.com/Masterminds/semver/v3 Version: v3.4.0 License: MIT License URL: https://github.com/Masterminds/semver/blob/v3.4.0/LICENSE.txt +---------- +Module: github.com/Masterminds/sprig/v3 +Version: v3.3.0 +License: MIT +License URL: https://github.com/Masterminds/sprig/blob/v3.3.0/LICENSE.txt + +---------- +Module: github.com/Masterminds/squirrel +Version: v1.5.4 +License: MIT +License URL: https://github.com/Masterminds/squirrel/blob/v1.5.4/LICENSE + +---------- +Module: github.com/ProtonMail/go-crypto +Version: v1.3.0 +License: BSD-3-Clause +License URL: https://github.com/ProtonMail/go-crypto/blob/v1.3.0/LICENSE + +---------- +Module: github.com/asaskevich/govalidator +Version: v0.0.0-20230301143203-a9d515a09cc2 +License: MIT +License URL: https://github.com/asaskevich/govalidator/blob/a9d515a09cc2/LICENSE + +---------- +Module: github.com/blang/semver/v4 +Version: v4.0.0 +License: MIT +License URL: https://github.com/blang/semver/blob/v4.0.0/v4/LICENSE + ---------- Module: github.com/cespare/xxhash/v2 Version: v2.3.0 License: MIT License URL: https://github.com/cespare/xxhash/blob/v2.3.0/LICENSE.txt +---------- +Module: github.com/chai2010/gettext-go +Version: v1.0.2 +License: BSD-3-Clause +License URL: https://github.com/chai2010/gettext-go/blob/v1.0.2/LICENSE + ---------- Module: github.com/clipperhouse/stringish Version: v0.1.1 @@ -93,6 +153,12 @@ Version: v2.4.0 License: MIT License URL: https://github.com/clipperhouse/uax29/blob/v2.4.0/LICENSE +---------- +Module: github.com/cloudflare/circl +Version: v1.6.3 +License: BSD-3-Clause +License URL: https://github.com/cloudflare/circl/blob/v1.6.3/LICENSE + ---------- Module: github.com/codesphere-cloud/cs-go Version: v0.17.2 @@ -117,24 +183,96 @@ Version: v1.5.2 License: MIT License URL: https://github.com/creativeprojects/go-selfupdate/blob/v1.5.2/LICENSE +---------- +Module: github.com/cyphar/filepath-securejoin +Version: v0.6.1 +License: MPL-2.0 +License URL: https://github.com/cyphar/filepath-securejoin/blob/v0.6.1/COPYING.md + +---------- +Module: github.com/cyphar/filepath-securejoin +Version: v0.6.1 +License: BSD-3-Clause +License URL: https://github.com/cyphar/filepath-securejoin/blob/v0.6.1/COPYING.md + ---------- Module: github.com/davecgh/go-spew/spew Version: v1.1.2-0.20180830191138-d8f796af33cc License: ISC License URL: https://github.com/davecgh/go-spew/blob/d8f796af33cc/LICENSE +---------- +Module: github.com/dylibso/observe-sdk/go +Version: v0.0.0-20240819160327-2d926c5d788a +License: Apache-2.0 +License URL: https://github.com/dylibso/observe-sdk/blob/2d926c5d788a/go/LICENSE + +---------- +Module: github.com/emicklei/go-restful/v3 +Version: v3.12.2 +License: MIT +License URL: https://github.com/emicklei/go-restful/blob/v3.12.2/LICENSE + +---------- +Module: github.com/evanphx/json-patch/v5 +Version: v5.9.11 +License: BSD-3-Clause +License URL: https://github.com/evanphx/json-patch/blob/v5.9.11/v5/LICENSE + +---------- +Module: github.com/exponent-io/jsonpath +Version: v0.0.0-20210407135951-1de76d718b3f +License: MIT +License URL: https://github.com/exponent-io/jsonpath/blob/1de76d718b3f/LICENSE + +---------- +Module: github.com/extism/go-sdk +Version: v1.7.1 +License: BSD-3-Clause +License URL: https://github.com/extism/go-sdk/blob/v1.7.1/LICENSE + +---------- +Module: github.com/fatih/color +Version: v1.18.0 +License: MIT +License URL: https://github.com/fatih/color/blob/v1.18.0/LICENSE.md + ---------- Module: github.com/felixge/httpsnoop Version: v1.0.4 License: MIT License URL: https://github.com/felixge/httpsnoop/blob/v1.0.4/LICENSE.txt +---------- +Module: github.com/fluxcd/cli-utils/pkg +Version: v0.37.0-flux.1 +License: Apache-2.0 +License URL: https://github.com/fluxcd/cli-utils/blob/v0.37.0-flux.1/LICENSE + +---------- +Module: github.com/fxamacker/cbor/v2 +Version: v2.9.0 +License: MIT +License URL: https://github.com/fxamacker/cbor/blob/v2.9.0/LICENSE + +---------- +Module: github.com/go-errors/errors +Version: v1.5.1 +License: MIT +License URL: https://github.com/go-errors/errors/blob/v1.5.1/LICENSE.MIT + ---------- Module: github.com/go-fed/httpsig Version: v1.1.0 License: BSD-3-Clause License URL: https://github.com/go-fed/httpsig/blob/v1.1.0/LICENSE +---------- +Module: github.com/go-gorp/gorp/v3 +Version: v3.1.0 +License: MIT +License URL: https://github.com/go-gorp/gorp/blob/v3.1.0/LICENSE + ---------- Module: github.com/go-logr/logr Version: v1.4.3 @@ -147,6 +285,108 @@ Version: v1.2.2 License: Apache-2.0 License URL: https://github.com/go-logr/stdr/blob/v1.2.2/LICENSE +---------- +Module: github.com/go-openapi/jsonpointer +Version: v0.22.4 +License: Apache-2.0 +License URL: https://github.com/go-openapi/jsonpointer/blob/v0.22.4/LICENSE + +---------- +Module: github.com/go-openapi/jsonreference +Version: v0.21.4 +License: Apache-2.0 +License URL: https://github.com/go-openapi/jsonreference/blob/v0.21.4/LICENSE + +---------- +Module: github.com/go-openapi/swag +Version: v0.25.4 +License: Apache-2.0 +License URL: https://github.com/go-openapi/swag/blob/v0.25.4/LICENSE + +---------- +Module: github.com/go-openapi/swag/cmdutils +Version: v0.25.4 +License: Apache-2.0 +License URL: https://github.com/go-openapi/swag/blob/cmdutils/v0.25.4/cmdutils/LICENSE + +---------- +Module: github.com/go-openapi/swag/conv +Version: v0.25.4 +License: Apache-2.0 +License URL: https://github.com/go-openapi/swag/blob/conv/v0.25.4/conv/LICENSE + +---------- +Module: github.com/go-openapi/swag/fileutils +Version: v0.25.4 +License: Apache-2.0 +License URL: https://github.com/go-openapi/swag/blob/fileutils/v0.25.4/fileutils/LICENSE + +---------- +Module: github.com/go-openapi/swag/jsonname +Version: v0.25.4 +License: Apache-2.0 +License URL: https://github.com/go-openapi/swag/blob/jsonname/v0.25.4/jsonname/LICENSE + +---------- +Module: github.com/go-openapi/swag/jsonutils +Version: v0.25.4 +License: Apache-2.0 +License URL: https://github.com/go-openapi/swag/blob/jsonutils/v0.25.4/jsonutils/LICENSE + +---------- +Module: github.com/go-openapi/swag/loading +Version: v0.25.4 +License: Apache-2.0 +License URL: https://github.com/go-openapi/swag/blob/loading/v0.25.4/loading/LICENSE + +---------- +Module: github.com/go-openapi/swag/mangling +Version: v0.25.4 +License: Apache-2.0 +License URL: https://github.com/go-openapi/swag/blob/mangling/v0.25.4/mangling/LICENSE + +---------- +Module: github.com/go-openapi/swag/netutils +Version: v0.25.4 +License: Apache-2.0 +License URL: https://github.com/go-openapi/swag/blob/netutils/v0.25.4/netutils/LICENSE + +---------- +Module: github.com/go-openapi/swag/stringutils +Version: v0.25.4 +License: Apache-2.0 +License URL: https://github.com/go-openapi/swag/blob/stringutils/v0.25.4/stringutils/LICENSE + +---------- +Module: github.com/go-openapi/swag/typeutils +Version: v0.25.4 +License: Apache-2.0 +License URL: https://github.com/go-openapi/swag/blob/typeutils/v0.25.4/typeutils/LICENSE + +---------- +Module: github.com/go-openapi/swag/yamlutils +Version: v0.25.4 +License: Apache-2.0 +License URL: https://github.com/go-openapi/swag/blob/yamlutils/v0.25.4/yamlutils/LICENSE + +---------- +Module: github.com/gobwas/glob +Version: v0.2.3 +License: MIT +License URL: https://github.com/gobwas/glob/blob/v0.2.3/LICENSE + +---------- +Module: github.com/google/btree +Version: v1.1.3 +License: Apache-2.0 +License URL: https://github.com/google/btree/blob/v1.1.3/LICENSE + +---------- +Module: github.com/google/gnostic-models +Version: v0.7.0 +License: Apache-2.0 +License URL: https://github.com/google/gnostic-models/blob/v0.7.0/LICENSE + ---------- Module: github.com/google/go-github/v74/github Version: v74.0.0 @@ -183,6 +423,24 @@ Version: v2.17.0 License: BSD-3-Clause License URL: https://github.com/googleapis/gax-go/blob/v2.17.0/v2/LICENSE +---------- +Module: github.com/gosuri/uitable +Version: v0.0.4 +License: MIT +License URL: https://github.com/gosuri/uitable/blob/v0.0.4/LICENSE + +---------- +Module: github.com/gosuri/uitable/util/wordwrap +Version: v0.0.4 +License: MIT +License URL: https://github.com/gosuri/uitable/blob/v0.0.4/util/wordwrap/LICENSE.md + +---------- +Module: github.com/gregjones/httpcache +Version: v0.0.0-20190611155906-901d90724c79 +License: MIT +License URL: https://github.com/gregjones/httpcache/blob/901d90724c79/LICENSE.txt + ---------- Module: github.com/hashicorp/go-cleanhttp Version: v0.5.2 @@ -201,30 +459,156 @@ Version: v1.8.0 License: MPL-2.0 License URL: https://github.com/hashicorp/go-version/blob/v1.8.0/LICENSE +---------- +Module: github.com/huandu/xstrings +Version: v1.5.0 +License: MIT +License URL: https://github.com/huandu/xstrings/blob/v1.5.0/LICENSE + +---------- +Module: github.com/ianlancetaylor/demangle +Version: v0.0.0-20250417193237-f615e6bd150b +License: BSD-3-Clause +License URL: https://github.com/ianlancetaylor/demangle/blob/f615e6bd150b/LICENSE + ---------- Module: github.com/jedib0t/go-pretty/v6 Version: v6.7.8 License: MIT License URL: https://github.com/jedib0t/go-pretty/blob/v6.7.8/LICENSE +---------- +Module: github.com/jmoiron/sqlx +Version: v1.4.0 +License: MIT +License URL: https://github.com/jmoiron/sqlx/blob/v1.4.0/LICENSE + +---------- +Module: github.com/json-iterator/go +Version: v1.1.12 +License: MIT +License URL: https://github.com/json-iterator/go/blob/v1.1.12/LICENSE + ---------- Module: github.com/kr/fs Version: v0.1.0 License: BSD-3-Clause License URL: https://github.com/kr/fs/blob/v0.1.0/LICENSE +---------- +Module: github.com/lann/builder +Version: v0.0.0-20180802200727-47ae307949d0 +License: MIT +License URL: https://github.com/lann/builder/blob/47ae307949d0/LICENSE + +---------- +Module: github.com/lann/ps +Version: v0.0.0-20150810152359-62de8c46ede0 +License: MIT +License URL: https://github.com/lann/ps/blob/62de8c46ede0/LICENSE + +---------- +Module: github.com/lib/pq +Version: v1.11.1 +License: MIT +License URL: https://github.com/lib/pq/blob/v1.11.1/LICENSE + +---------- +Module: github.com/liggitt/tabwriter +Version: v0.0.0-20181228230101-89fcab3d43de +License: BSD-3-Clause +License URL: https://github.com/liggitt/tabwriter/blob/89fcab3d43de/LICENSE + ---------- Module: github.com/lithammer/shortuuid Version: v3.0.0 License: MIT License URL: https://github.com/lithammer/shortuuid/blob/v3.0.0/LICENSE +---------- +Module: github.com/mattn/go-colorable +Version: v0.1.14 +License: MIT +License URL: https://github.com/mattn/go-colorable/blob/v0.1.14/LICENSE + +---------- +Module: github.com/mattn/go-isatty +Version: v0.0.20 +License: MIT +License URL: https://github.com/mattn/go-isatty/blob/v0.0.20/LICENSE + ---------- Module: github.com/mattn/go-runewidth Version: v0.0.19 License: MIT License URL: https://github.com/mattn/go-runewidth/blob/v0.0.19/LICENSE +---------- +Module: github.com/mitchellh/copystructure +Version: v1.2.0 +License: MIT +License URL: https://github.com/mitchellh/copystructure/blob/v1.2.0/LICENSE + +---------- +Module: github.com/mitchellh/go-wordwrap +Version: v1.0.1 +License: MIT +License URL: https://github.com/mitchellh/go-wordwrap/blob/v1.0.1/LICENSE.md + +---------- +Module: github.com/mitchellh/reflectwalk +Version: v1.0.2 +License: MIT +License URL: https://github.com/mitchellh/reflectwalk/blob/v1.0.2/LICENSE + +---------- +Module: github.com/moby/term +Version: v0.5.2 +License: Apache-2.0 +License URL: https://github.com/moby/term/blob/v0.5.2/LICENSE + +---------- +Module: github.com/modern-go/concurrent +Version: v0.0.0-20180306012644-bacd9c7ef1dd +License: Apache-2.0 +License URL: https://github.com/modern-go/concurrent/blob/bacd9c7ef1dd/LICENSE + +---------- +Module: github.com/modern-go/reflect2 +Version: v1.0.3-0.20250322232337-35a7c28c31ee +License: Apache-2.0 +License URL: https://github.com/modern-go/reflect2/blob/35a7c28c31ee/LICENSE + +---------- +Module: github.com/monochromegane/go-gitignore +Version: v0.0.0-20200626010858-205db1a8cc00 +License: MIT +License URL: https://github.com/monochromegane/go-gitignore/blob/205db1a8cc00/LICENSE + +---------- +Module: github.com/munnerz/goautoneg +Version: v0.0.0-20191010083416-a7dc8b61c822 +License: BSD-3-Clause +License URL: https://github.com/munnerz/goautoneg/blob/a7dc8b61c822/LICENSE + +---------- +Module: github.com/opencontainers/go-digest +Version: v1.0.0 +License: Apache-2.0 +License URL: https://github.com/opencontainers/go-digest/blob/v1.0.0/LICENSE + +---------- +Module: github.com/opencontainers/image-spec/specs-go +Version: v1.1.1 +License: Apache-2.0 +License URL: https://github.com/opencontainers/image-spec/blob/v1.1.1/LICENSE + +---------- +Module: github.com/peterbourgon/diskv +Version: v2.0.1 +License: MIT +License URL: https://github.com/peterbourgon/diskv/blob/v2.0.1/LICENSE + ---------- Module: github.com/pkg/sftp Version: v1.13.10 @@ -237,12 +621,42 @@ Version: v1.0.1-0.20181226105442-5d4384ee4fb2 License: BSD-3-Clause License URL: https://github.com/pmezard/go-difflib/blob/5d4384ee4fb2/LICENSE +---------- +Module: github.com/rubenv/sql-migrate +Version: v1.8.1 +License: MIT +License URL: https://github.com/rubenv/sql-migrate/blob/v1.8.1/LICENSE + +---------- +Module: github.com/rubenv/sql-migrate/sqlparse +Version: v1.8.1 +License: MIT +License URL: https://github.com/rubenv/sql-migrate/blob/v1.8.1/sqlparse/LICENSE + ---------- Module: github.com/russross/blackfriday/v2 Version: v2.1.0 License: BSD-2-Clause License URL: https://github.com/russross/blackfriday/blob/v2.1.0/LICENSE.txt +---------- +Module: github.com/santhosh-tekuri/jsonschema/v6 +Version: v6.0.2 +License: Apache-2.0 +License URL: https://github.com/santhosh-tekuri/jsonschema/blob/v6.0.2/LICENSE + +---------- +Module: github.com/shopspring/decimal +Version: v1.4.0 +License: MIT +License URL: https://github.com/shopspring/decimal/blob/v1.4.0/LICENSE + +---------- +Module: github.com/spf13/cast +Version: v1.10.0 +License: MIT +License URL: https://github.com/spf13/cast/blob/v1.10.0/LICENSE + ---------- Module: github.com/spf13/cobra Version: v1.10.2 @@ -267,12 +681,36 @@ Version: v1.11.1 License: MIT License URL: https://github.com/stretchr/testify/blob/v1.11.1/LICENSE +---------- +Module: github.com/tetratelabs/wabin +Version: v0.0.0-20230304001439-f6f874872834 +License: Apache-2.0 +License URL: https://github.com/tetratelabs/wabin/blob/f6f874872834/LICENSE + +---------- +Module: github.com/tetratelabs/wazero +Version: v1.11.0 +License: Apache-2.0 +License URL: https://github.com/tetratelabs/wazero/blob/v1.11.0/LICENSE + ---------- Module: github.com/ulikunitz/xz Version: v0.5.15 License: BSD-3-Clause License URL: https://github.com/ulikunitz/xz/blob/v0.5.15/LICENSE +---------- +Module: github.com/x448/float16 +Version: v0.8.4 +License: MIT +License URL: https://github.com/x448/float16/blob/v0.8.4/LICENSE + +---------- +Module: github.com/xlab/treeprint +Version: v1.2.0 +License: MIT +License URL: https://github.com/xlab/treeprint/blob/v1.2.0/LICENSE + ---------- Module: gitlab.com/gitlab-org/api/client-go Version: v1.39.0 @@ -345,6 +783,18 @@ Version: v1.40.0 License: BSD-3-Clause License URL: https://github.com/open-telemetry/opentelemetry-go/blob/trace/v1.40.0/trace/LICENSE +---------- +Module: go.opentelemetry.io/proto/otlp +Version: v1.9.0 +License: Apache-2.0 +License URL: https://github.com/open-telemetry/opentelemetry-proto-go/blob/otlp/v1.9.0/otlp/LICENSE + +---------- +Module: go.yaml.in/yaml/v2 +Version: v2.4.3 +License: Apache-2.0 +License URL: https://github.com/yaml/go-yaml/blob/v2.4.3/LICENSE + ---------- Module: go.yaml.in/yaml/v3 Version: v3.0.4 @@ -370,7 +820,7 @@ License: BSD-3-Clause License URL: https://cs.opensource.google/go/x/oauth2/+/v0.35.0:LICENSE ---------- -Module: golang.org/x/sync/semaphore +Module: golang.org/x/sync Version: v0.19.0 License: BSD-3-Clause License URL: https://cs.opensource.google/go/x/sync/+/v0.19.0:LICENSE @@ -441,6 +891,18 @@ Version: v1.36.11 License: BSD-3-Clause License URL: https://github.com/protocolbuffers/protobuf-go/blob/v1.36.11/LICENSE +---------- +Module: gopkg.in/evanphx/json-patch.v4 +Version: v4.13.0 +License: BSD-3-Clause +License URL: https://github.com/evanphx/json-patch/blob/v4.13.0/LICENSE + +---------- +Module: gopkg.in/inf.v0 +Version: v0.9.1 +License: BSD-3-Clause +License URL: https://github.com/go-inf/inf/blob/v0.9.1/LICENSE + ---------- Module: gopkg.in/validator.v2 Version: v2.0.1 @@ -452,5 +914,173 @@ Module: gopkg.in/yaml.v3 Version: v3.0.1 License: MIT License URL: https://github.com/go-yaml/yaml/blob/v3.0.1/LICENSE + +---------- +Module: helm.sh/helm/v4 +Version: v4.1.1 +License: Apache-2.0 +License URL: https://github.com/helm/helm/blob/v4.1.1/LICENSE + +---------- +Module: k8s.io/api +Version: v0.35.1 +License: Apache-2.0 +License URL: https://github.com/kubernetes/api/blob/v0.35.1/LICENSE + +---------- +Module: k8s.io/apiextensions-apiserver/pkg/apis/apiextensions +Version: v0.35.0 +License: Apache-2.0 +License URL: https://github.com/kubernetes/apiextensions-apiserver/blob/v0.35.0/LICENSE + +---------- +Module: k8s.io/apimachinery/pkg +Version: v0.35.1 +License: Apache-2.0 +License URL: https://github.com/kubernetes/apimachinery/blob/v0.35.1/LICENSE + +---------- +Module: k8s.io/apimachinery/third_party/forked/golang +Version: v0.35.1 +License: BSD-3-Clause +License URL: https://github.com/kubernetes/apimachinery/blob/v0.35.1/third_party/forked/golang/LICENSE + +---------- +Module: k8s.io/apiserver/pkg/endpoints/deprecation +Version: v0.35.0 +License: Apache-2.0 +License URL: https://github.com/kubernetes/apiserver/blob/v0.35.0/LICENSE + +---------- +Module: k8s.io/cli-runtime/pkg +Version: v0.35.1 +License: Apache-2.0 +License URL: https://github.com/kubernetes/cli-runtime/blob/v0.35.1/LICENSE + +---------- +Module: k8s.io/client-go +Version: v0.35.1 +License: Apache-2.0 +License URL: https://github.com/kubernetes/client-go/blob/v0.35.1/LICENSE + +---------- +Module: k8s.io/client-go/third_party/forked/golang/template +Version: v0.35.1 +License: BSD-3-Clause +License URL: https://github.com/kubernetes/client-go/blob/v0.35.1/third_party/forked/golang/LICENSE + +---------- +Module: k8s.io/component-base/version +Version: v0.35.0 +License: Apache-2.0 +License URL: https://github.com/kubernetes/component-base/blob/v0.35.0/LICENSE + +---------- +Module: k8s.io/klog/v2 +Version: v2.130.1 +License: Apache-2.0 +License URL: https://github.com/kubernetes/klog/blob/v2.130.1/LICENSE + +---------- +Module: k8s.io/kube-openapi/pkg +Version: v0.0.0-20251125145642-4e65d59e963e +License: Apache-2.0 +License URL: https://github.com/kubernetes/kube-openapi/blob/4e65d59e963e/LICENSE + +---------- +Module: k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json +Version: v0.0.0-20251125145642-4e65d59e963e +License: BSD-3-Clause +License URL: https://github.com/kubernetes/kube-openapi/blob/4e65d59e963e/pkg/internal/third_party/go-json-experiment/json/LICENSE + +---------- +Module: k8s.io/kube-openapi/pkg/validation/spec +Version: v0.0.0-20251125145642-4e65d59e963e +License: Apache-2.0 +License URL: https://github.com/kubernetes/kube-openapi/blob/4e65d59e963e/pkg/validation/spec/LICENSE + +---------- +Module: k8s.io/kubectl/pkg +Version: v0.35.0 +License: Apache-2.0 +License URL: https://github.com/kubernetes/kubectl/blob/v0.35.0/LICENSE + +---------- +Module: k8s.io/utils +Version: v0.0.0-20260106112306-0fe9cd71b2f8 +License: Apache-2.0 +License URL: https://github.com/kubernetes/utils/blob/0fe9cd71b2f8/LICENSE + +---------- +Module: k8s.io/utils/internal/third_party/forked/golang/net +Version: v0.0.0-20260106112306-0fe9cd71b2f8 +License: BSD-3-Clause +License URL: https://github.com/kubernetes/utils/blob/0fe9cd71b2f8/internal/third_party/forked/golang/LICENSE + +---------- +Module: oras.land/oras-go/v2 +Version: v2.6.0 +License: Apache-2.0 +License URL: https://github.com/oras-project/oras-go/blob/v2.6.0/LICENSE + +---------- +Module: sigs.k8s.io/controller-runtime/pkg +Version: v0.22.4 +License: Apache-2.0 +License URL: https://github.com/kubernetes-sigs/controller-runtime/blob/v0.22.4/LICENSE + +---------- +Module: sigs.k8s.io/json +Version: v0.0.0-20250730193827-2d320260d730 +License: Apache-2.0 +License URL: https://github.com/kubernetes-sigs/json/blob/2d320260d730/LICENSE + +---------- +Module: sigs.k8s.io/json +Version: v0.0.0-20250730193827-2d320260d730 +License: BSD-3-Clause +License URL: https://github.com/kubernetes-sigs/json/blob/2d320260d730/LICENSE + +---------- +Module: sigs.k8s.io/kustomize/api +Version: v0.20.1 +License: Apache-2.0 +License URL: https://github.com/kubernetes-sigs/kustomize/blob/api/v0.20.1/api/LICENSE + +---------- +Module: sigs.k8s.io/kustomize/kyaml +Version: v0.21.0 +License: Apache-2.0 +License URL: https://github.com/kubernetes-sigs/kustomize/blob/kyaml/v0.21.0/kyaml/LICENSE + +---------- +Module: sigs.k8s.io/randfill +Version: v1.0.0 +License: Apache-2.0 +License URL: https://github.com/kubernetes-sigs/randfill/blob/v1.0.0/LICENSE + +---------- +Module: sigs.k8s.io/structured-merge-diff/v6 +Version: v6.3.1 +License: Apache-2.0 +License URL: https://github.com/kubernetes-sigs/structured-merge-diff/blob/v6.3.1/LICENSE + +---------- +Module: sigs.k8s.io/yaml +Version: v1.6.0 +License: MIT +License URL: https://github.com/kubernetes-sigs/yaml/blob/v1.6.0/LICENSE + +---------- +Module: sigs.k8s.io/yaml +Version: v1.6.0 +License: Apache-2.0 +License URL: https://github.com/kubernetes-sigs/yaml/blob/v1.6.0/LICENSE + +---------- +Module: sigs.k8s.io/yaml +Version: v1.6.0 +License: BSD-3-Clause +License URL: https://github.com/kubernetes-sigs/yaml/blob/v1.6.0/LICENSE From e23e649b31e5fe01f17a41319cf254033fc2b15c Mon Sep 17 00:00:00 2001 From: bachgg Date: Fri, 6 Mar 2026 13:13:37 +0100 Subject: [PATCH 13/33] change command to `oms beta install argocd` --- cli/cmd/argocd.go | 51 +++++++----------------------------- cli/cmd/beta.go | 2 +- cli/cmd/beta_install.go | 24 +++++++++++++++++ cli/cmd/install.go | 2 +- internal/installer/argocd.go | 12 +++------ 5 files changed, 38 insertions(+), 53 deletions(-) create mode 100644 cli/cmd/beta_install.go diff --git a/cli/cmd/argocd.go b/cli/cmd/argocd.go index 5c1a6950..b5ce2aa8 100644 --- a/cli/cmd/argocd.go +++ b/cli/cmd/argocd.go @@ -5,7 +5,6 @@ package cmd import ( "fmt" - "log" "github.com/codesphere-cloud/cs-go/pkg/io" packageio "github.com/codesphere-cloud/cs-go/pkg/io" @@ -13,10 +12,6 @@ import ( "github.com/spf13/cobra" ) -type ArgoCDCmd struct { - cmd *cobra.Command -} - // InstallArgoCDCmd represents the argocd command type InstallArgoCDCmd struct { cmd *cobra.Command @@ -41,31 +36,14 @@ func (c *InstallArgoCDCmd) RunE(_ *cobra.Command, args []string) error { return nil } -type GetAdminPasswordCmd struct { - cmd *cobra.Command -} - -func (c *GetAdminPasswordCmd) RunE(_ *cobra.Command, args []string) error { - log.Println("Not implemented") - return nil -} - type Config struct { cmd *cobra.Command } func AddArgoCDCmd(parentCmd *cobra.Command, opts *GlobalOptions) { - argocd := ArgoCDCmd{ + argocd := InstallArgoCDCmd{ cmd: &cobra.Command{ Use: "argocd", - Short: "Commands to interact with ArgoCD", - }, - } - - // argocd install - install := InstallArgoCDCmd{ - cmd: &cobra.Command{ - Use: "install", Short: "Install an ArgoCD helm release", Long: io.Long(`Install an ArgoCD helm release`), Example: formatExamplesWithBinary("install ArgoCD", []packageio.Example{ @@ -74,25 +52,14 @@ func AddArgoCDCmd(parentCmd *cobra.Command, opts *GlobalOptions) { }, "oms-cli"), }, } - install.cmd.Flags().StringVarP(&install.Opts.GitPassword, "git-password", "c", "", "Password/token to read from the git repo where ArgoCD Application manifests are stored") - install.cmd.MarkFlagRequired("git-password") - install.cmd.Flags().StringVar(&install.Opts.RegistryPassword, "registry-password", "", "Password/token to read from the OCI registry (e.g. ghcr.io) where Helm chart artifacts are stored") - install.cmd.MarkFlagRequired("registry-password") - install.cmd.Flags().StringVar(&install.Opts.DatacenterId, "dc-id", "", "Codesphere Datacenter ID where this ArgoCD is installed") - install.cmd.MarkFlagRequired("dc-id") - install.cmd.Flags().StringVarP(&install.Opts.Version, "version", "v", "", "Version of the ArgoCD helm chart to install") - install.cmd.RunE = install.RunE - argocd.cmd.AddCommand(install.cmd) - - // argocd get-admin-password - getAdminPassword := GetAdminPasswordCmd{ - cmd: &cobra.Command{ - Use: "get-admin-password", - Short: "Retrieve the initial ArgoCD admin password", - }, - } - getAdminPassword.cmd.RunE = getAdminPassword.RunE - argocd.cmd.AddCommand(getAdminPassword.cmd) + argocd.cmd.Flags().StringVarP(&argocd.Opts.GitPassword, "git-password", "c", "", "Password/token to read from the git repo where ArgoCD Application manifests are stored") + argocd.cmd.MarkFlagRequired("git-password") + 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.MarkFlagRequired("registry-password") + argocd.cmd.Flags().StringVar(&argocd.Opts.DatacenterId, "dc-id", "", "Codesphere Datacenter ID where this ArgoCD is installed") + argocd.cmd.MarkFlagRequired("dc-id") + argocd.cmd.Flags().StringVarP(&argocd.Opts.Version, "version", "v", "", "Version of the ArgoCD helm chart to install") + argocd.cmd.RunE = argocd.RunE parentCmd.AddCommand(argocd.cmd) } diff --git a/cli/cmd/beta.go b/cli/cmd/beta.go index b56a5a22..40cd1f9c 100644 --- a/cli/cmd/beta.go +++ b/cli/cmd/beta.go @@ -25,5 +25,5 @@ func AddBetaCmd(rootCmd *cobra.Command, opts *GlobalOptions) { AddExtendCmd(beta.cmd, opts) AddBootstrapGcpCmd(beta.cmd, opts) - AddArgoCDCmd(beta.cmd, opts) + AddBetaInstallCmd(beta.cmd, opts) } diff --git a/cli/cmd/beta_install.go b/cli/cmd/beta_install.go new file mode 100644 index 00000000..19ba2e1c --- /dev/null +++ b/cli/cmd/beta_install.go @@ -0,0 +1,24 @@ +// Copyright (c) Codesphere Inc. +// SPDX-License-Identifier: Apache-2.0 + +package cmd + +import ( + "github.com/spf13/cobra" +) + +// BetaInstallCmd represents the install command +type BetaInstallCmd struct { + cmd *cobra.Command +} + +func AddBetaInstallCmd(rootCmd *cobra.Command, opts *GlobalOptions) { + install := BetaInstallCmd{ + cmd: &cobra.Command{ + Use: "install", + Short: "Install beta components", + }, + } + rootCmd.AddCommand(install.cmd) + AddArgoCDCmd(install.cmd, opts) +} diff --git a/cli/cmd/install.go b/cli/cmd/install.go index 84ed005e..13f5329a 100644 --- a/cli/cmd/install.go +++ b/cli/cmd/install.go @@ -14,7 +14,7 @@ type InstallCmd struct { } func AddInstallCmd(rootCmd *cobra.Command, opts *GlobalOptions) { - install := InstallCmd{ + install := BetaInstallCmd{ cmd: &cobra.Command{ Use: "install", Short: "Install Codesphere and other components", diff --git a/internal/installer/argocd.go b/internal/installer/argocd.go index 74f158b8..b9f891e7 100644 --- a/internal/installer/argocd.go +++ b/internal/installer/argocd.go @@ -49,21 +49,15 @@ func (a *ArgoCD) applyPostInstallResources() error { return fmt.Errorf("applying app projects: %w", err) } - dcNumber := "1" - - if err := applyLocalCluster(ctx, clientset, dcNumber); err != nil { + if err := applyLocalCluster(ctx, clientset, a.DcNumber); err != nil { return fmt.Errorf("applying local cluster secret: %w", err) } - secretOci := "abc" - - if err := applyHelmRegistrySecret(ctx, clientset, secretOci); err != nil { + if err := applyHelmRegistrySecret(ctx, clientset, a.PasswordOCI); err != nil { return fmt.Errorf("applying helm registry secret: %w", err) } - secretGit := "xyz" - - if err := applyGitRepoSecret(ctx, clientset, secretGit); err != nil { + if err := applyGitRepoSecret(ctx, clientset, a.PasswordGit); err != nil { return fmt.Errorf("applying git repo secret: %w", err) } From 3d013d497998e0819c74abe8577eef74f524c4be Mon Sep 17 00:00:00 2001 From: bachgg Date: Fri, 6 Mar 2026 13:18:01 +0100 Subject: [PATCH 14/33] add postinstall hint --- internal/installer/argocd.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/internal/installer/argocd.go b/internal/installer/argocd.go index b9f891e7..b9fcbe0e 100644 --- a/internal/installer/argocd.go +++ b/internal/installer/argocd.go @@ -64,6 +64,13 @@ func (a *ArgoCD) applyPostInstallResources() error { return nil } +func 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`) + log.Println(`To port-forward ArgoCD UI to localhost:8080:`) + log.Println(` kubectl port-forward svc/argocd-server 8080:80 -nargocd`) +} + // Install the ArgoCD chart func (a *ArgoCD) Install() error { if a.Version == "" { @@ -199,5 +206,7 @@ func (a *ArgoCD) Install() error { return fmt.Errorf("failed apply post chart install resources: %v", err) } + showPostInstallHints() + return nil } From 87b5e4faf0514a9a03d8de0b464f3f11c5e5481c Mon Sep 17 00:00:00 2001 From: bachgg Date: Fri, 6 Mar 2026 13:37:03 +0100 Subject: [PATCH 15/33] install latest chart if no version is provided --- internal/installer/argocd.go | 62 ++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/internal/installer/argocd.go b/internal/installer/argocd.go index b9fcbe0e..3a668b4d 100644 --- a/internal/installer/argocd.go +++ b/internal/installer/argocd.go @@ -73,10 +73,11 @@ func showPostInstallHints() { // Install the ArgoCD chart func (a *ArgoCD) Install() error { - if a.Version == "" { - a.Version = "9.1.4" + if a.Version != "" { + log.Printf("Installing/Upgrading ArgoCD helm chart version %s\n", a.Version) + } else { + log.Println("Installing/Upgrading ArgoCD helm chart (latest version)") } - log.Printf("Installing/Upgrading ArgoCD helm chart version %s\n", a.Version) settings := cli.New() ctx := context.Background() @@ -129,29 +130,34 @@ func (a *ArgoCD) Install() error { installedVersion, _ := metadata["Version"].(string) log.Printf("Found existing ArgoCD release with chart version %s\n", installedVersion) - installedSemver, err := semver.NewVersion(installedVersion) - if err != nil { - return fmt.Errorf("failed to parse installed version %q: %w", installedVersion, err) - } - requestedSemver, err := semver.NewVersion(a.Version) - if err != nil { - return fmt.Errorf("failed to parse requested version %q: %w", a.Version, err) + // Only perform version comparison if a specific version was requested + if a.Version != "" { + installedSemver, err := semver.NewVersion(installedVersion) + if err != nil { + return fmt.Errorf("failed to parse installed version %q: %w", installedVersion, err) + } + requestedSemver, err := semver.NewVersion(a.Version) + if err != nil { + return fmt.Errorf("failed to parse requested version %q: %w", a.Version, err) + } + + if requestedSemver.LessThan(installedSemver) { + return fmt.Errorf( + "requested version %s is older than installed version %s; downgrade is not allowed", + a.Version, installedVersion, + ) + } + + log.Printf("Upgrading ArgoCD from %s to %s\n", installedVersion, a.Version) + } else { + log.Printf("Upgrading ArgoCD from %s to latest\n", installedVersion) } - if requestedSemver.LessThan(installedSemver) { - return fmt.Errorf( - "requested version %s is older than installed version %s; downgrade is not allowed", - a.Version, installedVersion, - ) - } - - // Version is equal or larger — perform an upgrade - log.Printf("Upgrading ArgoCD from %s to %s\n", installedVersion, a.Version) - + // Version is equal, larger, or latest — perform an upgrade upgradeClient := action.NewUpgrade(actionConfig) upgradeClient.Namespace = "argocd" upgradeClient.WaitStrategy = "watcher" - upgradeClient.Version = a.Version + upgradeClient.Version = a.Version // empty string means latest upgradeClient.ChartPathOptions.RepoURL = repoURL chartPath, err := upgradeClient.ChartPathOptions.LocateChart(chartName, settings) @@ -169,7 +175,11 @@ func (a *ArgoCD) Install() error { return fmt.Errorf("upgrade failed: %w", err) } - fmt.Printf("Successfully upgraded Argo CD to chart version %s\n", a.Version) + if a.Version != "" { + fmt.Printf("Successfully upgraded Argo CD to chart version %s\n", a.Version) + } else { + fmt.Println("Successfully upgraded Argo CD to the latest chart version") + } } else { // No existing release — perform a fresh install log.Println("No existing ArgoCD release found, performing fresh install") @@ -180,7 +190,7 @@ func (a *ArgoCD) Install() error { installClient.CreateNamespace = true installClient.DryRunStrategy = "none" installClient.WaitStrategy = "watcher" - installClient.Version = a.Version + installClient.Version = a.Version // empty string means latest installClient.ChartPathOptions.RepoURL = repoURL chartPath, err := installClient.ChartPathOptions.LocateChart(chartName, settings) @@ -198,7 +208,11 @@ func (a *ArgoCD) Install() error { return fmt.Errorf("install failed: %w", err) } - fmt.Printf("Successfully installed Argo CD (chart version: %s)\n", a.Version) + if a.Version != "" { + fmt.Printf("Successfully installed Argo CD (chart version: %s)\n", a.Version) + } else { + fmt.Println("Successfully installed Argo CD (latest chart version)") + } } err = a.applyPostInstallResources() From 035a47263e4dff40222ba2a3d6bbb354d8a157b4 Mon Sep 17 00:00:00 2001 From: bachgg Date: Fri, 6 Mar 2026 13:40:44 +0100 Subject: [PATCH 16/33] remove outdated docs --- docs/oms-cli_beta_argocd.md | 16 -------- .../oms-cli_beta_argocd_get-admin-password.md | 18 --------- docs/oms-cli_beta_argocd_install.md | 37 ------------------- 3 files changed, 71 deletions(-) delete mode 100644 docs/oms-cli_beta_argocd.md delete mode 100644 docs/oms-cli_beta_argocd_get-admin-password.md delete mode 100644 docs/oms-cli_beta_argocd_install.md diff --git a/docs/oms-cli_beta_argocd.md b/docs/oms-cli_beta_argocd.md deleted file mode 100644 index 31f2a89a..00000000 --- a/docs/oms-cli_beta_argocd.md +++ /dev/null @@ -1,16 +0,0 @@ -## oms-cli beta argocd - -Commands to interact with ArgoCD - -### Options - -``` - -h, --help help for argocd -``` - -### SEE ALSO - -* [oms-cli beta](oms-cli_beta.md) - Commands for early testing -* [oms-cli beta argocd get-admin-password](oms-cli_beta_argocd_get-admin-password.md) - Retrieve the initial ArgoCD admin password -* [oms-cli beta argocd install](oms-cli_beta_argocd_install.md) - Install an ArgoCD helm release - diff --git a/docs/oms-cli_beta_argocd_get-admin-password.md b/docs/oms-cli_beta_argocd_get-admin-password.md deleted file mode 100644 index 7e5ca89d..00000000 --- a/docs/oms-cli_beta_argocd_get-admin-password.md +++ /dev/null @@ -1,18 +0,0 @@ -## oms-cli beta argocd get-admin-password - -Retrieve the initial ArgoCD admin password - -``` -oms-cli beta argocd get-admin-password [flags] -``` - -### Options - -``` - -h, --help help for get-admin-password -``` - -### SEE ALSO - -* [oms-cli beta argocd](oms-cli_beta_argocd.md) - Commands to interact with ArgoCD - diff --git a/docs/oms-cli_beta_argocd_install.md b/docs/oms-cli_beta_argocd_install.md deleted file mode 100644 index ee6e1dca..00000000 --- a/docs/oms-cli_beta_argocd_install.md +++ /dev/null @@ -1,37 +0,0 @@ -## oms-cli beta argocd install - -Install an ArgoCD helm release - -### Synopsis - -Install an ArgoCD helm release - -``` -oms-cli beta argocd install [flags] -``` - -### Examples - -``` -# Install an ArgoCD helm release of chart https://argoproj.github.io/argo-helm/argo-cd -$ oms-cli install ArgoCD - -# Version of the ArgoCD helm chart to install -$ oms-cli install ArgoCD --version - -``` - -### Options - -``` - --dc-id string Codesphere Datacenter ID where this ArgoCD is installed - -c, --git-password string Password/token to read from the git repo where ArgoCD Application manifests are stored - -h, --help help for install - --registry-password string Password/token to read from the OCI registry (e.g. ghcr.io) where Helm chart artifacts are stored - -v, --version string Version of the ArgoCD helm chart to install -``` - -### SEE ALSO - -* [oms-cli beta argocd](oms-cli_beta_argocd.md) - Commands to interact with ArgoCD - From 0ce9320e2b65a4d3cefef94ff6968bca959efc3c Mon Sep 17 00:00:00 2001 From: bachgg Date: Fri, 6 Mar 2026 13:47:57 +0100 Subject: [PATCH 17/33] fix updated formatExamples function --- cli/cmd/argocd.go | 4 ++-- go.mod | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cli/cmd/argocd.go b/cli/cmd/argocd.go index b5ce2aa8..7034de3c 100644 --- a/cli/cmd/argocd.go +++ b/cli/cmd/argocd.go @@ -46,10 +46,10 @@ func AddArgoCDCmd(parentCmd *cobra.Command, opts *GlobalOptions) { Use: "argocd", Short: "Install an ArgoCD helm release", Long: io.Long(`Install an ArgoCD helm release`), - Example: formatExamplesWithBinary("install ArgoCD", []packageio.Example{ + 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"}, - }, "oms-cli"), + }), }, } argocd.cmd.Flags().StringVarP(&argocd.Opts.GitPassword, "git-password", "c", "", "Password/token to read from the git repo where ArgoCD Application manifests are stored") diff --git a/go.mod b/go.mod index c575dd2b..32c1b069 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( cloud.google.com/go/iam v1.5.3 cloud.google.com/go/resourcemanager v1.10.7 cloud.google.com/go/serviceusage v1.9.7 + github.com/Masterminds/semver/v3 v3.4.0 github.com/codesphere-cloud/cs-go v0.19.1 github.com/creativeprojects/go-selfupdate v1.5.2 github.com/jedib0t/go-pretty/v6 v6.7.8 @@ -23,6 +24,9 @@ require ( google.golang.org/api v0.269.0 google.golang.org/grpc v1.79.1 helm.sh/helm/v4 v4.1.1 + k8s.io/api v0.35.1 + k8s.io/apimachinery v0.35.1 + k8s.io/client-go v0.35.1 ) require ( @@ -504,13 +508,12 @@ require ( mvdan.cc/gofumpt v0.9.2 // indirect mvdan.cc/unparam v0.0.0-20251027182757-5beb8c8f8f15 // indirect sigs.k8s.io/kind v0.31.0 // indirect - sigs.k8s.io/yaml v1.6.0 // indirect + sigs.k8s.io/yaml v1.6.0 software.sslmate.com/src/go-pkcs12 v0.7.0 // indirect ) require ( github.com/MakeNowJust/heredoc v1.0.0 // indirect - github.com/Masterminds/semver/v3 v3.4.0 // indirect github.com/Masterminds/squirrel v1.5.4 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect @@ -582,12 +585,9 @@ require ( gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.35.1 // indirect k8s.io/apiextensions-apiserver v0.35.0 // indirect - k8s.io/apimachinery v0.35.1 // indirect k8s.io/apiserver v0.35.0 // indirect k8s.io/cli-runtime v0.35.1 // indirect - k8s.io/client-go v0.35.1 // indirect k8s.io/component-base v0.35.0 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e // indirect From adfab7ce3d7b49c88102f61177d13d8817073b68 Mon Sep 17 00:00:00 2001 From: bachgg <82871810+bachgg@users.noreply.github.com> Date: Fri, 6 Mar 2026 12:50:38 +0000 Subject: [PATCH 18/33] chore(docs): Auto-update docs and licenses Signed-off-by: bachgg <82871810+bachgg@users.noreply.github.com> --- docs/oms_beta.md | 1 + docs/oms_beta_install.md | 15 +++++++++++++ docs/oms_beta_install_argocd.md | 37 +++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 docs/oms_beta_install.md create mode 100644 docs/oms_beta_install_argocd.md diff --git a/docs/oms_beta.md b/docs/oms_beta.md index a3099355..8c458add 100644 --- a/docs/oms_beta.md +++ b/docs/oms_beta.md @@ -18,4 +18,5 @@ Be aware that that usage and behavior may change as the features are developed. * [oms](oms.md) - Codesphere Operations Management System (OMS) * [oms beta bootstrap-gcp](oms_beta_bootstrap-gcp.md) - Bootstrap GCP infrastructure for Codesphere * [oms beta extend](oms_beta_extend.md) - Extend Codesphere ressources such as base images. +* [oms beta install](oms_beta_install.md) - Install beta components diff --git a/docs/oms_beta_install.md b/docs/oms_beta_install.md new file mode 100644 index 00000000..6868e530 --- /dev/null +++ b/docs/oms_beta_install.md @@ -0,0 +1,15 @@ +## oms beta install + +Install beta components + +### Options + +``` + -h, --help help for install +``` + +### SEE ALSO + +* [oms beta](oms_beta.md) - Commands for early testing +* [oms beta install argocd](oms_beta_install_argocd.md) - Install an ArgoCD helm release + diff --git a/docs/oms_beta_install_argocd.md b/docs/oms_beta_install_argocd.md new file mode 100644 index 00000000..920b5584 --- /dev/null +++ b/docs/oms_beta_install_argocd.md @@ -0,0 +1,37 @@ +## oms beta install argocd + +Install an ArgoCD helm release + +### Synopsis + +Install an ArgoCD helm release + +``` +oms beta install argocd [flags] +``` + +### Examples + +``` +# Install an ArgoCD helm release of chart https://argoproj.github.io/argo-helm/argo-cd +$ oms install ArgoCD + +# Version of the ArgoCD helm chart to install +$ oms install ArgoCD --version + +``` + +### Options + +``` + --dc-id string Codesphere Datacenter ID where this ArgoCD is installed + -c, --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 + -v, --version string Version of the ArgoCD helm chart to install +``` + +### SEE ALSO + +* [oms beta install](oms_beta_install.md) - Install beta components + From 6dedfcd77e620013b0b7e011f88fec8f026c7298 Mon Sep 17 00:00:00 2001 From: bachgg Date: Fri, 6 Mar 2026 13:58:01 +0100 Subject: [PATCH 19/33] fix wrong install command --- cli/cmd/install.go | 2 +- go.mod | 4 ++-- go.sum | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cli/cmd/install.go b/cli/cmd/install.go index 13f5329a..84ed005e 100644 --- a/cli/cmd/install.go +++ b/cli/cmd/install.go @@ -14,7 +14,7 @@ type InstallCmd struct { } func AddInstallCmd(rootCmd *cobra.Command, opts *GlobalOptions) { - install := BetaInstallCmd{ + install := InstallCmd{ cmd: &cobra.Command{ Use: "install", Short: "Install Codesphere and other components", diff --git a/go.mod b/go.mod index 32c1b069..8cd1896d 100644 --- a/go.mod +++ b/go.mod @@ -4,12 +4,12 @@ go 1.26.0 require ( cloud.google.com/go/artifactregistry v1.20.0 - cloud.google.com/go/compute v1.55.0 + cloud.google.com/go/compute v1.54.0 cloud.google.com/go/iam v1.5.3 cloud.google.com/go/resourcemanager v1.10.7 cloud.google.com/go/serviceusage v1.9.7 github.com/Masterminds/semver/v3 v3.4.0 - github.com/codesphere-cloud/cs-go v0.19.1 + github.com/codesphere-cloud/cs-go v0.17.2 github.com/creativeprojects/go-selfupdate v1.5.2 github.com/jedib0t/go-pretty/v6 v6.7.8 github.com/lithammer/shortuuid v3.0.0+incompatible diff --git a/go.sum b/go.sum index 2b549f8b..bc8b3c86 100644 --- a/go.sum +++ b/go.sum @@ -17,8 +17,8 @@ cloud.google.com/go/auth v0.18.2 h1:+Nbt5Ev0xEqxlNjd6c+yYUeosQ5TtEUaNcN/3FozlaM= cloud.google.com/go/auth v0.18.2/go.mod h1:xD+oY7gcahcu7G2SG2DsBerfFxgPAJz17zz2joOFF3M= cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= -cloud.google.com/go/compute v1.55.0 h1:1roY8Wqzi8EgDPFJ8SI2v+TI7DodHNn94xQ4fvx10XU= -cloud.google.com/go/compute v1.55.0/go.mod h1:fMFC0mRv+fW2ISg7M3tpDfpZ+kkrHpC/ImNFRCYiNK0= +cloud.google.com/go/compute v1.54.0 h1:4CKmnpO+40z44bKG5bdcKxQ7ocNpRtOc9SCLLUzze1w= +cloud.google.com/go/compute v1.54.0/go.mod h1:RfBj0L1x/pIM84BrzNX2V21oEv16EKRPBiTcBRRH1Ww= cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= cloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc= @@ -377,8 +377,8 @@ github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/T github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI= github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE= github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4= -github.com/codesphere-cloud/cs-go v0.19.1 h1:VmI5V0r27LKN4yEpTiqsbipA4tEZZthRe6IODzqSKeM= -github.com/codesphere-cloud/cs-go v0.19.1/go.mod h1:YEUqT/LwrYPGWF0OooJI2fK2mjDIo/TTwtVgs3y7bOs= +github.com/codesphere-cloud/cs-go v0.17.2 h1:X004BLgPiNPQgRypc814KDbLl5FolCUs0l65cvKvMQU= +github.com/codesphere-cloud/cs-go v0.17.2/go.mod h1:0zjtYUHVoKRWrNzRxnOYxEf1zy2YS4VeB07pfCXV/2U= github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4= github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE= github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= From 2748472529c55c7b763d43e4bcb5fe4838d5f67f Mon Sep 17 00:00:00 2001 From: bachgg <82871810+bachgg@users.noreply.github.com> Date: Fri, 6 Mar 2026 12:58:47 +0000 Subject: [PATCH 20/33] chore(docs): Auto-update docs and licenses Signed-off-by: bachgg <82871810+bachgg@users.noreply.github.com> --- NOTICE | 8 ++++---- internal/tmpl/NOTICE | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/NOTICE b/NOTICE index ebeab107..1fadc113 100644 --- a/NOTICE +++ b/NOTICE @@ -23,9 +23,9 @@ License URL: https://github.com/googleapis/google-cloud-go/blob/auth/oauth2adapt ---------- Module: cloud.google.com/go/compute -Version: v1.55.0 +Version: v1.54.0 License: Apache-2.0 -License URL: https://github.com/googleapis/google-cloud-go/blob/compute/v1.55.0/compute/LICENSE +License URL: https://github.com/googleapis/google-cloud-go/blob/compute/v1.54.0/compute/LICENSE ---------- Module: cloud.google.com/go/compute/metadata @@ -161,9 +161,9 @@ License URL: https://github.com/cloudflare/circl/blob/v1.6.3/LICENSE ---------- Module: github.com/codesphere-cloud/cs-go -Version: v0.19.1 +Version: v0.17.2 License: Apache-2.0 -License URL: https://github.com/codesphere-cloud/cs-go/blob/v0.19.1/LICENSE +License URL: https://github.com/codesphere-cloud/cs-go/blob/v0.17.2/LICENSE ---------- Module: github.com/codesphere-cloud/oms/internal/tmpl diff --git a/internal/tmpl/NOTICE b/internal/tmpl/NOTICE index ebeab107..1fadc113 100644 --- a/internal/tmpl/NOTICE +++ b/internal/tmpl/NOTICE @@ -23,9 +23,9 @@ License URL: https://github.com/googleapis/google-cloud-go/blob/auth/oauth2adapt ---------- Module: cloud.google.com/go/compute -Version: v1.55.0 +Version: v1.54.0 License: Apache-2.0 -License URL: https://github.com/googleapis/google-cloud-go/blob/compute/v1.55.0/compute/LICENSE +License URL: https://github.com/googleapis/google-cloud-go/blob/compute/v1.54.0/compute/LICENSE ---------- Module: cloud.google.com/go/compute/metadata @@ -161,9 +161,9 @@ License URL: https://github.com/cloudflare/circl/blob/v1.6.3/LICENSE ---------- Module: github.com/codesphere-cloud/cs-go -Version: v0.19.1 +Version: v0.17.2 License: Apache-2.0 -License URL: https://github.com/codesphere-cloud/cs-go/blob/v0.19.1/LICENSE +License URL: https://github.com/codesphere-cloud/cs-go/blob/v0.17.2/LICENSE ---------- Module: github.com/codesphere-cloud/oms/internal/tmpl From 3559622632970fc958b7cce0bbb37d6739785062 Mon Sep 17 00:00:00 2001 From: bachgg Date: Fri, 6 Mar 2026 14:04:54 +0100 Subject: [PATCH 21/33] fix linting problems --- cli/cmd/argocd.go | 16 +++++----------- internal/installer/argocd.go | 8 ++++---- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/cli/cmd/argocd.go b/cli/cmd/argocd.go index 7034de3c..6627f161 100644 --- a/cli/cmd/argocd.go +++ b/cli/cmd/argocd.go @@ -1,12 +1,10 @@ -// Copyright (c) Codesphere Inc. -// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) Codesphere Inc. SPDX-License-Identifier: Apache-2.0 package cmd import ( "fmt" - "github.com/codesphere-cloud/cs-go/pkg/io" packageio "github.com/codesphere-cloud/cs-go/pkg/io" "github.com/codesphere-cloud/oms/internal/installer" "github.com/spf13/cobra" @@ -36,16 +34,12 @@ func (c *InstallArgoCDCmd) RunE(_ *cobra.Command, args []string) error { return nil } -type Config struct { - cmd *cobra.Command -} - func AddArgoCDCmd(parentCmd *cobra.Command, opts *GlobalOptions) { argocd := InstallArgoCDCmd{ cmd: &cobra.Command{ Use: "argocd", Short: "Install an ArgoCD helm release", - Long: io.Long(`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"}, @@ -53,11 +47,11 @@ func AddArgoCDCmd(parentCmd *cobra.Command, opts *GlobalOptions) { }, } argocd.cmd.Flags().StringVarP(&argocd.Opts.GitPassword, "git-password", "c", "", "Password/token to read from the git repo where ArgoCD Application manifests are stored") - argocd.cmd.MarkFlagRequired("git-password") + _ = argocd.cmd.MarkFlagRequired("git-password") 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.MarkFlagRequired("registry-password") + _ = argocd.cmd.MarkFlagRequired("registry-password") argocd.cmd.Flags().StringVar(&argocd.Opts.DatacenterId, "dc-id", "", "Codesphere Datacenter ID where this ArgoCD is installed") - argocd.cmd.MarkFlagRequired("dc-id") + _ = argocd.cmd.MarkFlagRequired("dc-id") argocd.cmd.Flags().StringVarP(&argocd.Opts.Version, "version", "v", "", "Version of the ArgoCD helm chart to install") argocd.cmd.RunE = argocd.RunE diff --git a/internal/installer/argocd.go b/internal/installer/argocd.go index 3a668b4d..c0adfbe8 100644 --- a/internal/installer/argocd.go +++ b/internal/installer/argocd.go @@ -158,9 +158,9 @@ func (a *ArgoCD) Install() error { upgradeClient.Namespace = "argocd" upgradeClient.WaitStrategy = "watcher" upgradeClient.Version = a.Version // empty string means latest - upgradeClient.ChartPathOptions.RepoURL = repoURL + upgradeClient.RepoURL = repoURL - chartPath, err := upgradeClient.ChartPathOptions.LocateChart(chartName, settings) + chartPath, err := upgradeClient.LocateChart(chartName, settings) if err != nil { return fmt.Errorf("LocateChart failed: %w", err) } @@ -191,9 +191,9 @@ func (a *ArgoCD) Install() error { installClient.DryRunStrategy = "none" installClient.WaitStrategy = "watcher" installClient.Version = a.Version // empty string means latest - installClient.ChartPathOptions.RepoURL = repoURL + installClient.RepoURL = repoURL - chartPath, err := installClient.ChartPathOptions.LocateChart(chartName, settings) + chartPath, err := installClient.LocateChart(chartName, settings) if err != nil { return fmt.Errorf("LocateChart failed: %w", err) } From 56d9ed16f7e2944d532c27751ca8da4af08f3b34 Mon Sep 17 00:00:00 2001 From: bachgg Date: Fri, 6 Mar 2026 14:09:25 +0100 Subject: [PATCH 22/33] add DC 7 (new dev cluster on OVH) to AppProject --- internal/installer/manifests/argocd/app-projects.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/installer/manifests/argocd/app-projects.yaml b/internal/installer/manifests/argocd/app-projects.yaml index e573d965..34c6e47c 100644 --- a/internal/installer/manifests/argocd/app-projects.yaml +++ b/internal/installer/manifests/argocd/app-projects.yaml @@ -60,6 +60,8 @@ spec: name: "*" - namespace: "*" name: dc-5 + - namespace: "*" + name: dc-7 sourceRepos: - "*" roles: From 03f8a8d3848c3bd50bcaf61aa568273e7b4af499 Mon Sep 17 00:00:00 2001 From: bachgg Date: Mon, 9 Mar 2026 13:57:13 +0100 Subject: [PATCH 23/33] add option for fullinstall --- cli/cmd/argocd.go | 22 +++++++++++++++++----- internal/installer/argocd.go | 34 +++++++++++++++++++--------------- 2 files changed, 36 insertions(+), 20 deletions(-) diff --git a/cli/cmd/argocd.go b/cli/cmd/argocd.go index 6627f161..cdec06b3 100644 --- a/cli/cmd/argocd.go +++ b/cli/cmd/argocd.go @@ -22,10 +22,24 @@ type InstallArgoCDOpts struct { DatacenterId string GitPassword string RegistryPassword string + FullInstall bool } func (c *InstallArgoCDCmd) RunE(_ *cobra.Command, args []string) error { - install := installer.NewArgoCD(c.Opts.Version, c.Opts.DatacenterId, c.Opts.RegistryPassword, c.Opts.GitPassword) + if c.Opts.FullInstall { + requiredFlags := map[string]string{ + "git-password": c.Opts.GitPassword, + "registry-password": c.Opts.RegistryPassword, + "dc-id": c.Opts.DatacenterId, + } + + for flagName, value := range requiredFlags { + if value == "" { + return fmt.Errorf("flag --%s is required when --full-install is true", flagName) + } + } + } + install := installer.NewArgoCD(c.Opts.Version, c.Opts.DatacenterId, c.Opts.RegistryPassword, c.Opts.GitPassword, c.Opts.FullInstall) err := install.Install() if err != nil { return fmt.Errorf("failed to install chart ArgoCD: %w", err) @@ -46,13 +60,11 @@ func AddArgoCDCmd(parentCmd *cobra.Command, opts *GlobalOptions) { }), }, } - argocd.cmd.Flags().StringVarP(&argocd.Opts.GitPassword, "git-password", "c", "", "Password/token to read from the git repo where ArgoCD Application manifests are stored") - _ = argocd.cmd.MarkFlagRequired("git-password") + 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.MarkFlagRequired("registry-password") argocd.cmd.Flags().StringVar(&argocd.Opts.DatacenterId, "dc-id", "", "Codesphere Datacenter ID where this ArgoCD is installed") - _ = argocd.cmd.MarkFlagRequired("dc-id") argocd.cmd.Flags().StringVarP(&argocd.Opts.Version, "version", "v", "", "Version of the ArgoCD helm chart to install") + argocd.cmd.Flags().BoolVar(&argocd.Opts.FullInstall, "full-install", false, "Install other resources (AppProjects, Repo Creds, ...) after installing the chart") argocd.cmd.RunE = argocd.RunE parentCmd.AddCommand(argocd.cmd) diff --git a/internal/installer/argocd.go b/internal/installer/argocd.go index c0adfbe8..45d37831 100644 --- a/internal/installer/argocd.go +++ b/internal/installer/argocd.go @@ -22,18 +22,20 @@ type ArgoCDManager interface { } type ArgoCD struct { - Version string - DcNumber string - PasswordOCI string - PasswordGit string + Version string + DatacenterId string + OciPassword string + GitPassword string + FullInstall bool } -func NewArgoCD(version string, dcId string, passwordOCI string, passwordGit string) ArgoCDManager { +func NewArgoCD(version string, dcId string, passwordOCI string, passwordGit string, fullInstall bool) ArgoCDManager { return &ArgoCD{ - Version: version, - DcNumber: dcId, - PasswordOCI: passwordOCI, - PasswordGit: passwordGit, + Version: version, + DatacenterId: dcId, + OciPassword: passwordOCI, + GitPassword: passwordGit, + FullInstall: fullInstall, } } @@ -49,15 +51,15 @@ func (a *ArgoCD) applyPostInstallResources() error { return fmt.Errorf("applying app projects: %w", err) } - if err := applyLocalCluster(ctx, clientset, a.DcNumber); err != nil { + if err := applyLocalCluster(ctx, clientset, a.DatacenterId); err != nil { return fmt.Errorf("applying local cluster secret: %w", err) } - if err := applyHelmRegistrySecret(ctx, clientset, a.PasswordOCI); err != nil { + if err := applyHelmRegistrySecret(ctx, clientset, a.OciPassword); err != nil { return fmt.Errorf("applying helm registry secret: %w", err) } - if err := applyGitRepoSecret(ctx, clientset, a.PasswordGit); err != nil { + if err := applyGitRepoSecret(ctx, clientset, a.GitPassword); err != nil { return fmt.Errorf("applying git repo secret: %w", err) } @@ -215,9 +217,11 @@ func (a *ArgoCD) Install() error { } } - err = a.applyPostInstallResources() - if err != nil { - return fmt.Errorf("failed apply post chart install resources: %v", err) + if a.FullInstall { + err = a.applyPostInstallResources() + if err != nil { + return fmt.Errorf("failed apply post chart install resources: %v", err) + } } showPostInstallHints() From 13b9f15137b43bae4a23fb4de298794d738d403f Mon Sep 17 00:00:00 2001 From: bachgg Date: Tue, 10 Mar 2026 17:02:32 +0100 Subject: [PATCH 24/33] use same context --- internal/installer/argocd.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/internal/installer/argocd.go b/internal/installer/argocd.go index 45d37831..cf57c462 100644 --- a/internal/installer/argocd.go +++ b/internal/installer/argocd.go @@ -39,14 +39,12 @@ func NewArgoCD(version string, dcId string, passwordOCI string, passwordGit stri } } -func (a *ArgoCD) applyPostInstallResources() error { +func (a *ArgoCD) applyPostInstallResources(ctx context.Context) error { clientset, dynClient, err := newClients() if err != nil { return fmt.Errorf("creating kubernetes clients: %w", err) } - ctx := context.TODO() - if err := applyAppProjects(ctx, dynClient); err != nil { return fmt.Errorf("applying app projects: %w", err) } @@ -218,7 +216,7 @@ func (a *ArgoCD) Install() error { } if a.FullInstall { - err = a.applyPostInstallResources() + err = a.applyPostInstallResources(ctx) if err != nil { return fmt.Errorf("failed apply post chart install resources: %v", err) } From 98370cdd804ad1fb32c264c56856d3b968bebc84 Mon Sep 17 00:00:00 2001 From: bachgg Date: Wed, 11 Mar 2026 15:20:29 +0100 Subject: [PATCH 25/33] argocd 2 --- cli/cmd/argocd.go | 7 +- .../installer/{argocd.go => argocd.go.bak} | 105 +++++++++--- internal/installer/argocd_install.go | 151 ++++++++++++++++ internal/installer/argocd_test.go.bak | 46 +++++ internal/installer/helm.go.bak | 27 +++ internal/installer/helm_client.go | 162 ++++++++++++++++++ 6 files changed, 474 insertions(+), 24 deletions(-) rename internal/installer/{argocd.go => argocd.go.bak} (70%) create mode 100644 internal/installer/argocd_install.go create mode 100644 internal/installer/argocd_test.go.bak create mode 100644 internal/installer/helm.go.bak create mode 100644 internal/installer/helm_client.go diff --git a/cli/cmd/argocd.go b/cli/cmd/argocd.go index cdec06b3..08452a25 100644 --- a/cli/cmd/argocd.go +++ b/cli/cmd/argocd.go @@ -39,8 +39,11 @@ func (c *InstallArgoCDCmd) RunE(_ *cobra.Command, args []string) error { } } } - install := installer.NewArgoCD(c.Opts.Version, c.Opts.DatacenterId, c.Opts.RegistryPassword, c.Opts.GitPassword, c.Opts.FullInstall) - err := install.Install() + install, err := installer.NewArgoCD2(c.Opts.Version, c.Opts.DatacenterId, c.Opts.RegistryPassword, c.Opts.GitPassword, c.Opts.FullInstall) + if err != nil { + return fmt.Errorf("failed to initialize ArgoCD installer") + } + err = install.Install() if err != nil { return fmt.Errorf("failed to install chart ArgoCD: %w", err) } diff --git a/internal/installer/argocd.go b/internal/installer/argocd.go.bak similarity index 70% rename from internal/installer/argocd.go rename to internal/installer/argocd.go.bak index cf57c462..0b8f59f3 100644 --- a/internal/installer/argocd.go +++ b/internal/installer/argocd.go.bak @@ -27,16 +27,29 @@ type ArgoCD struct { OciPassword string GitPassword string FullInstall bool + + HelmInstaller HelmInstaller } -func NewArgoCD(version string, dcId string, passwordOCI string, passwordGit string, fullInstall bool) ArgoCDManager { +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 failed: %w", err) + } return &ArgoCD{ Version: version, DatacenterId: dcId, OciPassword: passwordOCI, GitPassword: passwordGit, FullInstall: fullInstall, - } + + HelmInstaller: &RealHelmInstaller{ + helmInstallClient: action.NewInstall(actionConfig), + }, + }, nil } func (a *ArgoCD) applyPostInstallResources(ctx context.Context) error { @@ -71,20 +84,33 @@ func showPostInstallHints() { log.Println(` kubectl port-forward svc/argocd-server 8080:80 -nargocd`) } -// Install the ArgoCD chart -func (a *ArgoCD) Install() error { - if a.Version != "" { - log.Printf("Installing/Upgrading ArgoCD helm chart version %s\n", a.Version) - } else { - log.Println("Installing/Upgrading ArgoCD helm chart (latest version)") - } +type RealHelmInstaller struct { + helmInstallClient *action.Install + helmInstallValues HelmInstallValues +} +func (h *RealHelmInstaller) SetInstallValues(v HelmInstallValues) { + h.helmInstallClient.ReleaseName = v.ReleaseName + h.helmInstallClient.Namespace = v.Namespace + h.helmInstallClient.CreateNamespace = v.CreateNamespace + h.helmInstallClient.DryRunStrategy = v.DryRunStrategy + h.helmInstallClient.WaitStrategy = v.WaitStrategy + h.helmInstallClient.Version = v.Version + h.helmInstallClient.RepoURL = v.RepoURL +} +func (h *RealHelmInstaller) LocateChart(name string, settings *cli.EnvSettings) (string, error) { + return h.helmInstallClient.LocateChart(name, settings) +} +func (h *RealHelmInstaller) RunWithContext(ctx context.Context, ch chart.Charter, vals map[string]interface{}) (release.Releaser, error) { + return h.helmInstallClient.RunWithContext(ctx, ch, vals) +} + +func (h *RealHelmInstaller) getRelease() (release.Accessor, error) { settings := cli.New() - ctx := context.Background() actionConfig := new(action.Configuration) if err := actionConfig.Init(settings.RESTClientGetter(), "argocd", os.Getenv("HELM_DRIVER")); err != nil { - return fmt.Errorf("init failed: %w", err) + return nil, fmt.Errorf("init failed: %w", err) } // Check if a release already exists @@ -95,7 +121,7 @@ func (a *ArgoCD) Install() error { releases, err := listClient.Run() if err != nil { - return fmt.Errorf("list releases failed: %w", err) + return nil, fmt.Errorf("list releases failed: %w", err) } // Find existing "argocd" release using the Accessor interface @@ -111,6 +137,26 @@ func (a *ArgoCD) Install() error { break } } + return existingAccessor, nil +} + +func (h *RealHelmInstaller) installRelease(version string) { + +} + +func (h *RealHelmInstaller) upgradeRelease(version string) { + +} + +// Install the ArgoCD chart +func (a *ArgoCD) Install() error { + if a.Version != "" { + log.Printf("Installing/Upgrading ArgoCD helm chart version %s\n", a.Version) + } else { + log.Println("Installing/Upgrading ArgoCD helm chart (latest version)") + } + + ctx := context.Background() chartName := "argo-cd" repoURL := "https://argoproj.github.io/argo-helm" @@ -120,6 +166,8 @@ func (a *ArgoCD) Install() error { }, } + existingAccessor := a.HelmInstaller.getRelease() + if existingAccessor != nil { // A release already exists — compare versions using the chart accessor chartAcc, err := chart.NewAccessor(existingAccessor.Chart()) @@ -184,16 +232,29 @@ func (a *ArgoCD) Install() error { // No existing release — perform a fresh install log.Println("No existing ArgoCD release found, performing fresh install") - installClient := action.NewInstall(actionConfig) - installClient.ReleaseName = "argocd" - installClient.Namespace = "argocd" - installClient.CreateNamespace = true - installClient.DryRunStrategy = "none" - installClient.WaitStrategy = "watcher" - installClient.Version = a.Version // empty string means latest - installClient.RepoURL = repoURL + //installClient := action.NewInstall(actionConfig) + + installValues := HelmInstallValues{ + ReleaseName: "argocd", + Namespace: "argocd", + CreateNamespace: true, + DryRunStrategy: "none", + WaitStrategy: "watcher", + Version: a.Version, // empty string means latest + RepoURL: repoURL, + } + + a.HelmInstaller.SetInstallValues(installValues) + + // installClient.ReleaseName = "argocd" + // installClient.Namespace = "argocd" + // installClient.CreateNamespace = true + // installClient.DryRunStrategy = "none" + // installClient.WaitStrategy = "watcher" + // installClient.Version = a.Version // empty string means latest + // installClient.RepoURL = repoURL - chartPath, err := installClient.LocateChart(chartName, settings) + chartPath, err := a.HelmInstaller.LocateChart(chartName, settings) if err != nil { return fmt.Errorf("LocateChart failed: %w", err) } @@ -203,7 +264,7 @@ func (a *ArgoCD) Install() error { return fmt.Errorf("load failed: %w", err) } - _, err = installClient.RunWithContext(ctx, chartRequested, vals) + _, err = a.HelmInstaller.RunWithContext(ctx, chartRequested, vals) if err != nil { return fmt.Errorf("install failed: %w", err) } diff --git a/internal/installer/argocd_install.go b/internal/installer/argocd_install.go new file mode 100644 index 00000000..704228b1 --- /dev/null +++ b/internal/installer/argocd_install.go @@ -0,0 +1,151 @@ +package installer + +import ( + "context" + "fmt" + "log" + "os" + + "github.com/Masterminds/semver/v3" + "helm.sh/helm/v4/pkg/action" + "helm.sh/helm/v4/pkg/cli" +) + +// ArgoCD holds the user-facing configuration for the install/upgrade command. +type ArgoCD2 struct { + Version string + DatacenterId string + OciPassword string + GitPassword string + FullInstall bool + Helm HelmClient // inject a real or mock client +} + +func NewArgoCD2(version string, dcId string, passwordOCI string, passwordGit string, fullInstall bool) (*ArgoCD2, 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 failed: %w", err) + } + helm, err := NewHelmClient("argocd") + if err != nil { + log.Fatal(err) + } + return &ArgoCD2{ + Version: version, + DatacenterId: dcId, + OciPassword: passwordOCI, + GitPassword: passwordGit, + FullInstall: fullInstall, + Helm: helm, + }, nil +} + +// Install is the top-level orchestrator. It delegates every Helm interaction +// to the HelmClient interface, keeping this function short and testable. +func (a *ArgoCD2) Install() error { + if a.Version != "" { + log.Printf("Installing/Upgrading ArgoCD helm chart version %s\n", a.Version) + } else { + log.Println("Installing/Upgrading ArgoCD helm chart (latest version)") + } + + ctx := context.Background() + + cfg := ChartConfig{ + ReleaseName: "argocd", + ChartName: "argo-cd", + RepoURL: "https://argoproj.github.io/argo-helm", + Namespace: "argocd", + Version: a.Version, + CreateNamespace: true, + Values: map[string]interface{}{ + "dex": map[string]interface{}{ + "enabled": false, + }, + }, + } + + // 1. Find existing release + existing, err := a.Helm.FindRelease(cfg.ReleaseName) + if err != nil { + return err + } + + if existing != nil { + // 2a. Upgrade path + if err := a.upgrade(ctx, cfg, existing); err != nil { + return err + } + } else { + // 2b. Fresh install path + if err := a.install(ctx, cfg); err != nil { + return err + } + } + + // 3. Optional post-install resources + // if a.FullInstall { + // if err := a.applyPostInstallResources(ctx); err != nil { + // return fmt.Errorf("failed apply post chart install resources: %v", err) + // } + // } + // + // showPostInstallHints() + + return nil +} + +// install performs a fresh Helm install. +func (a *ArgoCD2) 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 { + return err + } + + if cfg.Version != "" { + fmt.Printf("Successfully installed Argo CD (chart version: %s)\n", cfg.Version) + } else { + fmt.Println("Successfully installed Argo CD (latest chart version)") + } + return nil +} + +// upgrade validates the version constraint and then performs a Helm upgrade. +func (a *ArgoCD2) upgrade(ctx context.Context, cfg ChartConfig, existing *ReleaseInfo) error { + log.Printf("Found existing ArgoCD release with chart version %s\n", existing.InstalledVersion) + + // Prevent downgrades when a specific version is requested + if a.Version != "" { + installedSemver, err := semver.NewVersion(existing.InstalledVersion) + if err != nil { + return fmt.Errorf("failed to parse installed version %q: %w", existing.InstalledVersion, err) + } + requestedSemver, err := semver.NewVersion(a.Version) + if err != nil { + return fmt.Errorf("failed to parse requested version %q: %w", a.Version, err) + } + + if requestedSemver.LessThan(installedSemver) { + return fmt.Errorf( + "requested version %s is older than installed version %s; downgrade is not allowed", + a.Version, existing.InstalledVersion, + ) + } + log.Printf("Upgrading ArgoCD from %s to %s\n", existing.InstalledVersion, a.Version) + } else { + log.Printf("Upgrading ArgoCD from %s to latest\n", existing.InstalledVersion) + } + + if err := a.Helm.UpgradeChart(ctx, cfg); err != nil { + return err + } + + if cfg.Version != "" { + fmt.Printf("Successfully upgraded Argo CD to chart version %s\n", cfg.Version) + } else { + fmt.Println("Successfully upgraded Argo CD to the latest chart version") + } + return nil +} diff --git a/internal/installer/argocd_test.go.bak b/internal/installer/argocd_test.go.bak new file mode 100644 index 00000000..0a94e1dc --- /dev/null +++ b/internal/installer/argocd_test.go.bak @@ -0,0 +1,46 @@ +package installer_test + +import ( + "github.com/codesphere-cloud/oms/internal/installer" + . "github.com/onsi/ginkgo/v2" + "github.com/stretchr/testify/mock" + "helm.sh/helm/v4/pkg/release" +) + +var _ = Describe("ArgoCD Installer", func() { + var ( + argoCD *installer.ArgoCD + mockInstaller *installer.MockHelmInstaller + + fullInstall bool + ) + BeforeEach(func() { + fullInstall = true + mockInstaller = installer.NewMockHelmInstaller(GinkgoT()) + }) + + JustBeforeEach(func() { + argoCD = &installer.ArgoCD{ + Version: "v2.0.0", + DatacenterId: "some-dc", + OciPassword: "secret", + GitPassword: "secret", + FullInstall: fullInstall, + + HelmInstaller: mockInstaller, + } + }) + Describe("NewArgoCD", func() { + Context("when full install is false", func() { + BeforeEach(func() { + fullInstall = false + }) + It("initializes without error", func() { + mockInstaller.EXPECT().SetInstallValues(mock.Anything) + mockInstaller.EXPECT().RunWithContext(mock.Anything, mock.Anything, mock.Anything).Return(release.Releaser{}) + + argoCD.Install() + }) + }) + }) +}) diff --git a/internal/installer/helm.go.bak b/internal/installer/helm.go.bak new file mode 100644 index 00000000..2e67ec28 --- /dev/null +++ b/internal/installer/helm.go.bak @@ -0,0 +1,27 @@ +package installer + +import ( + "context" + + "helm.sh/helm/v4/pkg/action" + "helm.sh/helm/v4/pkg/chart" + "helm.sh/helm/v4/pkg/cli" + "helm.sh/helm/v4/pkg/kube" + "helm.sh/helm/v4/pkg/release" +) + +type HelmInstallValues struct { + ReleaseName string + Namespace string + CreateNamespace bool + DryRunStrategy action.DryRunStrategy + WaitStrategy kube.WaitStrategy + Version string + RepoURL string +} + +type HelmInstaller interface { + SetInstallValues(v HelmInstallValues) + LocateChart(name string, settings *cli.EnvSettings) (string, error) + RunWithContext(ctx context.Context, ch chart.Charter, vals map[string]interface{}) (release.Releaser, error) +} diff --git a/internal/installer/helm_client.go b/internal/installer/helm_client.go new file mode 100644 index 00000000..915ce10f --- /dev/null +++ b/internal/installer/helm_client.go @@ -0,0 +1,162 @@ +package installer + +import ( + "context" + "fmt" + "os" + "time" + + "helm.sh/helm/v4/pkg/action" + "helm.sh/helm/v4/pkg/chart" + "helm.sh/helm/v4/pkg/chart/loader" + "helm.sh/helm/v4/pkg/cli" + "helm.sh/helm/v4/pkg/release" +) + +// ReleaseInfo holds the details of an existing Helm release that the rest of +// the application cares about — completely decoupled from the Helm SDK types. +type ReleaseInfo struct { + Name string + InstalledVersion string // chart version currently deployed +} + +// ChartConfig describes *what* to install/upgrade and *where*. +type ChartConfig struct { + ReleaseName string + ChartName string + RepoURL string + Namespace string + Version string // "" means latest + Values map[string]interface{} + CreateNamespace bool +} + +// HelmClient is the seam that makes the Helm SDK mockable. +// Every method receives only plain Go types so that test doubles never need to +// import any helm.sh package. +type HelmClient interface { + // FindRelease returns info about an existing release, or nil if none exists. + FindRelease(releaseName string) (*ReleaseInfo, error) + + // InstallChart performs a fresh Helm install and returns an error on failure. + InstallChart(ctx context.Context, cfg ChartConfig) error + + // UpgradeChart upgrades an existing Helm release and returns an error on failure. + UpgradeChart(ctx context.Context, cfg ChartConfig) error +} + +// --------------------------------------------------------------------------- +// Concrete implementation backed by the Helm Go SDK v4 +// --------------------------------------------------------------------------- + +type helmClient struct { + settings *cli.EnvSettings + actionConfig *action.Configuration +} + +// NewHelmClient initialises a real Helm SDK client scoped to `namespace`. +func NewHelmClient(namespace string) (HelmClient, error) { + settings := cli.New() + actionConfig := new(action.Configuration) + if err := actionConfig.Init( + settings.RESTClientGetter(), + namespace, + os.Getenv("HELM_DRIVER"), + ); err != nil { + return nil, fmt.Errorf("helm action config init failed: %w", err) + } + return &helmClient{ + settings: settings, + actionConfig: actionConfig, + }, nil +} + +func (h *helmClient) FindRelease(releaseName string) (*ReleaseInfo, error) { + listClient := action.NewList(h.actionConfig) + listClient.Filter = "^" + releaseName + "$" + listClient.Deployed = true + listClient.SetStateMask() + + releases, err := listClient.Run() + if err != nil { + return nil, fmt.Errorf("list releases failed: %w", err) + } + + for _, r := range releases { + acc, err := release.NewAccessor(r) + if err != nil { + continue + } + if acc.Name() != releaseName { + continue + } + + chartAcc, err := chart.NewAccessor(acc.Chart()) + if err != nil { + return nil, fmt.Errorf("failed to access chart metadata: %w", err) + } + metadata := chartAcc.MetadataAsMap() + version, _ := metadata["Version"].(string) + + return &ReleaseInfo{ + Name: acc.Name(), + InstalledVersion: version, + }, nil + } + + return nil, nil // no release found +} + +func (h *helmClient) InstallChart(ctx context.Context, cfg ChartConfig) error { + installClient := action.NewInstall(h.actionConfig) + installClient.ReleaseName = cfg.ReleaseName + installClient.Namespace = cfg.Namespace + installClient.CreateNamespace = cfg.CreateNamespace + installClient.DryRunStrategy = "none" + installClient.WaitStrategy = "watcher" + installClient.Version = cfg.Version + installClient.RepoURL = cfg.RepoURL + installClient.Timeout = 5 * time.Minute + + chartPath, err := installClient.LocateChart(cfg.ChartName, h.settings) + if err != nil { + return fmt.Errorf("LocateChart failed: %w", err) + } + + chartRequested, err := loader.Load(chartPath) + if err != nil { + return fmt.Errorf("load chart failed: %w", err) + } + + _, err = installClient.RunWithContext(ctx, chartRequested, cfg.Values) + if err != nil { + return fmt.Errorf("install failed: %w", err) + } + + return nil +} + +func (h *helmClient) UpgradeChart(ctx context.Context, cfg ChartConfig) error { + upgradeClient := action.NewUpgrade(h.actionConfig) + upgradeClient.Namespace = cfg.Namespace + upgradeClient.WaitStrategy = "watcher" + upgradeClient.Version = cfg.Version + upgradeClient.RepoURL = cfg.RepoURL + + chartPath, err := upgradeClient.LocateChart(cfg.ChartName, h.settings) + if err != nil { + return fmt.Errorf("LocateChart failed: %w", err) + } + + chartRequested, err := loader.Load(chartPath) + if err != nil { + return fmt.Errorf("load chart failed: %w", err) + } + + _, err = upgradeClient.RunWithContext(ctx, cfg.ReleaseName, chartRequested, cfg.Values) + if err != nil { + return fmt.Errorf("upgrade failed: %w", err) + } + + return nil +} From bc7fe9f32ed972de12cc0c9e2e7ad1faee4d5091 Mon Sep 17 00:00:00 2001 From: bachgg Date: Wed, 11 Mar 2026 16:00:21 +0100 Subject: [PATCH 26/33] rename --- cli/cmd/argocd.go | 2 +- internal/installer/argocd.go.bak | 289 ------------------ .../{argocd_install.go => argocd_chart.go} | 12 +- internal/installer/argocd_test.go.bak | 46 --- internal/installer/helm.go.bak | 27 -- 5 files changed, 7 insertions(+), 369 deletions(-) delete mode 100644 internal/installer/argocd.go.bak rename internal/installer/{argocd_install.go => argocd_chart.go} (91%) delete mode 100644 internal/installer/argocd_test.go.bak delete mode 100644 internal/installer/helm.go.bak diff --git a/cli/cmd/argocd.go b/cli/cmd/argocd.go index 08452a25..a6d588dd 100644 --- a/cli/cmd/argocd.go +++ b/cli/cmd/argocd.go @@ -39,7 +39,7 @@ func (c *InstallArgoCDCmd) RunE(_ *cobra.Command, args []string) error { } } } - install, err := installer.NewArgoCD2(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) if err != nil { return fmt.Errorf("failed to initialize ArgoCD installer") } diff --git a/internal/installer/argocd.go.bak b/internal/installer/argocd.go.bak deleted file mode 100644 index 0b8f59f3..00000000 --- a/internal/installer/argocd.go.bak +++ /dev/null @@ -1,289 +0,0 @@ -// Copyright (c) Codesphere Inc. -// SPDX-License-Identifier: Apache-2.0 - -package installer - -import ( - "context" - "fmt" - "log" - "os" - - "github.com/Masterminds/semver/v3" - "helm.sh/helm/v4/pkg/action" - "helm.sh/helm/v4/pkg/chart" - "helm.sh/helm/v4/pkg/chart/loader" - "helm.sh/helm/v4/pkg/cli" - "helm.sh/helm/v4/pkg/release" -) - -type ArgoCDManager interface { - Install() error -} - -type ArgoCD struct { - Version string - DatacenterId string - OciPassword string - GitPassword string - FullInstall bool - - HelmInstaller HelmInstaller -} - -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 failed: %w", err) - } - return &ArgoCD{ - Version: version, - DatacenterId: dcId, - OciPassword: passwordOCI, - GitPassword: passwordGit, - FullInstall: fullInstall, - - HelmInstaller: &RealHelmInstaller{ - helmInstallClient: action.NewInstall(actionConfig), - }, - }, nil -} - -func (a *ArgoCD) applyPostInstallResources(ctx context.Context) error { - clientset, dynClient, err := newClients() - if err != nil { - return fmt.Errorf("creating kubernetes clients: %w", err) - } - - if err := applyAppProjects(ctx, dynClient); err != nil { - return fmt.Errorf("applying app projects: %w", err) - } - - if err := applyLocalCluster(ctx, clientset, a.DatacenterId); err != nil { - return fmt.Errorf("applying local cluster secret: %w", err) - } - - if err := applyHelmRegistrySecret(ctx, clientset, a.OciPassword); err != nil { - return fmt.Errorf("applying helm registry secret: %w", err) - } - - if err := applyGitRepoSecret(ctx, clientset, a.GitPassword); err != nil { - return fmt.Errorf("applying git repo secret: %w", err) - } - - return nil -} - -func 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`) - log.Println(`To port-forward ArgoCD UI to localhost:8080:`) - log.Println(` kubectl port-forward svc/argocd-server 8080:80 -nargocd`) -} - -type RealHelmInstaller struct { - helmInstallClient *action.Install - helmInstallValues HelmInstallValues -} - -func (h *RealHelmInstaller) SetInstallValues(v HelmInstallValues) { - h.helmInstallClient.ReleaseName = v.ReleaseName - h.helmInstallClient.Namespace = v.Namespace - h.helmInstallClient.CreateNamespace = v.CreateNamespace - h.helmInstallClient.DryRunStrategy = v.DryRunStrategy - h.helmInstallClient.WaitStrategy = v.WaitStrategy - h.helmInstallClient.Version = v.Version - h.helmInstallClient.RepoURL = v.RepoURL -} -func (h *RealHelmInstaller) LocateChart(name string, settings *cli.EnvSettings) (string, error) { - return h.helmInstallClient.LocateChart(name, settings) -} -func (h *RealHelmInstaller) RunWithContext(ctx context.Context, ch chart.Charter, vals map[string]interface{}) (release.Releaser, error) { - return h.helmInstallClient.RunWithContext(ctx, ch, vals) -} - -func (h *RealHelmInstaller) getRelease() (release.Accessor, 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 failed: %w", err) - } - - // Check if a release already exists - listClient := action.NewList(actionConfig) - listClient.Filter = "^argocd$" - listClient.Deployed = true - listClient.SetStateMask() - - releases, err := listClient.Run() - if err != nil { - return nil, fmt.Errorf("list releases failed: %w", err) - } - - // Find existing "argocd" release using the Accessor interface - var existingAccessor release.Accessor - - for _, r := range releases { - acc, err := release.NewAccessor(r) - if err != nil { - continue - } - if acc.Name() == "argocd" { - existingAccessor = acc - break - } - } - return existingAccessor, nil -} - -func (h *RealHelmInstaller) installRelease(version string) { - -} - -func (h *RealHelmInstaller) upgradeRelease(version string) { - -} - -// Install the ArgoCD chart -func (a *ArgoCD) Install() error { - if a.Version != "" { - log.Printf("Installing/Upgrading ArgoCD helm chart version %s\n", a.Version) - } else { - log.Println("Installing/Upgrading ArgoCD helm chart (latest version)") - } - - ctx := context.Background() - - chartName := "argo-cd" - repoURL := "https://argoproj.github.io/argo-helm" - vals := map[string]interface{}{ - "dex": map[string]interface{}{ - "enabled": false, - }, - } - - existingAccessor := a.HelmInstaller.getRelease() - - if existingAccessor != nil { - // A release already exists — compare versions using the chart accessor - chartAcc, err := chart.NewAccessor(existingAccessor.Chart()) - if err != nil { - return fmt.Errorf("failed to access chart metadata: %w", err) - } - metadata := chartAcc.MetadataAsMap() - installedVersion, _ := metadata["Version"].(string) - log.Printf("Found existing ArgoCD release with chart version %s\n", installedVersion) - - // Only perform version comparison if a specific version was requested - if a.Version != "" { - installedSemver, err := semver.NewVersion(installedVersion) - if err != nil { - return fmt.Errorf("failed to parse installed version %q: %w", installedVersion, err) - } - requestedSemver, err := semver.NewVersion(a.Version) - if err != nil { - return fmt.Errorf("failed to parse requested version %q: %w", a.Version, err) - } - - if requestedSemver.LessThan(installedSemver) { - return fmt.Errorf( - "requested version %s is older than installed version %s; downgrade is not allowed", - a.Version, installedVersion, - ) - } - - log.Printf("Upgrading ArgoCD from %s to %s\n", installedVersion, a.Version) - } else { - log.Printf("Upgrading ArgoCD from %s to latest\n", installedVersion) - } - - // Version is equal, larger, or latest — perform an upgrade - upgradeClient := action.NewUpgrade(actionConfig) - upgradeClient.Namespace = "argocd" - upgradeClient.WaitStrategy = "watcher" - upgradeClient.Version = a.Version // empty string means latest - upgradeClient.RepoURL = repoURL - - chartPath, err := upgradeClient.LocateChart(chartName, settings) - if err != nil { - return fmt.Errorf("LocateChart failed: %w", err) - } - - chartRequested, err := loader.Load(chartPath) - if err != nil { - return fmt.Errorf("load failed: %w", err) - } - - _, err = upgradeClient.RunWithContext(ctx, existingAccessor.Name(), chartRequested, vals) - if err != nil { - return fmt.Errorf("upgrade failed: %w", err) - } - - if a.Version != "" { - fmt.Printf("Successfully upgraded Argo CD to chart version %s\n", a.Version) - } else { - fmt.Println("Successfully upgraded Argo CD to the latest chart version") - } - } else { - // No existing release — perform a fresh install - log.Println("No existing ArgoCD release found, performing fresh install") - - //installClient := action.NewInstall(actionConfig) - - installValues := HelmInstallValues{ - ReleaseName: "argocd", - Namespace: "argocd", - CreateNamespace: true, - DryRunStrategy: "none", - WaitStrategy: "watcher", - Version: a.Version, // empty string means latest - RepoURL: repoURL, - } - - a.HelmInstaller.SetInstallValues(installValues) - - // installClient.ReleaseName = "argocd" - // installClient.Namespace = "argocd" - // installClient.CreateNamespace = true - // installClient.DryRunStrategy = "none" - // installClient.WaitStrategy = "watcher" - // installClient.Version = a.Version // empty string means latest - // installClient.RepoURL = repoURL - - chartPath, err := a.HelmInstaller.LocateChart(chartName, settings) - if err != nil { - return fmt.Errorf("LocateChart failed: %w", err) - } - - chartRequested, err := loader.Load(chartPath) - if err != nil { - return fmt.Errorf("load failed: %w", err) - } - - _, err = a.HelmInstaller.RunWithContext(ctx, chartRequested, vals) - if err != nil { - return fmt.Errorf("install failed: %w", err) - } - - if a.Version != "" { - fmt.Printf("Successfully installed Argo CD (chart version: %s)\n", a.Version) - } else { - fmt.Println("Successfully installed Argo CD (latest chart version)") - } - } - - if a.FullInstall { - err = a.applyPostInstallResources(ctx) - if err != nil { - return fmt.Errorf("failed apply post chart install resources: %v", err) - } - } - - showPostInstallHints() - - return nil -} diff --git a/internal/installer/argocd_install.go b/internal/installer/argocd_chart.go similarity index 91% rename from internal/installer/argocd_install.go rename to internal/installer/argocd_chart.go index 704228b1..0da3b983 100644 --- a/internal/installer/argocd_install.go +++ b/internal/installer/argocd_chart.go @@ -12,7 +12,7 @@ import ( ) // ArgoCD holds the user-facing configuration for the install/upgrade command. -type ArgoCD2 struct { +type ArgoCD struct { Version string DatacenterId string OciPassword string @@ -21,7 +21,7 @@ type ArgoCD2 struct { Helm HelmClient // inject a real or mock client } -func NewArgoCD2(version string, dcId string, passwordOCI string, passwordGit string, fullInstall bool) (*ArgoCD2, error) { +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 { @@ -31,7 +31,7 @@ func NewArgoCD2(version string, dcId string, passwordOCI string, passwordGit str if err != nil { log.Fatal(err) } - return &ArgoCD2{ + return &ArgoCD{ Version: version, DatacenterId: dcId, OciPassword: passwordOCI, @@ -43,7 +43,7 @@ func NewArgoCD2(version string, dcId string, passwordOCI string, passwordGit str // Install is the top-level orchestrator. It delegates every Helm interaction // to the HelmClient interface, keeping this function short and testable. -func (a *ArgoCD2) Install() error { +func (a *ArgoCD) Install() error { if a.Version != "" { log.Printf("Installing/Upgrading ArgoCD helm chart version %s\n", a.Version) } else { @@ -97,7 +97,7 @@ func (a *ArgoCD2) Install() error { } // install performs a fresh Helm install. -func (a *ArgoCD2) install(ctx context.Context, cfg ChartConfig) 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 { @@ -113,7 +113,7 @@ func (a *ArgoCD2) install(ctx context.Context, cfg ChartConfig) error { } // upgrade validates the version constraint and then performs a Helm upgrade. -func (a *ArgoCD2) upgrade(ctx context.Context, cfg ChartConfig, existing *ReleaseInfo) error { +func (a *ArgoCD) upgrade(ctx context.Context, cfg ChartConfig, existing *ReleaseInfo) error { log.Printf("Found existing ArgoCD release with chart version %s\n", existing.InstalledVersion) // Prevent downgrades when a specific version is requested diff --git a/internal/installer/argocd_test.go.bak b/internal/installer/argocd_test.go.bak deleted file mode 100644 index 0a94e1dc..00000000 --- a/internal/installer/argocd_test.go.bak +++ /dev/null @@ -1,46 +0,0 @@ -package installer_test - -import ( - "github.com/codesphere-cloud/oms/internal/installer" - . "github.com/onsi/ginkgo/v2" - "github.com/stretchr/testify/mock" - "helm.sh/helm/v4/pkg/release" -) - -var _ = Describe("ArgoCD Installer", func() { - var ( - argoCD *installer.ArgoCD - mockInstaller *installer.MockHelmInstaller - - fullInstall bool - ) - BeforeEach(func() { - fullInstall = true - mockInstaller = installer.NewMockHelmInstaller(GinkgoT()) - }) - - JustBeforeEach(func() { - argoCD = &installer.ArgoCD{ - Version: "v2.0.0", - DatacenterId: "some-dc", - OciPassword: "secret", - GitPassword: "secret", - FullInstall: fullInstall, - - HelmInstaller: mockInstaller, - } - }) - Describe("NewArgoCD", func() { - Context("when full install is false", func() { - BeforeEach(func() { - fullInstall = false - }) - It("initializes without error", func() { - mockInstaller.EXPECT().SetInstallValues(mock.Anything) - mockInstaller.EXPECT().RunWithContext(mock.Anything, mock.Anything, mock.Anything).Return(release.Releaser{}) - - argoCD.Install() - }) - }) - }) -}) diff --git a/internal/installer/helm.go.bak b/internal/installer/helm.go.bak deleted file mode 100644 index 2e67ec28..00000000 --- a/internal/installer/helm.go.bak +++ /dev/null @@ -1,27 +0,0 @@ -package installer - -import ( - "context" - - "helm.sh/helm/v4/pkg/action" - "helm.sh/helm/v4/pkg/chart" - "helm.sh/helm/v4/pkg/cli" - "helm.sh/helm/v4/pkg/kube" - "helm.sh/helm/v4/pkg/release" -) - -type HelmInstallValues struct { - ReleaseName string - Namespace string - CreateNamespace bool - DryRunStrategy action.DryRunStrategy - WaitStrategy kube.WaitStrategy - Version string - RepoURL string -} - -type HelmInstaller interface { - SetInstallValues(v HelmInstallValues) - LocateChart(name string, settings *cli.EnvSettings) (string, error) - RunWithContext(ctx context.Context, ch chart.Charter, vals map[string]interface{}) (release.Releaser, error) -} From 4cbba455f44bd311781a46a49e08a482d58f25ff Mon Sep 17 00:00:00 2001 From: bachgg Date: Wed, 11 Mar 2026 16:13:22 +0100 Subject: [PATCH 27/33] add tests --- internal/installer/argocd_chart_test.go | 193 +++++++++++++++++ internal/installer/mocks.go | 276 ++++++++++++++++++------ 2 files changed, 398 insertions(+), 71 deletions(-) create mode 100644 internal/installer/argocd_chart_test.go diff --git a/internal/installer/argocd_chart_test.go b/internal/installer/argocd_chart_test.go new file mode 100644 index 00000000..c4380ef2 --- /dev/null +++ b/internal/installer/argocd_chart_test.go @@ -0,0 +1,193 @@ +package installer_test + +import ( + "errors" + + "github.com/codesphere-cloud/oms/internal/installer" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/stretchr/testify/mock" +) + +var _ = Describe("ArgoCD.Install", func() { + + var ( + helmMock *installer.MockHelmClient + a *installer.ArgoCD + ) + + BeforeEach(func() { + helmMock = new(installer.MockHelmClient) + }) + + Context("when no existing release is found", func() { + BeforeEach(func() { + helmMock.On("FindRelease", "argocd").Return(nil, nil) + }) + + It("performs a fresh install with a specific version", func() { + helmMock.On("InstallChart", mock.Anything, mock.MatchedBy(func(cfg installer.ChartConfig) bool { + return cfg.Version == "7.0.0" && + cfg.ReleaseName == "argocd" && + cfg.Namespace == "argocd" && + cfg.CreateNamespace == true + })).Return(nil) + + a = &installer.ArgoCD{Version: "7.0.0", Helm: helmMock} + + err := a.Install() + Expect(err).ToNot(HaveOccurred()) + + helmMock.AssertCalled(GinkgoT(), "FindRelease", "argocd") + helmMock.AssertCalled(GinkgoT(), "InstallChart", mock.Anything, mock.Anything) + helmMock.AssertNotCalled(GinkgoT(), "UpgradeChart", mock.Anything, mock.Anything) + }) + + It("performs a fresh install with latest version when Version is empty", func() { + helmMock.On("InstallChart", mock.Anything, mock.MatchedBy(func(cfg installer.ChartConfig) bool { + return cfg.Version == "" + })).Return(nil) + + a = &installer.ArgoCD{Version: "", Helm: helmMock} + + err := a.Install() + Expect(err).ToNot(HaveOccurred()) + + helmMock.AssertNumberOfCalls(GinkgoT(), "InstallChart", 1) + helmMock.AssertNotCalled(GinkgoT(), "UpgradeChart", mock.Anything, mock.Anything) + }) + + It("returns an error when InstallChart fails", func() { + helmMock.On("InstallChart", mock.Anything, mock.Anything). + Return(errors.New("chart not found")) + + a = &installer.ArgoCD{Version: "7.0.0", Helm: helmMock} + + err := a.Install() + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("chart not found")) + }) + }) + + Context("when an existing release is found", func() { + existingRelease := &installer.ReleaseInfo{Name: "argocd", InstalledVersion: "6.0.0"} + + BeforeEach(func() { + helmMock.On("FindRelease", "argocd").Return(existingRelease, nil) + }) + + It("upgrades to a newer version", func() { + helmMock.On("UpgradeChart", mock.Anything, mock.MatchedBy(func(cfg installer.ChartConfig) bool { + return cfg.Version == "7.0.0" + })).Return(nil) + + a = &installer.ArgoCD{Version: "7.0.0", Helm: helmMock} + + err := a.Install() + Expect(err).ToNot(HaveOccurred()) + + helmMock.AssertNumberOfCalls(GinkgoT(), "UpgradeChart", 1) + helmMock.AssertNotCalled(GinkgoT(), "InstallChart", mock.Anything, mock.Anything) + }) + + It("upgrades to the same version (no-op upgrade)", func() { + helmMock.On("UpgradeChart", mock.Anything, mock.MatchedBy(func(cfg installer.ChartConfig) bool { + return cfg.Version == "6.0.0" + })).Return(nil) + + a = &installer.ArgoCD{Version: "6.0.0", Helm: helmMock} + + err := a.Install() + Expect(err).ToNot(HaveOccurred()) + + helmMock.AssertNumberOfCalls(GinkgoT(), "UpgradeChart", 1) + }) + + It("upgrades to latest when Version is empty", func() { + helmMock.On("UpgradeChart", mock.Anything, mock.MatchedBy(func(cfg installer.ChartConfig) bool { + return cfg.Version == "" + })).Return(nil) + + a = &installer.ArgoCD{Version: "", Helm: helmMock} + + err := a.Install() + Expect(err).ToNot(HaveOccurred()) + + helmMock.AssertNumberOfCalls(GinkgoT(), "UpgradeChart", 1) + }) + + It("rejects a downgrade", func() { + a = &installer.ArgoCD{Version: "5.0.0", Helm: helmMock} + + err := a.Install() + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("downgrade is not allowed")) + + helmMock.AssertNotCalled(GinkgoT(), "UpgradeChart", mock.Anything, mock.Anything) + helmMock.AssertNotCalled(GinkgoT(), "InstallChart", mock.Anything, mock.Anything) + }) + + It("returns an error when UpgradeChart fails", func() { + helmMock.On("UpgradeChart", mock.Anything, mock.Anything). + Return(errors.New("timeout waiting for condition")) + + a = &installer.ArgoCD{Version: "7.0.0", Helm: helmMock} + + err := a.Install() + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("timeout")) + }) + }) + + Context("when FindRelease returns an error", func() { + It("propagates the error without installing or upgrading", func() { + helmMock.On("FindRelease", "argocd"). + Return(nil, errors.New("cluster unreachable")) + + a = &installer.ArgoCD{Version: "7.0.0", Helm: helmMock} + + err := a.Install() + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("cluster unreachable")) + + helmMock.AssertNotCalled(GinkgoT(), "InstallChart", mock.Anything, mock.Anything) + helmMock.AssertNotCalled(GinkgoT(), "UpgradeChart", mock.Anything, mock.Anything) + }) + }) + + Context("chart configuration", func() { + It("always uses the correct chart name and repo URL", func() { + helmMock.On("FindRelease", "argocd").Return(nil, nil) + helmMock.On("InstallChart", mock.Anything, mock.MatchedBy(func(cfg installer.ChartConfig) bool { + return cfg.ChartName == "argo-cd" && + cfg.RepoURL == "https://argoproj.github.io/argo-helm" + })).Return(nil) + + a = &installer.ArgoCD{Version: "7.0.0", Helm: helmMock} + + err := a.Install() + Expect(err).ToNot(HaveOccurred()) + + helmMock.AssertExpectations(GinkgoT()) + }) + + It("disables dex in the values", func() { + helmMock.On("FindRelease", "argocd").Return(nil, nil) + helmMock.On("InstallChart", mock.Anything, mock.MatchedBy(func(cfg installer.ChartConfig) bool { + dex, ok := cfg.Values["dex"].(map[string]interface{}) + return ok && dex["enabled"] == false + })).Return(nil) + + a = &installer.ArgoCD{Version: "7.0.0", Helm: helmMock} + + err := a.Install() + Expect(err).ToNot(HaveOccurred()) + + helmMock.AssertExpectations(GinkgoT()) + }) + }) + + AfterEach(func() { + helmMock.AssertExpectations(GinkgoT()) + }) +}) diff --git a/internal/installer/mocks.go b/internal/installer/mocks.go index 61a1f4dc..5f2a5f85 100644 --- a/internal/installer/mocks.go +++ b/internal/installer/mocks.go @@ -5,82 +5,13 @@ package installer import ( + "context" + "github.com/codesphere-cloud/oms/internal/installer/files" "github.com/codesphere-cloud/oms/internal/util" mock "github.com/stretchr/testify/mock" ) -// NewMockArgoCDManager creates a new instance of MockArgoCDManager. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockArgoCDManager(t interface { - mock.TestingT - Cleanup(func()) -}) *MockArgoCDManager { - mock := &MockArgoCDManager{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} - -// MockArgoCDManager is an autogenerated mock type for the ArgoCDManager type -type MockArgoCDManager struct { - mock.Mock -} - -type MockArgoCDManager_Expecter struct { - mock *mock.Mock -} - -func (_m *MockArgoCDManager) EXPECT() *MockArgoCDManager_Expecter { - return &MockArgoCDManager_Expecter{mock: &_m.Mock} -} - -// Install provides a mock function for the type MockArgoCDManager -func (_mock *MockArgoCDManager) Install() error { - ret := _mock.Called() - - if len(ret) == 0 { - panic("no return value specified for Install") - } - - var r0 error - if returnFunc, ok := ret.Get(0).(func() error); ok { - r0 = returnFunc() - } else { - r0 = ret.Error(0) - } - return r0 -} - -// MockArgoCDManager_Install_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Install' -type MockArgoCDManager_Install_Call struct { - *mock.Call -} - -// Install is a helper method to define mock.On call -func (_e *MockArgoCDManager_Expecter) Install() *MockArgoCDManager_Install_Call { - return &MockArgoCDManager_Install_Call{Call: _e.mock.On("Install")} -} - -func (_c *MockArgoCDManager_Install_Call) Run(run func()) *MockArgoCDManager_Install_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockArgoCDManager_Install_Call) Return(err error) *MockArgoCDManager_Install_Call { - _c.Call.Return(err) - return _c -} - -func (_c *MockArgoCDManager_Install_Call) RunAndReturn(run func() error) *MockArgoCDManager_Install_Call { - _c.Call.Return(run) - return _c -} - // NewMockConfigManager creates a new instance of MockConfigManager. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewMockConfigManager(t interface { @@ -778,6 +709,209 @@ func (_c *MockInstallConfigManager_WriteVault_Call) RunAndReturn(run func(vaultP return _c } +// NewMockHelmClient creates a new instance of MockHelmClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockHelmClient(t interface { + mock.TestingT + Cleanup(func()) +}) *MockHelmClient { + mock := &MockHelmClient{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// MockHelmClient is an autogenerated mock type for the HelmClient type +type MockHelmClient struct { + mock.Mock +} + +type MockHelmClient_Expecter struct { + mock *mock.Mock +} + +func (_m *MockHelmClient) EXPECT() *MockHelmClient_Expecter { + return &MockHelmClient_Expecter{mock: &_m.Mock} +} + +// FindRelease provides a mock function for the type MockHelmClient +func (_mock *MockHelmClient) FindRelease(releaseName string) (*ReleaseInfo, error) { + ret := _mock.Called(releaseName) + + if len(ret) == 0 { + panic("no return value specified for FindRelease") + } + + var r0 *ReleaseInfo + var r1 error + if returnFunc, ok := ret.Get(0).(func(string) (*ReleaseInfo, error)); ok { + return returnFunc(releaseName) + } + if returnFunc, ok := ret.Get(0).(func(string) *ReleaseInfo); ok { + r0 = returnFunc(releaseName) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*ReleaseInfo) + } + } + if returnFunc, ok := ret.Get(1).(func(string) error); ok { + r1 = returnFunc(releaseName) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// MockHelmClient_FindRelease_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FindRelease' +type MockHelmClient_FindRelease_Call struct { + *mock.Call +} + +// FindRelease is a helper method to define mock.On call +// - releaseName string +func (_e *MockHelmClient_Expecter) FindRelease(releaseName interface{}) *MockHelmClient_FindRelease_Call { + return &MockHelmClient_FindRelease_Call{Call: _e.mock.On("FindRelease", releaseName)} +} + +func (_c *MockHelmClient_FindRelease_Call) Run(run func(releaseName string)) *MockHelmClient_FindRelease_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 string + if args[0] != nil { + arg0 = args[0].(string) + } + run( + arg0, + ) + }) + return _c +} + +func (_c *MockHelmClient_FindRelease_Call) Return(releaseInfo *ReleaseInfo, err error) *MockHelmClient_FindRelease_Call { + _c.Call.Return(releaseInfo, err) + return _c +} + +func (_c *MockHelmClient_FindRelease_Call) RunAndReturn(run func(releaseName string) (*ReleaseInfo, error)) *MockHelmClient_FindRelease_Call { + _c.Call.Return(run) + return _c +} + +// InstallChart provides a mock function for the type MockHelmClient +func (_mock *MockHelmClient) InstallChart(ctx context.Context, cfg ChartConfig) error { + ret := _mock.Called(ctx, cfg) + + if len(ret) == 0 { + panic("no return value specified for InstallChart") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context, ChartConfig) error); ok { + r0 = returnFunc(ctx, cfg) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// MockHelmClient_InstallChart_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'InstallChart' +type MockHelmClient_InstallChart_Call struct { + *mock.Call +} + +// InstallChart is a helper method to define mock.On call +// - ctx context.Context +// - cfg ChartConfig +func (_e *MockHelmClient_Expecter) InstallChart(ctx interface{}, cfg interface{}) *MockHelmClient_InstallChart_Call { + return &MockHelmClient_InstallChart_Call{Call: _e.mock.On("InstallChart", ctx, cfg)} +} + +func (_c *MockHelmClient_InstallChart_Call) Run(run func(ctx context.Context, cfg ChartConfig)) *MockHelmClient_InstallChart_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 ChartConfig + if args[1] != nil { + arg1 = args[1].(ChartConfig) + } + run( + arg0, + arg1, + ) + }) + return _c +} + +func (_c *MockHelmClient_InstallChart_Call) Return(err error) *MockHelmClient_InstallChart_Call { + _c.Call.Return(err) + return _c +} + +func (_c *MockHelmClient_InstallChart_Call) RunAndReturn(run func(ctx context.Context, cfg ChartConfig) error) *MockHelmClient_InstallChart_Call { + _c.Call.Return(run) + return _c +} + +// UpgradeChart provides a mock function for the type MockHelmClient +func (_mock *MockHelmClient) UpgradeChart(ctx context.Context, cfg ChartConfig) error { + ret := _mock.Called(ctx, cfg) + + if len(ret) == 0 { + panic("no return value specified for UpgradeChart") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context, ChartConfig) error); ok { + r0 = returnFunc(ctx, cfg) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// MockHelmClient_UpgradeChart_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpgradeChart' +type MockHelmClient_UpgradeChart_Call struct { + *mock.Call +} + +// UpgradeChart is a helper method to define mock.On call +// - ctx context.Context +// - cfg ChartConfig +func (_e *MockHelmClient_Expecter) UpgradeChart(ctx interface{}, cfg interface{}) *MockHelmClient_UpgradeChart_Call { + return &MockHelmClient_UpgradeChart_Call{Call: _e.mock.On("UpgradeChart", ctx, cfg)} +} + +func (_c *MockHelmClient_UpgradeChart_Call) Run(run func(ctx context.Context, cfg ChartConfig)) *MockHelmClient_UpgradeChart_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 ChartConfig + if args[1] != nil { + arg1 = args[1].(ChartConfig) + } + run( + arg0, + arg1, + ) + }) + return _c +} + +func (_c *MockHelmClient_UpgradeChart_Call) Return(err error) *MockHelmClient_UpgradeChart_Call { + _c.Call.Return(err) + return _c +} + +func (_c *MockHelmClient_UpgradeChart_Call) RunAndReturn(run func(ctx context.Context, cfg ChartConfig) error) *MockHelmClient_UpgradeChart_Call { + _c.Call.Return(run) + return _c +} + // NewMockK0sManager creates a new instance of MockK0sManager. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewMockK0sManager(t interface { From fcbd1bee9062ef025f2c6e18b59ce637954a1e8f Mon Sep 17 00:00:00 2001 From: bachgg Date: Wed, 11 Mar 2026 17:49:47 +0100 Subject: [PATCH 28/33] now complete testing --- .../installer/{argocd_chart.go => argocd.go} | 31 +++- internal/installer/argocd_resources.go | 70 ++++++-- internal/installer/argocd_resources_test.go | 168 ------------------ .../{argocd_chart_test.go => argocd_test.go} | 40 +++-- internal/installer/helm_client.go | 1 - internal/installer/mocks.go | 79 +++++++- 6 files changed, 189 insertions(+), 200 deletions(-) rename internal/installer/{argocd_chart.go => argocd.go} (82%) delete mode 100644 internal/installer/argocd_resources_test.go rename internal/installer/{argocd_chart_test.go => argocd_test.go} (83%) diff --git a/internal/installer/argocd_chart.go b/internal/installer/argocd.go similarity index 82% rename from internal/installer/argocd_chart.go rename to internal/installer/argocd.go index 0da3b983..d2193ae2 100644 --- a/internal/installer/argocd_chart.go +++ b/internal/installer/argocd.go @@ -19,18 +19,24 @@ type ArgoCD struct { GitPassword string FullInstall bool 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 failed: %w", err) + return nil, fmt.Errorf("init helm client failed: %w", err) } helm, err := NewHelmClient("argocd") if err != nil { log.Fatal(err) } + + resources, err := NewArgoCDResources(dcId, passwordOCI, passwordGit) + if err != nil { + return nil, fmt.Errorf("init argocd resources client failed: %v", err) + } return &ArgoCD{ Version: version, DatacenterId: dcId, @@ -38,6 +44,7 @@ func NewArgoCD(version string, dcId string, passwordOCI string, passwordGit stri GitPassword: passwordGit, FullInstall: fullInstall, Helm: helm, + Resources: resources, }, nil } @@ -85,13 +92,14 @@ func (a *ArgoCD) Install() error { } // 3. Optional post-install resources - // if a.FullInstall { - // if err := a.applyPostInstallResources(ctx); err != nil { - // return fmt.Errorf("failed apply post chart install resources: %v", err) - // } - // } - // - // showPostInstallHints() + if a.FullInstall { + err = a.Resources.ApplyAll(ctx) + if err != nil { + return fmt.Errorf("failed apply post chart install resources: %v", err) + } + } + + a.showPostInstallHints() return nil } @@ -149,3 +157,10 @@ func (a *ArgoCD) upgrade(ctx context.Context, cfg ChartConfig, existing *Release } return nil } + +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`) + log.Println(`To port-forward ArgoCD UI to localhost:8080:`) + log.Println(` kubectl port-forward svc/argocd-server 8080:80 -nargocd`) +} diff --git a/internal/installer/argocd_resources.go b/internal/installer/argocd_resources.go index d4ed01ee..6c17b471 100644 --- a/internal/installer/argocd_resources.go +++ b/internal/installer/argocd_resources.go @@ -13,6 +13,34 @@ import ( "k8s.io/client-go/kubernetes" ) +type ArgoCDResources interface { + ApplyAll(ctx context.Context) error +} + +type argoCDResources struct { + clientset kubernetes.Interface + dynClient dynamic.Interface + + DatacenterId string + OciPassword string + GitPassword string +} + +func NewArgoCDResources(dataCenterId string, ociPassword string, gitPassword string) (ArgoCDResources, error) { + clientset, dynClient, err := 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, + }, nil +} + //go:embed manifests/argocd/app-projects.yaml var appProjectsYAML []byte @@ -25,7 +53,27 @@ var helmRegistryTpl []byte //go:embed manifests/argocd/repo-creds-git.yaml.tpl var gitRepoTpl []byte -func applyAppProjects(ctx context.Context, dynClient dynamic.Interface) error { +func (a *argoCDResources) ApplyAll(ctx context.Context) error { + if err := a.applyAppProjects(ctx); err != nil { + 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 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) + } + + return nil +} + +func (a *argoCDResources) applyAppProjects(ctx context.Context) error { log.Println("Applying AppProjects... ") objects, err := decodeMultiDocYAML(appProjectsYAML) if err != nil { @@ -37,45 +85,45 @@ func applyAppProjects(ctx context.Context, dynClient dynamic.Interface) error { if err != nil { return err } - if err := applyUnstructured(ctx, dynClient, gvr, obj); err != nil { + if err := applyUnstructured(ctx, a.dynClient, gvr, obj); err != nil { return fmt.Errorf("applying app project %q: %w", obj.GetName(), err) } } return nil } -func applyLocalCluster(ctx context.Context, clientset kubernetes.Interface, dcNumber string) error { +func (a *argoCDResources) applyLocalCluster(ctx context.Context) error { log.Println("Applying local cluster secret... ") rendered, err := renderTemplate(localClusterTpl, map[string]string{ - "DC_NUMBER": dcNumber, + "DC_NUMBER": a.DatacenterId, }) if err != nil { return fmt.Errorf("rendering local cluster template: %w", err) } - return applySecretFromYAML(ctx, clientset, rendered) + return applySecretFromYAML(ctx, a.clientset, rendered) } -func applyHelmRegistrySecret(ctx context.Context, clientset kubernetes.Interface, ociReadPassword string) error { +func (a *argoCDResources) applyHelmRegistrySecret(ctx context.Context) error { log.Println("Applying helm registry secret... ") rendered, err := renderTemplate(helmRegistryTpl, map[string]string{ - "SECRET_CODESPHERE_OCI_READ": ociReadPassword, + "SECRET_CODESPHERE_OCI_READ": a.OciPassword, }) if err != nil { return fmt.Errorf("rendering helm registry template: %w", err) } - return applySecretFromYAML(ctx, clientset, rendered) + return applySecretFromYAML(ctx, a.clientset, rendered) } -func applyGitRepoSecret(ctx context.Context, clientset kubernetes.Interface, reposReadPassword string) error { +func (a *argoCDResources) applyGitRepoSecret(ctx context.Context) error { log.Println("Applying git repo secret... ") rendered, err := renderTemplate(gitRepoTpl, map[string]string{ - "SECRET_CODESPHERE_REPOS_READ": reposReadPassword, + "SECRET_CODESPHERE_REPOS_READ": a.GitPassword, }) if err != nil { return fmt.Errorf("rendering git repo template: %w", err) } - return applySecretFromYAML(ctx, clientset, rendered) + return applySecretFromYAML(ctx, a.clientset, rendered) } diff --git a/internal/installer/argocd_resources_test.go b/internal/installer/argocd_resources_test.go deleted file mode 100644 index 449711fc..00000000 --- a/internal/installer/argocd_resources_test.go +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright (c) Codesphere Inc. -// SPDX-License-Identifier: Apache-2.0 - -package installer - -import ( - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - corev1 "k8s.io/api/core/v1" - sigyaml "sigs.k8s.io/yaml" -) - -var _ = Describe("Embedded ArgoCD YAML manifests", func() { - Describe("appProjectsYAML", func() { - It("is not empty", func() { - Expect(appProjectsYAML).ToNot(BeEmpty()) - }) - - It("decodes into three AppProject objects", func() { - objects, err := decodeMultiDocYAML(appProjectsYAML) - Expect(err).ToNot(HaveOccurred()) - Expect(objects).To(HaveLen(3)) - }) - - It("contains the prod project with correct metadata", func() { - objects, err := decodeMultiDocYAML(appProjectsYAML) - Expect(err).ToNot(HaveOccurred()) - - prod := objects[0] - Expect(prod.GetName()).To(Equal("prod")) - Expect(prod.GetNamespace()).To(Equal("argocd")) - Expect(prod.GetKind()).To(Equal("AppProject")) - Expect(prod.GetAPIVersion()).To(Equal("argoproj.io/v1alpha1")) - Expect(prod.GetFinalizers()).To(ContainElement("resources-finalizer.argocd.argoproj.io")) - }) - - It("contains the dev project with correct metadata", func() { - objects, err := decodeMultiDocYAML(appProjectsYAML) - Expect(err).ToNot(HaveOccurred()) - - dev := objects[1] - Expect(dev.GetName()).To(Equal("dev")) - Expect(dev.GetNamespace()).To(Equal("argocd")) - Expect(dev.GetFinalizers()).To(ContainElement("resources-finalizer.argocd.argoproj.io")) - }) - - It("contains the default project with restricted spec", func() { - objects, err := decodeMultiDocYAML(appProjectsYAML) - Expect(err).ToNot(HaveOccurred()) - - def := objects[2] - Expect(def.GetName()).To(Equal("default")) - Expect(def.GetNamespace()).To(Equal("argocd")) - - spec, ok := def.Object["spec"].(map[string]interface{}) - Expect(ok).To(BeTrue()) - - sourceRepos, ok := spec["sourceRepos"].([]interface{}) - Expect(ok).To(BeTrue()) - Expect(sourceRepos).To(BeEmpty()) - - destinations, ok := spec["destinations"].([]interface{}) - Expect(ok).To(BeTrue()) - Expect(destinations).To(BeEmpty()) - }) - - It("maps all projects to a valid GVR", func() { - objects, err := decodeMultiDocYAML(appProjectsYAML) - Expect(err).ToNot(HaveOccurred()) - - for _, obj := range objects { - gvr, err := gvrForUnstructured(obj) - Expect(err).ToNot(HaveOccurred()) - Expect(gvr.Resource).To(Equal("appprojects")) - } - }) - }) - - Describe("localClusterTpl", func() { - It("is not empty", func() { - Expect(localClusterTpl).ToNot(BeEmpty()) - }) - - It("renders and parses into a valid Secret", func() { - rendered, err := renderTemplate(localClusterTpl, map[string]string{ - "DC_NUMBER": "3", - }) - Expect(err).ToNot(HaveOccurred()) - - secret := &corev1.Secret{} - err = sigyaml.Unmarshal(rendered, secret) - Expect(err).ToNot(HaveOccurred()) - - Expect(secret.Name).To(Equal("argocd-cluster-dc-3")) - Expect(secret.Namespace).To(Equal("argocd")) - Expect(secret.Labels).To(HaveKeyWithValue("argocd.argoproj.io/secret-type", "cluster")) - Expect(secret.StringData).To(HaveKeyWithValue("name", "dc-3")) - Expect(secret.StringData).To(HaveKeyWithValue("server", "https://kubernetes.default.svc")) - Expect(secret.StringData).To(HaveKey("config")) - }) - - It("substitutes the DC_NUMBER in all required fields", func() { - rendered, err := renderTemplate(localClusterTpl, map[string]string{ - "DC_NUMBER": "7", - }) - Expect(err).ToNot(HaveOccurred()) - - secret := &corev1.Secret{} - err = sigyaml.Unmarshal(rendered, secret) - Expect(err).ToNot(HaveOccurred()) - - Expect(secret.Name).To(Equal("argocd-cluster-dc-7")) - Expect(secret.StringData["name"]).To(Equal("dc-7")) - }) - }) - - Describe("helmRegistryTpl", func() { - It("is not empty", func() { - Expect(helmRegistryTpl).ToNot(BeEmpty()) - }) - - It("renders and parses into a valid OCI repository Secret", func() { - rendered, err := renderTemplate(helmRegistryTpl, map[string]string{ - "SECRET_CODESPHERE_OCI_READ": "ghp_testtoken123", - }) - Expect(err).ToNot(HaveOccurred()) - - secret := &corev1.Secret{} - err = sigyaml.Unmarshal(rendered, secret) - Expect(err).ToNot(HaveOccurred()) - - Expect(secret.Name).To(Equal("argocd-codesphere-oci-read")) - Expect(secret.Namespace).To(Equal("argocd")) - Expect(secret.Labels).To(HaveKeyWithValue("argocd.argoproj.io/secret-type", "repository")) - Expect(secret.StringData).To(HaveKeyWithValue("type", "helm")) - Expect(secret.StringData).To(HaveKeyWithValue("url", "ghcr.io/codesphere-cloud/charts")) - Expect(secret.StringData).To(HaveKeyWithValue("username", "github")) - Expect(secret.StringData).To(HaveKeyWithValue("password", "ghp_testtoken123")) - Expect(secret.StringData).To(HaveKeyWithValue("enableOCI", "true")) - }) - }) - - Describe("gitRepoTpl", func() { - It("is not empty", func() { - Expect(gitRepoTpl).ToNot(BeEmpty()) - }) - - It("renders and parses into a valid repo-creds Secret", func() { - rendered, err := renderTemplate(gitRepoTpl, map[string]string{ - "SECRET_CODESPHERE_REPOS_READ": "ghp_repotoken456", - }) - Expect(err).ToNot(HaveOccurred()) - - secret := &corev1.Secret{} - err = sigyaml.Unmarshal(rendered, secret) - Expect(err).ToNot(HaveOccurred()) - - Expect(secret.Name).To(Equal("argocd-codesphere-repos-read")) - Expect(secret.Namespace).To(Equal("argocd")) - Expect(secret.Labels).To(HaveKeyWithValue("argocd.argoproj.io/secret-type", "repo-creds")) - Expect(secret.StringData).To(HaveKeyWithValue("type", "git")) - Expect(secret.StringData).To(HaveKeyWithValue("url", "https://github.com/codesphere-cloud")) - Expect(secret.StringData).To(HaveKeyWithValue("username", "github")) - Expect(secret.StringData).To(HaveKeyWithValue("password", "ghp_repotoken456")) - }) - }) -}) diff --git a/internal/installer/argocd_chart_test.go b/internal/installer/argocd_test.go similarity index 83% rename from internal/installer/argocd_chart_test.go rename to internal/installer/argocd_test.go index c4380ef2..1119125b 100644 --- a/internal/installer/argocd_chart_test.go +++ b/internal/installer/argocd_test.go @@ -12,12 +12,15 @@ import ( var _ = Describe("ArgoCD.Install", func() { var ( - helmMock *installer.MockHelmClient - a *installer.ArgoCD + helmMock *installer.MockHelmClient + argoCDResourcesMock *installer.MockArgoCDResources + a *installer.ArgoCD ) BeforeEach(func() { - helmMock = new(installer.MockHelmClient) + helmMock = installer.NewMockHelmClient(GinkgoT()) + argoCDResourcesMock = installer.NewMockArgoCDResources(GinkgoT()) + a = &installer.ArgoCD{Version: "7.0.0", Helm: helmMock, Resources: argoCDResourcesMock} }) Context("when no existing release is found", func() { @@ -33,8 +36,6 @@ var _ = Describe("ArgoCD.Install", func() { cfg.CreateNamespace == true })).Return(nil) - a = &installer.ArgoCD{Version: "7.0.0", Helm: helmMock} - err := a.Install() Expect(err).ToNot(HaveOccurred()) @@ -61,8 +62,6 @@ var _ = Describe("ArgoCD.Install", func() { helmMock.On("InstallChart", mock.Anything, mock.Anything). Return(errors.New("chart not found")) - a = &installer.ArgoCD{Version: "7.0.0", Helm: helmMock} - err := a.Install() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("chart not found")) @@ -81,8 +80,6 @@ var _ = Describe("ArgoCD.Install", func() { return cfg.Version == "7.0.0" })).Return(nil) - a = &installer.ArgoCD{Version: "7.0.0", Helm: helmMock} - err := a.Install() Expect(err).ToNot(HaveOccurred()) @@ -187,7 +184,28 @@ var _ = Describe("ArgoCD.Install", func() { }) }) - AfterEach(func() { - helmMock.AssertExpectations(GinkgoT()) + Context("full installation", func() { + BeforeEach(func() { + helmMock.On("FindRelease", "argocd").Return(nil, nil) + helmMock.On("InstallChart", mock.Anything, mock.Anything).Return(nil) + }) + It("installs extra ArgoCD resources when FullInstall option in true", func() { + argoCDResourcesMock.On("ApplyAll", mock.Anything).Return(nil) + a.FullInstall = true + + err := a.Install() + Expect(err).ToNot(HaveOccurred()) + + argoCDResourcesMock.AssertCalled(GinkgoT(), "ApplyAll", mock.Anything) + }) + It("does not install extra ArgoCD resources when FullInstall option in false", func() { + a.FullInstall = false + + err := a.Install() + Expect(err).ToNot(HaveOccurred()) + + argoCDResourcesMock.AssertNotCalled(GinkgoT(), "ApplyAll") + }) }) + }) diff --git a/internal/installer/helm_client.go b/internal/installer/helm_client.go index 915ce10f..f51de832 100644 --- a/internal/installer/helm_client.go +++ b/internal/installer/helm_client.go @@ -54,7 +54,6 @@ type helmClient struct { actionConfig *action.Configuration } -// NewHelmClient initialises a real Helm SDK client scoped to `namespace`. func NewHelmClient(namespace string) (HelmClient, error) { settings := cli.New() actionConfig := new(action.Configuration) diff --git a/internal/installer/mocks.go b/internal/installer/mocks.go index 5f2a5f85..797f266e 100644 --- a/internal/installer/mocks.go +++ b/internal/installer/mocks.go @@ -6,12 +6,89 @@ package installer import ( "context" - "github.com/codesphere-cloud/oms/internal/installer/files" "github.com/codesphere-cloud/oms/internal/util" mock "github.com/stretchr/testify/mock" ) +// NewMockArgoCDResources creates a new instance of MockArgoCDResources. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockArgoCDResources(t interface { + mock.TestingT + Cleanup(func()) +}) *MockArgoCDResources { + mock := &MockArgoCDResources{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// MockArgoCDResources is an autogenerated mock type for the ArgoCDResources type +type MockArgoCDResources struct { + mock.Mock +} + +type MockArgoCDResources_Expecter struct { + mock *mock.Mock +} + +func (_m *MockArgoCDResources) EXPECT() *MockArgoCDResources_Expecter { + return &MockArgoCDResources_Expecter{mock: &_m.Mock} +} + +// ApplyAll provides a mock function for the type MockArgoCDResources +func (_mock *MockArgoCDResources) ApplyAll(ctx context.Context) error { + ret := _mock.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for ApplyAll") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = returnFunc(ctx) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// MockArgoCDResources_ApplyAll_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ApplyAll' +type MockArgoCDResources_ApplyAll_Call struct { + *mock.Call +} + +// ApplyAll is a helper method to define mock.On call +// - ctx context.Context +func (_e *MockArgoCDResources_Expecter) ApplyAll(ctx interface{}) *MockArgoCDResources_ApplyAll_Call { + return &MockArgoCDResources_ApplyAll_Call{Call: _e.mock.On("ApplyAll", ctx)} +} + +func (_c *MockArgoCDResources_ApplyAll_Call) Run(run func(ctx context.Context)) *MockArgoCDResources_ApplyAll_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + run( + arg0, + ) + }) + return _c +} + +func (_c *MockArgoCDResources_ApplyAll_Call) Return(err error) *MockArgoCDResources_ApplyAll_Call { + _c.Call.Return(err) + return _c +} + +func (_c *MockArgoCDResources_ApplyAll_Call) RunAndReturn(run func(ctx context.Context) error) *MockArgoCDResources_ApplyAll_Call { + _c.Call.Return(run) + return _c +} + // NewMockConfigManager creates a new instance of MockConfigManager. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewMockConfigManager(t interface { From 39392711ca10678ae23356141fad6b4e5682c7ec Mon Sep 17 00:00:00 2001 From: bachgg Date: Thu, 12 Mar 2026 10:23:18 +0100 Subject: [PATCH 29/33] finalize tests --- internal/installer/argocd_test.go | 65 +++++++++++-------------------- 1 file changed, 22 insertions(+), 43 deletions(-) diff --git a/internal/installer/argocd_test.go b/internal/installer/argocd_test.go index 1119125b..49c68a8b 100644 --- a/internal/installer/argocd_test.go +++ b/internal/installer/argocd_test.go @@ -29,7 +29,8 @@ var _ = Describe("ArgoCD.Install", func() { }) It("performs a fresh install with a specific version", func() { - helmMock.On("InstallChart", mock.Anything, mock.MatchedBy(func(cfg installer.ChartConfig) bool { + helmMock.EXPECT().FindRelease("argocd").Return(nil, nil) + helmMock.EXPECT().InstallChart(mock.Anything, mock.MatchedBy(func(cfg installer.ChartConfig) bool { return cfg.Version == "7.0.0" && cfg.ReleaseName == "argocd" && cfg.Namespace == "argocd" && @@ -38,14 +39,11 @@ var _ = Describe("ArgoCD.Install", func() { err := a.Install() Expect(err).ToNot(HaveOccurred()) - - helmMock.AssertCalled(GinkgoT(), "FindRelease", "argocd") - helmMock.AssertCalled(GinkgoT(), "InstallChart", mock.Anything, mock.Anything) - helmMock.AssertNotCalled(GinkgoT(), "UpgradeChart", mock.Anything, mock.Anything) }) It("performs a fresh install with latest version when Version is empty", func() { - helmMock.On("InstallChart", mock.Anything, mock.MatchedBy(func(cfg installer.ChartConfig) bool { + helmMock.EXPECT().FindRelease("argocd").Return(nil, nil) + helmMock.EXPECT().InstallChart(mock.Anything, mock.MatchedBy(func(cfg installer.ChartConfig) bool { return cfg.Version == "" })).Return(nil) @@ -53,13 +51,10 @@ var _ = Describe("ArgoCD.Install", func() { err := a.Install() Expect(err).ToNot(HaveOccurred()) - - helmMock.AssertNumberOfCalls(GinkgoT(), "InstallChart", 1) - helmMock.AssertNotCalled(GinkgoT(), "UpgradeChart", mock.Anything, mock.Anything) }) It("returns an error when InstallChart fails", func() { - helmMock.On("InstallChart", mock.Anything, mock.Anything). + helmMock.EXPECT().InstallChart(mock.Anything, mock.Anything). Return(errors.New("chart not found")) err := a.Install() @@ -73,22 +68,24 @@ var _ = Describe("ArgoCD.Install", func() { BeforeEach(func() { helmMock.On("FindRelease", "argocd").Return(existingRelease, nil) + helmMock.EXPECT().FindRelease("argocd").Return(&installer.ReleaseInfo{ + Name: "argocd", + InstalledVersion: "6.0.0", + }, nil) }) It("upgrades to a newer version", func() { - helmMock.On("UpgradeChart", mock.Anything, mock.MatchedBy(func(cfg installer.ChartConfig) bool { + + helmMock.EXPECT().UpgradeChart(mock.Anything, mock.MatchedBy(func(cfg installer.ChartConfig) bool { return cfg.Version == "7.0.0" })).Return(nil) err := a.Install() Expect(err).ToNot(HaveOccurred()) - - helmMock.AssertNumberOfCalls(GinkgoT(), "UpgradeChart", 1) - helmMock.AssertNotCalled(GinkgoT(), "InstallChart", mock.Anything, mock.Anything) }) It("upgrades to the same version (no-op upgrade)", func() { - helmMock.On("UpgradeChart", mock.Anything, mock.MatchedBy(func(cfg installer.ChartConfig) bool { + helmMock.EXPECT().UpgradeChart(mock.Anything, mock.MatchedBy(func(cfg installer.ChartConfig) bool { return cfg.Version == "6.0.0" })).Return(nil) @@ -96,12 +93,10 @@ var _ = Describe("ArgoCD.Install", func() { err := a.Install() Expect(err).ToNot(HaveOccurred()) - - helmMock.AssertNumberOfCalls(GinkgoT(), "UpgradeChart", 1) }) It("upgrades to latest when Version is empty", func() { - helmMock.On("UpgradeChart", mock.Anything, mock.MatchedBy(func(cfg installer.ChartConfig) bool { + helmMock.EXPECT().UpgradeChart(mock.Anything, mock.MatchedBy(func(cfg installer.ChartConfig) bool { return cfg.Version == "" })).Return(nil) @@ -109,8 +104,6 @@ var _ = Describe("ArgoCD.Install", func() { err := a.Install() Expect(err).ToNot(HaveOccurred()) - - helmMock.AssertNumberOfCalls(GinkgoT(), "UpgradeChart", 1) }) It("rejects a downgrade", func() { @@ -119,13 +112,10 @@ var _ = Describe("ArgoCD.Install", func() { err := a.Install() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("downgrade is not allowed")) - - helmMock.AssertNotCalled(GinkgoT(), "UpgradeChart", mock.Anything, mock.Anything) - helmMock.AssertNotCalled(GinkgoT(), "InstallChart", mock.Anything, mock.Anything) }) It("returns an error when UpgradeChart fails", func() { - helmMock.On("UpgradeChart", mock.Anything, mock.Anything). + helmMock.EXPECT().UpgradeChart(mock.Anything, mock.Anything). Return(errors.New("timeout waiting for condition")) a = &installer.ArgoCD{Version: "7.0.0", Helm: helmMock} @@ -138,7 +128,7 @@ var _ = Describe("ArgoCD.Install", func() { Context("when FindRelease returns an error", func() { It("propagates the error without installing or upgrading", func() { - helmMock.On("FindRelease", "argocd"). + helmMock.EXPECT().FindRelease("argocd"). Return(nil, errors.New("cluster unreachable")) a = &installer.ArgoCD{Version: "7.0.0", Helm: helmMock} @@ -146,16 +136,13 @@ var _ = Describe("ArgoCD.Install", func() { err := a.Install() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("cluster unreachable")) - - helmMock.AssertNotCalled(GinkgoT(), "InstallChart", mock.Anything, mock.Anything) - helmMock.AssertNotCalled(GinkgoT(), "UpgradeChart", mock.Anything, mock.Anything) }) }) Context("chart configuration", func() { It("always uses the correct chart name and repo URL", func() { - helmMock.On("FindRelease", "argocd").Return(nil, nil) - helmMock.On("InstallChart", mock.Anything, mock.MatchedBy(func(cfg installer.ChartConfig) bool { + helmMock.EXPECT().FindRelease("argocd").Return(nil, nil) + helmMock.EXPECT().InstallChart(mock.Anything, mock.MatchedBy(func(cfg installer.ChartConfig) bool { return cfg.ChartName == "argo-cd" && cfg.RepoURL == "https://argoproj.github.io/argo-helm" })).Return(nil) @@ -164,13 +151,11 @@ var _ = Describe("ArgoCD.Install", func() { err := a.Install() Expect(err).ToNot(HaveOccurred()) - - helmMock.AssertExpectations(GinkgoT()) }) It("disables dex in the values", func() { - helmMock.On("FindRelease", "argocd").Return(nil, nil) - helmMock.On("InstallChart", mock.Anything, mock.MatchedBy(func(cfg installer.ChartConfig) bool { + helmMock.EXPECT().FindRelease("argocd").Return(nil, nil) + helmMock.EXPECT().InstallChart(mock.Anything, mock.MatchedBy(func(cfg installer.ChartConfig) bool { dex, ok := cfg.Values["dex"].(map[string]interface{}) return ok && dex["enabled"] == false })).Return(nil) @@ -179,32 +164,26 @@ var _ = Describe("ArgoCD.Install", func() { err := a.Install() Expect(err).ToNot(HaveOccurred()) - - helmMock.AssertExpectations(GinkgoT()) }) }) Context("full installation", func() { BeforeEach(func() { - helmMock.On("FindRelease", "argocd").Return(nil, nil) - helmMock.On("InstallChart", mock.Anything, mock.Anything).Return(nil) + helmMock.EXPECT().FindRelease("argocd").Return(nil, nil) + helmMock.EXPECT().InstallChart(mock.Anything, mock.Anything).Return(nil) }) It("installs extra ArgoCD resources when FullInstall option in true", func() { - argoCDResourcesMock.On("ApplyAll", mock.Anything).Return(nil) + argoCDResourcesMock.EXPECT().ApplyAll(mock.Anything).Return(nil) a.FullInstall = true err := a.Install() Expect(err).ToNot(HaveOccurred()) - - argoCDResourcesMock.AssertCalled(GinkgoT(), "ApplyAll", mock.Anything) }) It("does not install extra ArgoCD resources when FullInstall option in false", func() { a.FullInstall = false err := a.Install() Expect(err).ToNot(HaveOccurred()) - - argoCDResourcesMock.AssertNotCalled(GinkgoT(), "ApplyAll") }) }) From 0f5866ee6eea7a561c66ea151060226a0cde783c Mon Sep 17 00:00:00 2001 From: bachgg Date: Thu, 12 Mar 2026 10:27:39 +0100 Subject: [PATCH 30/33] remove internal k8s test stuff --- internal/installer/k8s_test.go | 156 --------------------------------- 1 file changed, 156 deletions(-) delete mode 100644 internal/installer/k8s_test.go diff --git a/internal/installer/k8s_test.go b/internal/installer/k8s_test.go deleted file mode 100644 index 48424cb6..00000000 --- a/internal/installer/k8s_test.go +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright (c) Codesphere Inc. -// SPDX-License-Identifier: Apache-2.0 - -package installer - -import ( - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" -) - -var _ = Describe("decodeMultiDocYAML", func() { - It("decodes a single YAML document", func() { - yaml := []byte(` -apiVersion: v1 -kind: Secret -metadata: - name: my-secret - namespace: argocd -`) - objects, err := decodeMultiDocYAML(yaml) - Expect(err).ToNot(HaveOccurred()) - Expect(objects).To(HaveLen(1)) - Expect(objects[0].GetName()).To(Equal("my-secret")) - Expect(objects[0].GetNamespace()).To(Equal("argocd")) - Expect(objects[0].GetKind()).To(Equal("Secret")) - }) - - It("decodes multiple YAML documents separated by ---", func() { - yaml := []byte(` -apiVersion: argoproj.io/v1alpha1 -kind: AppProject -metadata: - name: prod - namespace: argocd ---- -apiVersion: argoproj.io/v1alpha1 -kind: AppProject -metadata: - name: dev - namespace: argocd ---- -apiVersion: argoproj.io/v1alpha1 -kind: AppProject -metadata: - name: default - namespace: argocd -`) - objects, err := decodeMultiDocYAML(yaml) - Expect(err).ToNot(HaveOccurred()) - Expect(objects).To(HaveLen(3)) - Expect(objects[0].GetName()).To(Equal("prod")) - Expect(objects[1].GetName()).To(Equal("dev")) - Expect(objects[2].GetName()).To(Equal("default")) - }) - - It("skips empty documents", func() { - yaml := []byte(` ---- -apiVersion: v1 -kind: Secret -metadata: - name: only-one - namespace: argocd ---- -`) - objects, err := decodeMultiDocYAML(yaml) - Expect(err).ToNot(HaveOccurred()) - Expect(objects).To(HaveLen(1)) - Expect(objects[0].GetName()).To(Equal("only-one")) - }) - - It("returns empty slice for empty input", func() { - objects, err := decodeMultiDocYAML([]byte("")) - Expect(err).ToNot(HaveOccurred()) - Expect(objects).To(BeEmpty()) - }) - - It("returns an error for invalid YAML", func() { - yaml := []byte(`not: valid: yaml: [`) - _, err := decodeMultiDocYAML(yaml) - Expect(err).To(HaveOccurred()) - }) -}) - -var _ = Describe("renderTemplate", func() { - It("replaces a single variable", func() { - tpl := []byte(`name: "dc-${DC_NUMBER}"`) - rendered, err := renderTemplate(tpl, map[string]string{ - "DC_NUMBER": "5", - }) - Expect(err).ToNot(HaveOccurred()) - Expect(string(rendered)).To(Equal(`name: "dc-5"`)) - }) - - It("replaces multiple variables", func() { - tpl := []byte(`server: ${HOST}, port: ${PORT}`) - rendered, err := renderTemplate(tpl, map[string]string{ - "HOST": "localhost", - "PORT": "8080", - }) - Expect(err).ToNot(HaveOccurred()) - Expect(string(rendered)).To(Equal(`server: localhost, port: 8080`)) - }) - - It("replaces multiple occurrences of the same variable", func() { - tpl := []byte(`a: ${VAR}, b: ${VAR}`) - rendered, err := renderTemplate(tpl, map[string]string{ - "VAR": "value", - }) - Expect(err).ToNot(HaveOccurred()) - Expect(string(rendered)).To(Equal(`a: value, b: value`)) - }) - - It("leaves unmatched placeholders intact", func() { - tpl := []byte(`name: ${KNOWN}, secret: ${UNKNOWN}`) - rendered, err := renderTemplate(tpl, map[string]string{ - "KNOWN": "replaced", - }) - Expect(err).ToNot(HaveOccurred()) - Expect(string(rendered)).To(ContainSubstring("replaced")) - Expect(string(rendered)).To(ContainSubstring("${UNKNOWN}")) - }) - - It("returns input unchanged when vars map is empty", func() { - tpl := []byte(`nothing: to replace`) - rendered, err := renderTemplate(tpl, map[string]string{}) - Expect(err).ToNot(HaveOccurred()) - Expect(string(rendered)).To(Equal(`nothing: to replace`)) - }) -}) - -var _ = Describe("gvrForUnstructured", func() { - It("returns the correct GVR for AppProject", func() { - obj := &unstructured.Unstructured{} - obj.SetAPIVersion("argoproj.io/v1alpha1") - obj.SetKind("AppProject") - - gvr, err := gvrForUnstructured(obj) - Expect(err).ToNot(HaveOccurred()) - Expect(gvr.Group).To(Equal("argoproj.io")) - Expect(gvr.Version).To(Equal("v1alpha1")) - Expect(gvr.Resource).To(Equal("appprojects")) - }) - - It("returns an error for an unknown kind", func() { - obj := &unstructured.Unstructured{} - obj.SetAPIVersion("v1") - obj.SetKind("ConfigMap") - - _, err := gvrForUnstructured(obj) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("no GVR mapping")) - }) -}) From 3c7ca1752509a5589520af1ba1a0025533b3b2b3 Mon Sep 17 00:00:00 2001 From: bachgg Date: Thu, 12 Mar 2026 10:29:03 +0100 Subject: [PATCH 31/33] Revert "remove internal k8s test stuff" This reverts commit 0f5866ee6eea7a561c66ea151060226a0cde783c. --- internal/installer/k8s_test.go | 156 +++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 internal/installer/k8s_test.go diff --git a/internal/installer/k8s_test.go b/internal/installer/k8s_test.go new file mode 100644 index 00000000..48424cb6 --- /dev/null +++ b/internal/installer/k8s_test.go @@ -0,0 +1,156 @@ +// Copyright (c) Codesphere Inc. +// SPDX-License-Identifier: Apache-2.0 + +package installer + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +var _ = Describe("decodeMultiDocYAML", func() { + It("decodes a single YAML document", func() { + yaml := []byte(` +apiVersion: v1 +kind: Secret +metadata: + name: my-secret + namespace: argocd +`) + objects, err := decodeMultiDocYAML(yaml) + Expect(err).ToNot(HaveOccurred()) + Expect(objects).To(HaveLen(1)) + Expect(objects[0].GetName()).To(Equal("my-secret")) + Expect(objects[0].GetNamespace()).To(Equal("argocd")) + Expect(objects[0].GetKind()).To(Equal("Secret")) + }) + + It("decodes multiple YAML documents separated by ---", func() { + yaml := []byte(` +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: prod + namespace: argocd +--- +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: dev + namespace: argocd +--- +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: default + namespace: argocd +`) + objects, err := decodeMultiDocYAML(yaml) + Expect(err).ToNot(HaveOccurred()) + Expect(objects).To(HaveLen(3)) + Expect(objects[0].GetName()).To(Equal("prod")) + Expect(objects[1].GetName()).To(Equal("dev")) + Expect(objects[2].GetName()).To(Equal("default")) + }) + + It("skips empty documents", func() { + yaml := []byte(` +--- +apiVersion: v1 +kind: Secret +metadata: + name: only-one + namespace: argocd +--- +`) + objects, err := decodeMultiDocYAML(yaml) + Expect(err).ToNot(HaveOccurred()) + Expect(objects).To(HaveLen(1)) + Expect(objects[0].GetName()).To(Equal("only-one")) + }) + + It("returns empty slice for empty input", func() { + objects, err := decodeMultiDocYAML([]byte("")) + Expect(err).ToNot(HaveOccurred()) + Expect(objects).To(BeEmpty()) + }) + + It("returns an error for invalid YAML", func() { + yaml := []byte(`not: valid: yaml: [`) + _, err := decodeMultiDocYAML(yaml) + Expect(err).To(HaveOccurred()) + }) +}) + +var _ = Describe("renderTemplate", func() { + It("replaces a single variable", func() { + tpl := []byte(`name: "dc-${DC_NUMBER}"`) + rendered, err := renderTemplate(tpl, map[string]string{ + "DC_NUMBER": "5", + }) + Expect(err).ToNot(HaveOccurred()) + Expect(string(rendered)).To(Equal(`name: "dc-5"`)) + }) + + It("replaces multiple variables", func() { + tpl := []byte(`server: ${HOST}, port: ${PORT}`) + rendered, err := renderTemplate(tpl, map[string]string{ + "HOST": "localhost", + "PORT": "8080", + }) + Expect(err).ToNot(HaveOccurred()) + Expect(string(rendered)).To(Equal(`server: localhost, port: 8080`)) + }) + + It("replaces multiple occurrences of the same variable", func() { + tpl := []byte(`a: ${VAR}, b: ${VAR}`) + rendered, err := renderTemplate(tpl, map[string]string{ + "VAR": "value", + }) + Expect(err).ToNot(HaveOccurred()) + Expect(string(rendered)).To(Equal(`a: value, b: value`)) + }) + + It("leaves unmatched placeholders intact", func() { + tpl := []byte(`name: ${KNOWN}, secret: ${UNKNOWN}`) + rendered, err := renderTemplate(tpl, map[string]string{ + "KNOWN": "replaced", + }) + Expect(err).ToNot(HaveOccurred()) + Expect(string(rendered)).To(ContainSubstring("replaced")) + Expect(string(rendered)).To(ContainSubstring("${UNKNOWN}")) + }) + + It("returns input unchanged when vars map is empty", func() { + tpl := []byte(`nothing: to replace`) + rendered, err := renderTemplate(tpl, map[string]string{}) + Expect(err).ToNot(HaveOccurred()) + Expect(string(rendered)).To(Equal(`nothing: to replace`)) + }) +}) + +var _ = Describe("gvrForUnstructured", func() { + It("returns the correct GVR for AppProject", func() { + obj := &unstructured.Unstructured{} + obj.SetAPIVersion("argoproj.io/v1alpha1") + obj.SetKind("AppProject") + + gvr, err := gvrForUnstructured(obj) + Expect(err).ToNot(HaveOccurred()) + Expect(gvr.Group).To(Equal("argoproj.io")) + Expect(gvr.Version).To(Equal("v1alpha1")) + Expect(gvr.Resource).To(Equal("appprojects")) + }) + + It("returns an error for an unknown kind", func() { + obj := &unstructured.Unstructured{} + obj.SetAPIVersion("v1") + obj.SetKind("ConfigMap") + + _, err := gvrForUnstructured(obj) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("no GVR mapping")) + }) +}) From 23a10ea8ac15b6559d3e624a81d7c11b886055af Mon Sep 17 00:00:00 2001 From: bachgg Date: Thu, 12 Mar 2026 10:31:56 +0100 Subject: [PATCH 32/33] make k8s helper functions public for testing --- internal/installer/argocd_resources.go | 18 ++++++++--------- internal/installer/k8s.go | 20 +++++++++---------- internal/installer/k8s_test.go | 27 +++++++++++++------------- 3 files changed, 33 insertions(+), 32 deletions(-) diff --git a/internal/installer/argocd_resources.go b/internal/installer/argocd_resources.go index 6c17b471..634c8679 100644 --- a/internal/installer/argocd_resources.go +++ b/internal/installer/argocd_resources.go @@ -75,17 +75,17 @@ func (a *argoCDResources) ApplyAll(ctx context.Context) error { func (a *argoCDResources) applyAppProjects(ctx context.Context) error { log.Println("Applying AppProjects... ") - objects, err := decodeMultiDocYAML(appProjectsYAML) + objects, err := DecodeMultiDocYAML(appProjectsYAML) if err != nil { return fmt.Errorf("decoding app projects yaml: %w", err) } for _, obj := range objects { - gvr, err := gvrForUnstructured(obj) + gvr, err := GvrForUnstructured(obj) if err != nil { return err } - if err := applyUnstructured(ctx, a.dynClient, gvr, obj); err != nil { + if err := ApplyUnstructured(ctx, a.dynClient, gvr, obj); err != nil { return fmt.Errorf("applying app project %q: %w", obj.GetName(), err) } } @@ -94,36 +94,36 @@ func (a *argoCDResources) applyAppProjects(ctx context.Context) error { func (a *argoCDResources) applyLocalCluster(ctx context.Context) error { log.Println("Applying local cluster secret... ") - rendered, err := renderTemplate(localClusterTpl, map[string]string{ + rendered, err := RenderTemplate(localClusterTpl, map[string]string{ "DC_NUMBER": a.DatacenterId, }) if err != nil { return fmt.Errorf("rendering local cluster template: %w", err) } - return applySecretFromYAML(ctx, a.clientset, rendered) + return ApplySecretFromYAML(ctx, a.clientset, rendered) } func (a *argoCDResources) applyHelmRegistrySecret(ctx context.Context) error { log.Println("Applying helm registry secret... ") - rendered, err := renderTemplate(helmRegistryTpl, map[string]string{ + rendered, err := RenderTemplate(helmRegistryTpl, map[string]string{ "SECRET_CODESPHERE_OCI_READ": a.OciPassword, }) if err != nil { return fmt.Errorf("rendering helm registry template: %w", err) } - return applySecretFromYAML(ctx, a.clientset, rendered) + return ApplySecretFromYAML(ctx, a.clientset, rendered) } func (a *argoCDResources) applyGitRepoSecret(ctx context.Context) error { log.Println("Applying git repo secret... ") - rendered, err := renderTemplate(gitRepoTpl, map[string]string{ + rendered, err := RenderTemplate(gitRepoTpl, map[string]string{ "SECRET_CODESPHERE_REPOS_READ": a.GitPassword, }) if err != nil { return fmt.Errorf("rendering git repo template: %w", err) } - return applySecretFromYAML(ctx, a.clientset, rendered) + return ApplySecretFromYAML(ctx, a.clientset, rendered) } diff --git a/internal/installer/k8s.go b/internal/installer/k8s.go index 7b3bca40..0582b076 100644 --- a/internal/installer/k8s.go +++ b/internal/installer/k8s.go @@ -21,9 +21,9 @@ import ( sigyaml "sigs.k8s.io/yaml" ) -// decodeMultiDocYAML splits a multi-document YAML byte slice into +// DecodeMultiDocYAML splits a multi-document YAML byte slice into // individual unstructured objects. This handles the "---" separators. -func decodeMultiDocYAML(data []byte) ([]*unstructured.Unstructured, error) { +func DecodeMultiDocYAML(data []byte) ([]*unstructured.Unstructured, error) { var objects []*unstructured.Unstructured reader := yaml.NewYAMLOrJSONDecoder(bytes.NewReader(data), 4096) @@ -44,8 +44,8 @@ func decodeMultiDocYAML(data []byte) ([]*unstructured.Unstructured, error) { return objects, nil } -// renderTemplate performs simple ${VAR} substitution on a raw byte slice. -func renderTemplate(raw []byte, vars map[string]string) ([]byte, error) { +// RenderTemplate performs simple ${VAR} substitution on a raw byte slice. +func RenderTemplate(raw []byte, vars map[string]string) ([]byte, error) { content := string(raw) for key, val := range vars { content = strings.ReplaceAll(content, "${"+key+"}", val) @@ -53,8 +53,8 @@ func renderTemplate(raw []byte, vars map[string]string) ([]byte, error) { return []byte(content), nil } -// gvrForUnstructured maps an unstructured object's GVK to the appropriate GVR. -func gvrForUnstructured(obj *unstructured.Unstructured) (schema.GroupVersionResource, error) { +// GvrForUnstructured maps an unstructured object's GVK to the appropriate GVR. +func GvrForUnstructured(obj *unstructured.Unstructured) (schema.GroupVersionResource, error) { gvk := obj.GroupVersionKind() switch gvk.Kind { @@ -69,8 +69,8 @@ func gvrForUnstructured(obj *unstructured.Unstructured) (schema.GroupVersionReso } } -// applyUnstructured creates or updates an unstructured resource using the dynamic client. -func applyUnstructured(ctx context.Context, dynClient dynamic.Interface, gvr schema.GroupVersionResource, obj *unstructured.Unstructured) error { +// ApplyUnstructured creates or updates an unstructured resource using the dynamic client. +func ApplyUnstructured(ctx context.Context, dynClient dynamic.Interface, gvr schema.GroupVersionResource, obj *unstructured.Unstructured) error { ns := obj.GetNamespace() name := obj.GetName() resource := dynClient.Resource(gvr).Namespace(ns) @@ -92,8 +92,8 @@ func applyUnstructured(ctx context.Context, dynClient dynamic.Interface, gvr sch return nil } -// applySecretFromYAML creates or updates a corev1.Secret parsed from YAML bytes. -func applySecretFromYAML(ctx context.Context, clientset kubernetes.Interface, data []byte) error { +// ApplySecretFromYAML creates or updates a corev1.Secret parsed from YAML bytes. +func ApplySecretFromYAML(ctx context.Context, clientset kubernetes.Interface, data []byte) error { secret := &corev1.Secret{} if err := sigyaml.Unmarshal(data, secret); err != nil { return fmt.Errorf("unmarshaling secret yaml: %w", err) diff --git a/internal/installer/k8s_test.go b/internal/installer/k8s_test.go index 48424cb6..fcfa9082 100644 --- a/internal/installer/k8s_test.go +++ b/internal/installer/k8s_test.go @@ -1,9 +1,10 @@ // Copyright (c) Codesphere Inc. // SPDX-License-Identifier: Apache-2.0 -package installer +package installer_test import ( + "github.com/codesphere-cloud/oms/internal/installer" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -19,7 +20,7 @@ metadata: name: my-secret namespace: argocd `) - objects, err := decodeMultiDocYAML(yaml) + objects, err := installer.DecodeMultiDocYAML(yaml) Expect(err).ToNot(HaveOccurred()) Expect(objects).To(HaveLen(1)) Expect(objects[0].GetName()).To(Equal("my-secret")) @@ -47,7 +48,7 @@ metadata: name: default namespace: argocd `) - objects, err := decodeMultiDocYAML(yaml) + objects, err := installer.DecodeMultiDocYAML(yaml) Expect(err).ToNot(HaveOccurred()) Expect(objects).To(HaveLen(3)) Expect(objects[0].GetName()).To(Equal("prod")) @@ -65,21 +66,21 @@ metadata: namespace: argocd --- `) - objects, err := decodeMultiDocYAML(yaml) + objects, err := installer.DecodeMultiDocYAML(yaml) Expect(err).ToNot(HaveOccurred()) Expect(objects).To(HaveLen(1)) Expect(objects[0].GetName()).To(Equal("only-one")) }) It("returns empty slice for empty input", func() { - objects, err := decodeMultiDocYAML([]byte("")) + objects, err := installer.DecodeMultiDocYAML([]byte("")) Expect(err).ToNot(HaveOccurred()) Expect(objects).To(BeEmpty()) }) It("returns an error for invalid YAML", func() { yaml := []byte(`not: valid: yaml: [`) - _, err := decodeMultiDocYAML(yaml) + _, err := installer.DecodeMultiDocYAML(yaml) Expect(err).To(HaveOccurred()) }) }) @@ -87,7 +88,7 @@ metadata: var _ = Describe("renderTemplate", func() { It("replaces a single variable", func() { tpl := []byte(`name: "dc-${DC_NUMBER}"`) - rendered, err := renderTemplate(tpl, map[string]string{ + rendered, err := installer.RenderTemplate(tpl, map[string]string{ "DC_NUMBER": "5", }) Expect(err).ToNot(HaveOccurred()) @@ -96,7 +97,7 @@ var _ = Describe("renderTemplate", func() { It("replaces multiple variables", func() { tpl := []byte(`server: ${HOST}, port: ${PORT}`) - rendered, err := renderTemplate(tpl, map[string]string{ + rendered, err := installer.RenderTemplate(tpl, map[string]string{ "HOST": "localhost", "PORT": "8080", }) @@ -106,7 +107,7 @@ var _ = Describe("renderTemplate", func() { It("replaces multiple occurrences of the same variable", func() { tpl := []byte(`a: ${VAR}, b: ${VAR}`) - rendered, err := renderTemplate(tpl, map[string]string{ + rendered, err := installer.RenderTemplate(tpl, map[string]string{ "VAR": "value", }) Expect(err).ToNot(HaveOccurred()) @@ -115,7 +116,7 @@ var _ = Describe("renderTemplate", func() { It("leaves unmatched placeholders intact", func() { tpl := []byte(`name: ${KNOWN}, secret: ${UNKNOWN}`) - rendered, err := renderTemplate(tpl, map[string]string{ + rendered, err := installer.RenderTemplate(tpl, map[string]string{ "KNOWN": "replaced", }) Expect(err).ToNot(HaveOccurred()) @@ -125,7 +126,7 @@ var _ = Describe("renderTemplate", func() { It("returns input unchanged when vars map is empty", func() { tpl := []byte(`nothing: to replace`) - rendered, err := renderTemplate(tpl, map[string]string{}) + rendered, err := installer.RenderTemplate(tpl, map[string]string{}) Expect(err).ToNot(HaveOccurred()) Expect(string(rendered)).To(Equal(`nothing: to replace`)) }) @@ -137,7 +138,7 @@ var _ = Describe("gvrForUnstructured", func() { obj.SetAPIVersion("argoproj.io/v1alpha1") obj.SetKind("AppProject") - gvr, err := gvrForUnstructured(obj) + gvr, err := installer.GvrForUnstructured(obj) Expect(err).ToNot(HaveOccurred()) Expect(gvr.Group).To(Equal("argoproj.io")) Expect(gvr.Version).To(Equal("v1alpha1")) @@ -149,7 +150,7 @@ var _ = Describe("gvrForUnstructured", func() { obj.SetAPIVersion("v1") obj.SetKind("ConfigMap") - _, err := gvrForUnstructured(obj) + _, err := installer.GvrForUnstructured(obj) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("no GVR mapping")) }) From fe500d1081d9300d59581fd2e0728016ea5a3f14 Mon Sep 17 00:00:00 2001 From: bachgg Date: Thu, 12 Mar 2026 10:34:39 +0100 Subject: [PATCH 33/33] move k8s helpers to util --- internal/installer/argocd_resources.go | 21 +++++++++--------- internal/{installer => util}/k8s.go | 4 ++-- internal/{installer => util}/k8s_test.go | 28 ++++++++++++------------ 3 files changed, 27 insertions(+), 26 deletions(-) rename internal/{installer => util}/k8s.go (98%) rename internal/{installer => util}/k8s_test.go (82%) diff --git a/internal/installer/argocd_resources.go b/internal/installer/argocd_resources.go index 634c8679..74b1b7ca 100644 --- a/internal/installer/argocd_resources.go +++ b/internal/installer/argocd_resources.go @@ -9,6 +9,7 @@ import ( "fmt" "log" + k8s "github.com/codesphere-cloud/oms/internal/util" "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" ) @@ -27,7 +28,7 @@ type argoCDResources struct { } func NewArgoCDResources(dataCenterId string, ociPassword string, gitPassword string) (ArgoCDResources, error) { - clientset, dynClient, err := newClients() + clientset, dynClient, err := k8s.NewClients() if err != nil { return nil, fmt.Errorf("creating kubernetes clients: %w", err) } @@ -75,17 +76,17 @@ func (a *argoCDResources) ApplyAll(ctx context.Context) error { func (a *argoCDResources) applyAppProjects(ctx context.Context) error { log.Println("Applying AppProjects... ") - objects, err := DecodeMultiDocYAML(appProjectsYAML) + objects, err := k8s.DecodeMultiDocYAML(appProjectsYAML) if err != nil { return fmt.Errorf("decoding app projects yaml: %w", err) } for _, obj := range objects { - gvr, err := GvrForUnstructured(obj) + gvr, err := k8s.GvrForUnstructured(obj) if err != nil { return err } - if err := ApplyUnstructured(ctx, a.dynClient, gvr, obj); err != nil { + if err := k8s.ApplyUnstructured(ctx, a.dynClient, gvr, obj); err != nil { return fmt.Errorf("applying app project %q: %w", obj.GetName(), err) } } @@ -94,36 +95,36 @@ func (a *argoCDResources) applyAppProjects(ctx context.Context) error { func (a *argoCDResources) applyLocalCluster(ctx context.Context) error { log.Println("Applying local cluster secret... ") - rendered, err := RenderTemplate(localClusterTpl, map[string]string{ + rendered, err := k8s.RenderTemplate(localClusterTpl, map[string]string{ "DC_NUMBER": a.DatacenterId, }) if err != nil { return fmt.Errorf("rendering local cluster template: %w", err) } - return ApplySecretFromYAML(ctx, a.clientset, rendered) + return k8s.ApplySecretFromYAML(ctx, a.clientset, rendered) } func (a *argoCDResources) applyHelmRegistrySecret(ctx context.Context) error { log.Println("Applying helm registry secret... ") - rendered, err := RenderTemplate(helmRegistryTpl, map[string]string{ + rendered, err := k8s.RenderTemplate(helmRegistryTpl, map[string]string{ "SECRET_CODESPHERE_OCI_READ": a.OciPassword, }) if err != nil { return fmt.Errorf("rendering helm registry template: %w", err) } - return ApplySecretFromYAML(ctx, a.clientset, rendered) + return k8s.ApplySecretFromYAML(ctx, a.clientset, rendered) } func (a *argoCDResources) applyGitRepoSecret(ctx context.Context) error { log.Println("Applying git repo secret... ") - rendered, err := RenderTemplate(gitRepoTpl, map[string]string{ + rendered, err := k8s.RenderTemplate(gitRepoTpl, map[string]string{ "SECRET_CODESPHERE_REPOS_READ": a.GitPassword, }) if err != nil { return fmt.Errorf("rendering git repo template: %w", err) } - return ApplySecretFromYAML(ctx, a.clientset, rendered) + return k8s.ApplySecretFromYAML(ctx, a.clientset, rendered) } diff --git a/internal/installer/k8s.go b/internal/util/k8s.go similarity index 98% rename from internal/installer/k8s.go rename to internal/util/k8s.go index 0582b076..e841eb78 100644 --- a/internal/installer/k8s.go +++ b/internal/util/k8s.go @@ -1,7 +1,7 @@ // Copyright (c) Codesphere Inc. // SPDX-License-Identifier: Apache-2.0 -package installer +package util import ( "bytes" @@ -121,7 +121,7 @@ func ApplySecretFromYAML(ctx context.Context, clientset kubernetes.Interface, da // newClients creates both a typed and dynamic Kubernetes client // using the current kubeconfig context (respects KUBECONFIG env var // and defaults to ~/.kube/config). -func newClients() (kubernetes.Interface, dynamic.Interface, error) { +func NewClients() (kubernetes.Interface, dynamic.Interface, error) { loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() configOverrides := &clientcmd.ConfigOverrides{} kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides) diff --git a/internal/installer/k8s_test.go b/internal/util/k8s_test.go similarity index 82% rename from internal/installer/k8s_test.go rename to internal/util/k8s_test.go index fcfa9082..8954388d 100644 --- a/internal/installer/k8s_test.go +++ b/internal/util/k8s_test.go @@ -1,10 +1,10 @@ // Copyright (c) Codesphere Inc. // SPDX-License-Identifier: Apache-2.0 -package installer_test +package util_test import ( - "github.com/codesphere-cloud/oms/internal/installer" + "github.com/codesphere-cloud/oms/internal/util" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -20,7 +20,7 @@ metadata: name: my-secret namespace: argocd `) - objects, err := installer.DecodeMultiDocYAML(yaml) + objects, err := util.DecodeMultiDocYAML(yaml) Expect(err).ToNot(HaveOccurred()) Expect(objects).To(HaveLen(1)) Expect(objects[0].GetName()).To(Equal("my-secret")) @@ -48,7 +48,7 @@ metadata: name: default namespace: argocd `) - objects, err := installer.DecodeMultiDocYAML(yaml) + objects, err := util.DecodeMultiDocYAML(yaml) Expect(err).ToNot(HaveOccurred()) Expect(objects).To(HaveLen(3)) Expect(objects[0].GetName()).To(Equal("prod")) @@ -66,21 +66,21 @@ metadata: namespace: argocd --- `) - objects, err := installer.DecodeMultiDocYAML(yaml) + objects, err := util.DecodeMultiDocYAML(yaml) Expect(err).ToNot(HaveOccurred()) Expect(objects).To(HaveLen(1)) Expect(objects[0].GetName()).To(Equal("only-one")) }) It("returns empty slice for empty input", func() { - objects, err := installer.DecodeMultiDocYAML([]byte("")) + objects, err := util.DecodeMultiDocYAML([]byte("")) Expect(err).ToNot(HaveOccurred()) Expect(objects).To(BeEmpty()) }) It("returns an error for invalid YAML", func() { yaml := []byte(`not: valid: yaml: [`) - _, err := installer.DecodeMultiDocYAML(yaml) + _, err := util.DecodeMultiDocYAML(yaml) Expect(err).To(HaveOccurred()) }) }) @@ -88,7 +88,7 @@ metadata: var _ = Describe("renderTemplate", func() { It("replaces a single variable", func() { tpl := []byte(`name: "dc-${DC_NUMBER}"`) - rendered, err := installer.RenderTemplate(tpl, map[string]string{ + rendered, err := util.RenderTemplate(tpl, map[string]string{ "DC_NUMBER": "5", }) Expect(err).ToNot(HaveOccurred()) @@ -97,7 +97,7 @@ var _ = Describe("renderTemplate", func() { It("replaces multiple variables", func() { tpl := []byte(`server: ${HOST}, port: ${PORT}`) - rendered, err := installer.RenderTemplate(tpl, map[string]string{ + rendered, err := util.RenderTemplate(tpl, map[string]string{ "HOST": "localhost", "PORT": "8080", }) @@ -107,7 +107,7 @@ var _ = Describe("renderTemplate", func() { It("replaces multiple occurrences of the same variable", func() { tpl := []byte(`a: ${VAR}, b: ${VAR}`) - rendered, err := installer.RenderTemplate(tpl, map[string]string{ + rendered, err := util.RenderTemplate(tpl, map[string]string{ "VAR": "value", }) Expect(err).ToNot(HaveOccurred()) @@ -116,7 +116,7 @@ var _ = Describe("renderTemplate", func() { It("leaves unmatched placeholders intact", func() { tpl := []byte(`name: ${KNOWN}, secret: ${UNKNOWN}`) - rendered, err := installer.RenderTemplate(tpl, map[string]string{ + rendered, err := util.RenderTemplate(tpl, map[string]string{ "KNOWN": "replaced", }) Expect(err).ToNot(HaveOccurred()) @@ -126,7 +126,7 @@ var _ = Describe("renderTemplate", func() { It("returns input unchanged when vars map is empty", func() { tpl := []byte(`nothing: to replace`) - rendered, err := installer.RenderTemplate(tpl, map[string]string{}) + rendered, err := util.RenderTemplate(tpl, map[string]string{}) Expect(err).ToNot(HaveOccurred()) Expect(string(rendered)).To(Equal(`nothing: to replace`)) }) @@ -138,7 +138,7 @@ var _ = Describe("gvrForUnstructured", func() { obj.SetAPIVersion("argoproj.io/v1alpha1") obj.SetKind("AppProject") - gvr, err := installer.GvrForUnstructured(obj) + gvr, err := util.GvrForUnstructured(obj) Expect(err).ToNot(HaveOccurred()) Expect(gvr.Group).To(Equal("argoproj.io")) Expect(gvr.Version).To(Equal("v1alpha1")) @@ -150,7 +150,7 @@ var _ = Describe("gvrForUnstructured", func() { obj.SetAPIVersion("v1") obj.SetKind("ConfigMap") - _, err := installer.GvrForUnstructured(obj) + _, err := util.GvrForUnstructured(obj) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("no GVR mapping")) })