From 2d3a71f0ed8dfce43e8fcaf5b5d87a19c8692076 Mon Sep 17 00:00:00 2001 From: Doug Goldstein Date: Tue, 29 Apr 2025 10:51:16 -0500 Subject: [PATCH 1/3] feat(cli): require subcommand and include top-level command If you ran the binary with no arguments then nothing happened and there was no details on what to do. The binary also didn't include its own name so the help output was awkward so add the name of the binary. --- go/understack/cmd/root.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/go/understack/cmd/root.go b/go/understack/cmd/root.go index d0d39ac51..c2481e0ff 100644 --- a/go/understack/cmd/root.go +++ b/go/understack/cmd/root.go @@ -5,9 +5,13 @@ import ( ) var RootCmd = &cobra.Command{ - Use: "", + Use: "understack", Short: "", Long: ``, + RunE: func(cmd *cobra.Command, args []string) error { + // If no subcommand, show help + return cmd.Help() + }, PersistentPreRun: func(cmd *cobra.Command, args []string) { }, PreRun: func(cmd *cobra.Command, args []string) { From cba26d92a3827c9fa3ecc092a4ac6351986160d5 Mon Sep 17 00:00:00 2001 From: Doug Goldstein Date: Tue, 29 Apr 2025 11:27:34 -0500 Subject: [PATCH 2/3] feat(cli): add deploy repo as a persistent flag To help all the deployment commands operate on the correct path, add a global/persistent flag that defines the path to your deployment repo and changes to its path for all the execution of the commands. --- go/understack/cmd/dex/secrets.go | 2 +- go/understack/cmd/helmConfig/helmConfig.go | 4 -- go/understack/cmd/init/init.go | 2 - go/understack/cmd/other/other.go | 1 - go/understack/cmd/root.go | 72 +++++++++++++++++++++- go/understack/go.mod | 10 +++ go/understack/go.sum | 20 ++++++ go/understack/helpers/helpers.go | 1 - 8 files changed, 101 insertions(+), 11 deletions(-) diff --git a/go/understack/cmd/dex/secrets.go b/go/understack/cmd/dex/secrets.go index e8c04ae02..1c07666dd 100644 --- a/go/understack/cmd/dex/secrets.go +++ b/go/understack/cmd/dex/secrets.go @@ -55,7 +55,7 @@ func generateDexServiceSecrets() error { } } - helpers.UpdateKustomizeFile(filepath.Join(envutil.Getenv("UC_DEPLOY"), envutil.Getenv("DEPLOY_NAME"), "manifests", "dex")) + helpers.UpdateKustomizeFile(filepath.Join(envutil.Getenv("DEPLOY_NAME"), "manifests", "dex")) return nil } diff --git a/go/understack/cmd/helmConfig/helmConfig.go b/go/understack/cmd/helmConfig/helmConfig.go index d9461cdd7..cbbe913f7 100644 --- a/go/understack/cmd/helmConfig/helmConfig.go +++ b/go/understack/cmd/helmConfig/helmConfig.go @@ -68,7 +68,6 @@ func dex() error { } filePath := filepath.Join( - envutil.Getenv("UC_DEPLOY"), envutil.Getenv("DEPLOY_NAME"), "helm", "dex.yaml", @@ -86,7 +85,6 @@ func glance() error { size: 20Gi` filePath := filepath.Join( - envutil.Getenv("UC_DEPLOY"), envutil.Getenv("DEPLOY_NAME"), "helm", "glance.yaml", @@ -119,7 +117,6 @@ func ironic() error { mountPath: /var/lib/openstack-helm` filePath := filepath.Join( - envutil.Getenv("UC_DEPLOY"), envutil.Getenv("DEPLOY_NAME"), "helm", "ironic.yaml", @@ -216,7 +213,6 @@ func rook() error { memory: "50Mi"` filePath := filepath.Join( - envutil.Getenv("UC_DEPLOY"), envutil.Getenv("DEPLOY_NAME"), "helm", "rook-cluster.yaml", diff --git a/go/understack/cmd/init/init.go b/go/understack/cmd/init/init.go index 3fb1b33f4..66ea948af 100644 --- a/go/understack/cmd/init/init.go +++ b/go/understack/cmd/init/init.go @@ -33,7 +33,6 @@ var Init = &cobra.Command{ func initRun(cmd *cobra.Command, args []string) { log.Info("using envs", - "UC_DEPLOY", envutil.Getenv("UC_DEPLOY"), "DEPLOY_NAME", envutil.Getenv("DEPLOY_NAME"), "UC_DEPLOY_GIT_URL", envutil.Getenv("UC_DEPLOY_GIT_URL"), "UC_DEPLOY_SSH_FILE", envutil.Getenv("UC_DEPLOY_SSH_FILE"), @@ -58,7 +57,6 @@ func initRun(cmd *cobra.Command, args []string) { os.Exit(1) } - fmt.Println(envutil.Getenv("UC_DEPLOY")) fmt.Println(envutil.Getenv("DEPLOY_NAME")) log.Info("== Node Update") diff --git a/go/understack/cmd/other/other.go b/go/understack/cmd/other/other.go index a9b55f3a1..930c53842 100644 --- a/go/understack/cmd/other/other.go +++ b/go/understack/cmd/other/other.go @@ -101,7 +101,6 @@ func updateOpenStackSecretsFile() error { } secretFilePath := filepath.Join( - envutil.Getenv("UC_DEPLOY"), envutil.Getenv("DEPLOY_NAME"), "manifests", "secret-openstack.yaml", diff --git a/go/understack/cmd/root.go b/go/understack/cmd/root.go index c2481e0ff..e4d486cf5 100644 --- a/go/understack/cmd/root.go +++ b/go/understack/cmd/root.go @@ -1,7 +1,18 @@ package cmd import ( + "fmt" + "github.com/charmbracelet/log" "github.com/spf13/cobra" + "github.com/spf13/viper" + "os" + "path/filepath" + "strings" +) + +const ( + deployRepoEnvVar = "UC_DEPLOY" + deployRepoFlag = "deploy-repo" ) var RootCmd = &cobra.Command{ @@ -12,8 +23,7 @@ var RootCmd = &cobra.Command{ // If no subcommand, show help return cmd.Help() }, - PersistentPreRun: func(cmd *cobra.Command, args []string) { - }, + PersistentPreRunE: preRun, PreRun: func(cmd *cobra.Command, args []string) { }, Run: func(cmd *cobra.Command, args []string) { @@ -24,10 +34,68 @@ var RootCmd = &cobra.Command{ }, } +func expandPath(path string) (string, error) { + if strings.HasPrefix(path, "~") { + home, err := os.UserHomeDir() + if err != nil { + return "", err + } + return filepath.Join(home, path[1:]), nil + } + return path, nil +} + +func preRun(cmd *cobra.Command, args []string) error { + deployRepo := viper.GetString(deployRepoFlag) + if deployRepo == "" { + return nil + } + + expandedRepoDir, err := expandPath(deployRepo) + if err != nil { + return fmt.Errorf("failed to expand path: %w", err) + } + + info, err := os.Stat(expandedRepoDir) + if err != nil { + return fmt.Errorf("could not access directory '%s': %w", expandedRepoDir, err) + } + if !info.IsDir() { + return fmt.Errorf("path '%s' is not a directory", expandedRepoDir) + } + + // Switch working directory + if err := os.Chdir(expandedRepoDir); err != nil { + return fmt.Errorf("failed to change working directory to '%s': %w", expandedRepoDir, err) + } + + log.Infof("deployment repo path: %s", expandedRepoDir) + + return nil +} + // Execute will execute the root command func Execute() error { return RootCmd.Execute() } func init() { + // bind our flag + if err := viper.BindEnv(deployRepoFlag, deployRepoEnvVar); err != nil { + log.Fatal("failed to bind", "env", deployRepoFlag, "err", err) + os.Exit(1) + } + deployRepo := viper.GetString(deployRepoFlag) + if deployRepo == "" { + deployRepo = "." + } + helpText := fmt.Sprintf( + "Path to your deployment repo (env: %s) (current: %s)", + deployRepoEnvVar, deployRepo, + ) + RootCmd.PersistentFlags().String(deployRepoFlag, "", helpText) + if err := viper.BindPFlag(deployRepoFlag, RootCmd.PersistentFlags().Lookup(deployRepoFlag)); err != nil { + log.Fatal("failed to bind", "flag", deployRepoFlag, "err", err) + os.Exit(1) + } } diff --git a/go/understack/go.mod b/go/understack/go.mod index 077cdb714..6a3cf5084 100644 --- a/go/understack/go.mod +++ b/go/understack/go.mod @@ -7,6 +7,7 @@ require ( github.com/charmbracelet/log v0.4.1 github.com/gookit/goutil v0.6.18 github.com/spf13/cobra v1.9.1 + github.com/spf13/viper v1.20.1 k8s.io/api v0.32.3 k8s.io/apimachinery v0.32.3 k8s.io/client-go v0.32.3 @@ -22,12 +23,14 @@ require ( github.com/charmbracelet/x/ansi v0.4.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/gnostic-models v0.6.8 // indirect @@ -48,13 +51,20 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/muesli/termenv v0.16.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/rivo/uniseg v0.4.7 // indirect + github.com/sagikazarmark/locafero v0.7.0 // indirect github.com/shopspring/decimal v1.4.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.12.0 // indirect github.com/spf13/cast v1.7.1 // indirect github.com/spf13/pflag v1.0.6 // indirect + github.com/subosito/gotenv v1.6.0 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.9.0 // indirect golang.org/x/crypto v0.32.0 // indirect golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect golang.org/x/net v0.33.0 // indirect diff --git a/go/understack/go.sum b/go/understack/go.sum index 1c1d9df74..2600a7296 100644 --- a/go/understack/go.sum +++ b/go/understack/go.sum @@ -24,6 +24,8 @@ github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxER github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= 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.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= @@ -40,6 +42,8 @@ github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+Gr github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= +github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= @@ -100,6 +104,8 @@ github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -110,14 +116,22 @@ github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= +github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= +github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= +github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -127,12 +141,18 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= 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/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/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= diff --git a/go/understack/helpers/helpers.go b/go/understack/helpers/helpers.go index 7aca4e9b8..447df4cf9 100644 --- a/go/understack/helpers/helpers.go +++ b/go/understack/helpers/helpers.go @@ -19,7 +19,6 @@ type SecretConfig struct { func GetManifestPathToService(service string) string { return filepath.Join( - envutil.Getenv("UC_DEPLOY"), envutil.Getenv("DEPLOY_NAME"), "manifests", service, From 4b748ed2ae72f9b49b996037195e9d0546f7b184 Mon Sep 17 00:00:00 2001 From: Doug Goldstein Date: Tue, 29 Apr 2025 12:05:11 -0500 Subject: [PATCH 3/3] fix(cli): correct the ArgoCD secret name generated The value used here was the path to the deploy repo instead of the deployment name. --- go/understack/cmd/argocd/secrets.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/understack/cmd/argocd/secrets.go b/go/understack/cmd/argocd/secrets.go index ca038c2da..32ebc8e1f 100644 --- a/go/understack/cmd/argocd/secrets.go +++ b/go/understack/cmd/argocd/secrets.go @@ -64,7 +64,7 @@ func GenerateSecrets() error { func generateDeployRepoSecret(basePath string) error { vars := map[string]any{ "Config": `{"tlsClientConfig":{"insecure":false}}`, - "Name": envutil.Getenv("UC_DEPLOY"), + "Name": envutil.Getenv("DEPLOY_NAME"), "Server": "https://kubernetes.default.svc", "DEPLOY_NAME": envutil.Getenv("DEPLOY_NAME"), "UC_DEPLOY_GIT_URL": envutil.Getenv("UC_DEPLOY_GIT_URL"),