Files
zeekayandHanzo Dev 1ab6e799c1 cd: the module is github.com/hanzoai/cd, where it is actually hosted
go.mod still declared `module github.com/hanzoai/deploy` after the repo was
renamed. A Go module path is not cosmetic — it is the resolvable location. So
`go get github.com/hanzoai/cd` could not work, and anything importing this
module had to spell a name that no longer exists at that address. The build log
of v3.6.0 is where it surfaced: the compiler was still emitting
github.com/hanzoai/deploy/cmd/cd/commands.

3541 import paths across 690 .go, 19 .proto, 4 .yaml, 3 .sh, 2 .mod, Procfile
and Makefile. Plus the 62 ghcr.io/hanzoai/deploy image references, moved to
ghcr.io/hanzoai/cd to match hanzo.yml's build target — a distinct string from
the module path, replaced separately so neither could clobber the other.

go mod tidy clean, CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build ./... clean.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-28 21:16:24 -07:00

250 lines
7.7 KiB
Go

package commands
import (
"bytes"
"fmt"
"os"
"os/exec"
"runtime/debug"
"syscall"
"github.com/hanzoai/cd/common"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
"sigs.k8s.io/yaml"
cmdutil "github.com/hanzoai/cd/cmd/util"
"github.com/hanzoai/cd/util/cli"
"github.com/hanzoai/cd/util/dex"
"github.com/hanzoai/cd/util/env"
"github.com/hanzoai/cd/util/errors"
"github.com/hanzoai/cd/util/settings"
"github.com/hanzoai/cd/util/tls"
)
func NewCommand() *cobra.Command {
command := &cobra.Command{
Use: common.CommandDex,
Short: "cd-dex tools used by Hanzo CD",
Long: "cd-dex has internal utility tools used by Hanzo CD",
DisableAutoGenTag: true,
Run: func(c *cobra.Command, args []string) {
c.HelpFunc()(c, args)
},
}
command.AddCommand(NewRunDexCommand())
command.AddCommand(NewGenDexConfigCommand())
return command
}
func NewRunDexCommand() *cobra.Command {
var (
clientConfig clientcmd.ClientConfig
disableTLS bool
)
command := cobra.Command{
Use: "rundex",
Short: "Runs dex generating a config using settings from the Hanzo CD configmap and secret",
RunE: func(c *cobra.Command, _ []string) error {
ctx := c.Context()
vers := common.GetVersion()
namespace, _, err := clientConfig.Namespace()
errors.CheckError(err)
vers.LogStartupInfo(
"Hanzo CD Dex Server",
map[string]any{
"namespace": namespace,
},
)
cli.SetLogFormat(cmdutil.LogFormat)
cli.SetLogLevel(cmdutil.LogLevel)
// Recover from panic and log the error using the configured logger instead of the default.
defer func() {
if r := recover(); r != nil {
log.WithField("trace", string(debug.Stack())).Fatal("Recovered from panic: ", r)
}
}()
_, err = exec.LookPath("dex")
errors.CheckError(err)
config, err := clientConfig.ClientConfig()
errors.CheckError(err)
config.UserAgent = fmt.Sprintf("cd-dex/%s (%s)", vers.Version, vers.Platform)
kubeClientset := kubernetes.NewForConfigOrDie(config)
if !disableTLS {
config, err := tls.CreateServerTLSConfig("/tls/tls.crt", "/tls/tls.key", []string{"localhost", "dexserver"}, "")
if err != nil {
log.Fatalf("could not create TLS config: %v", err)
}
certPem, keyPem := tls.EncodeX509KeyPair(config.Certificates[0])
err = os.WriteFile("/tmp/tls.crt", certPem, 0o600)
if err != nil {
log.Fatalf("could not write TLS certificate: %v", err)
}
err = os.WriteFile("/tmp/tls.key", keyPem, 0o600)
if err != nil {
log.Fatalf("could not write TLS key: %v", err)
}
}
settingsMgr := settings.NewSettingsManager(ctx, kubeClientset, namespace)
prevSettings, err := settingsMgr.GetSettings()
errors.CheckError(err)
updateCh := make(chan *settings.ArgoCDSettings, 1)
settingsMgr.Subscribe(updateCh)
for {
var cmd *exec.Cmd
dexCfgBytes, err := dex.GenerateDexConfigYAML(prevSettings, disableTLS)
errors.CheckError(err)
if len(dexCfgBytes) == 0 {
log.Infof("dex is not configured")
} else {
err = os.WriteFile("/tmp/dex.yaml", dexCfgBytes, 0o644)
errors.CheckError(err)
log.Debug(redactor(string(dexCfgBytes)))
cmd = exec.CommandContext(ctx, "dex", "serve", "/tmp/dex.yaml")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Start()
errors.CheckError(err)
}
// loop until the dex config changes
for {
newSettings := <-updateCh
newDexCfgBytes, err := dex.GenerateDexConfigYAML(newSettings, disableTLS)
errors.CheckError(err)
if !bytes.Equal(newDexCfgBytes, dexCfgBytes) {
prevSettings = newSettings
log.Infof("dex config modified. restarting dex")
if cmd != nil && cmd.Process != nil {
err = cmd.Process.Signal(syscall.SIGTERM)
errors.CheckError(err)
_, err = cmd.Process.Wait()
errors.CheckError(err)
}
break
}
log.Infof("dex config unmodified")
}
}
},
}
clientConfig = cli.AddKubectlFlagsToCmd(&command)
command.Flags().StringVar(&cmdutil.LogFormat, "logformat", env.StringFromEnv("CD_DEX_SERVER_LOGFORMAT", "json"), "Set the logging format. One of: json|text")
command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", env.StringFromEnv("CD_DEX_SERVER_LOGLEVEL", "info"), "Set the logging level. One of: debug|info|warn|error")
command.Flags().BoolVar(&disableTLS, "disable-tls", env.ParseBoolFromEnv("CD_DEX_SERVER_DISABLE_TLS", false), "Disable TLS on the HTTP endpoint")
return &command
}
func NewGenDexConfigCommand() *cobra.Command {
var (
clientConfig clientcmd.ClientConfig
out string
disableTLS bool
)
command := cobra.Command{
Use: "gendexcfg",
Short: "Generates a dex config from Hanzo CD settings",
RunE: func(c *cobra.Command, _ []string) error {
ctx := c.Context()
cli.SetLogFormat(cmdutil.LogFormat)
cli.SetLogLevel(cmdutil.LogLevel)
config, err := clientConfig.ClientConfig()
errors.CheckError(err)
namespace, _, err := clientConfig.Namespace()
errors.CheckError(err)
kubeClientset := kubernetes.NewForConfigOrDie(config)
settingsMgr := settings.NewSettingsManager(ctx, kubeClientset, namespace)
settings, err := settingsMgr.GetSettings()
errors.CheckError(err)
dexCfgBytes, err := dex.GenerateDexConfigYAML(settings, disableTLS)
errors.CheckError(err)
if len(dexCfgBytes) == 0 {
log.Infof("dex is not configured")
return nil
}
if out == "" {
dexCfg := make(map[string]any)
err := yaml.Unmarshal(dexCfgBytes, &dexCfg)
errors.CheckError(err)
if staticClientsInterface, ok := dexCfg["staticClients"]; ok {
if staticClients, ok := staticClientsInterface.([]any); ok {
for i := range staticClients {
staticClient := staticClients[i]
if mappings, ok := staticClient.(map[string]any); ok {
for key := range mappings {
if key == "secret" {
mappings[key] = "******"
}
}
staticClients[i] = mappings
}
}
dexCfg["staticClients"] = staticClients
}
}
errors.CheckError(err)
maskedDexCfgBytes, err := yaml.Marshal(dexCfg)
errors.CheckError(err)
fmt.Print(string(maskedDexCfgBytes))
} else {
err = os.WriteFile(out, dexCfgBytes, 0o644)
errors.CheckError(err)
}
return nil
},
}
clientConfig = cli.AddKubectlFlagsToCmd(&command)
command.Flags().StringVar(&cmdutil.LogFormat, "logformat", env.StringFromEnv("CD_DEX_SERVER_LOGFORMAT", "json"), "Set the logging format. One of: json|text")
command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", env.StringFromEnv("CD_DEX_SERVER_LOGLEVEL", "info"), "Set the logging level. One of: debug|info|warn|error")
command.Flags().StringVarP(&out, "out", "o", "", "Output to the specified file instead of stdout")
command.Flags().BoolVar(&disableTLS, "disable-tls", env.ParseBoolFromEnv("CD_DEX_SERVER_DISABLE_TLS", false), "Disable TLS on the HTTP endpoint")
return &command
}
func iterateStringFields(obj any, callback func(name string, val string) string) {
switch obj := obj.(type) {
case map[string]any:
for field, val := range obj {
if strVal, ok := val.(string); ok {
obj[field] = callback(field, strVal)
} else {
iterateStringFields(val, callback)
}
}
case []any:
for i := range obj {
iterateStringFields(obj[i], callback)
}
}
}
func redactor(dirtyString string) string {
config := make(map[string]any)
err := yaml.Unmarshal([]byte(dirtyString), &config)
errors.CheckError(err)
iterateStringFields(config, func(name string, val string) string {
if name == "clientSecret" || name == "secret" || name == "bindPW" {
return "********"
}
return val
})
data, err := yaml.Marshal(config)
errors.CheckError(err)
return string(data)
}