]> go.fuhry.dev Git - runtime.git/commitdiff
[ldap] port ldap_health_exporter to builtin log and urfave/cli
authorDan Fuhry <dan@fuhry.com>
Mon, 19 Oct 2026 20:41:05 +0000 (16:41 -0400)
committerDan Fuhry <dan@fuhry.com>
Mon, 19 Oct 2026 21:28:53 +0000 (17:28 -0400)
cmd/ldap_health_exporter/BUILD.bazel
cmd/ldap_health_exporter/main.go
ldap/BUILD.bazel
ldap/server.go

index 519c1ddafd6f5016553dab70ee7c0cd6bb99e414..b55881657e85a38768d2cf9f3dfc171ddbf6cff5 100644 (file)
@@ -9,9 +9,10 @@ go_multi_library(
     deps = [
         "//ldap",
         "//mtls",
+        "//utils/context",
+        "//utils/log",
         "@com_github_sirupsen_logrus//:logrus",
-        "@com_github_urfave_cli_v2//:cli",
-        "@com_github_urfave_cli_v2//altsrc",
+        "@com_github_urfave_cli_v3//:cli",
         "@org_golang_x_sync//errgroup",
     ],
 )
index 4d3da0f4245bbe145ed872e2a1353753b65940dd..5d152c500adc724cc74b4bc5f157b8e251dcfa87 100644 (file)
@@ -1,7 +1,7 @@
 package main
 
 import (
-       "context"
+       "flag"
        "os"
        "os/signal"
        "syscall"
@@ -9,10 +9,13 @@ import (
 
        exporter "go.fuhry.dev/runtime/ldap"
        "go.fuhry.dev/runtime/mtls"
+       "go.fuhry.dev/runtime/utils/context"
+       "go.fuhry.dev/runtime/utils/log"
 
-       log "github.com/sirupsen/logrus"
-       "github.com/urfave/cli/v2"
-       "github.com/urfave/cli/v2/altsrc"
+       "github.com/sirupsen/logrus"
+
+       // "github.com/urfave/cli-altsrc/v3"
+       "github.com/urfave/cli/v3"
        "golang.org/x/sync/errgroup"
 )
 
@@ -22,7 +25,6 @@ const (
        ldapAddr          = "ldapAddr"
        ldapUser          = "ldapUser"
        ldapPass          = "ldapPass"
-       mtlsId            = "mtlsId"
        interval          = "interval"
        metrics           = "metrPath"
        jsonLog           = "jsonLog"
@@ -32,104 +34,123 @@ const (
 )
 
 func main() {
+       ctx, cancel := context.Interruptible()
+       defer cancel()
+
        flags := []cli.Flag{
-               altsrc.NewStringFlag(&cli.StringFlag{
+               &cli.StringFlag{
                        Name:    promAddr,
                        Value:   ":9330",
                        Usage:   "Bind address for Prometheus HTTP metrics server",
-                       EnvVars: []string{"PROM_ADDR"},
-               }),
-               altsrc.NewStringFlag(&cli.StringFlag{
+                       Sources: cli.EnvVars("PROM_ADDR"),
+               },
+               &cli.StringFlag{
                        Name:    metrics,
                        Value:   "/metrics",
                        Usage:   "Path on which to expose Prometheus metrics",
-                       EnvVars: []string{"METRICS_PATH"},
-               }),
-               altsrc.NewStringFlag(&cli.StringFlag{
+                       Sources: cli.EnvVars("METRICS_PATH"),
+               },
+               &cli.StringFlag{
                        Name:    ldapNet,
                        Value:   "tcp",
                        Usage:   "Network of OpenLDAP server",
-                       EnvVars: []string{"LDAP_NET"},
-               }),
-               altsrc.NewStringFlag(&cli.StringFlag{
+                       Sources: cli.EnvVars("LDAP_NET"),
+               },
+               &cli.StringFlag{
                        Name:    ldapAddr,
                        Value:   "localhost:389",
                        Usage:   "Address and port of OpenLDAP server",
-                       EnvVars: []string{"LDAP_ADDR"},
-               }),
-               altsrc.NewStringFlag(&cli.StringFlag{
+                       Sources: cli.EnvVars("LDAP_ADDR"),
+               },
+               &cli.StringFlag{
                        Name:    ldapUser,
                        Usage:   "OpenLDAP bind username (optional)",
-                       EnvVars: []string{"LDAP_USER"},
-               }),
-               altsrc.NewStringFlag(&cli.StringFlag{
+                       Sources: cli.EnvVars("LDAP_USER"),
+               },
+               &cli.StringFlag{
                        Name:    ldapPass,
                        Usage:   "OpenLDAP bind password (optional)",
-                       EnvVars: []string{"LDAP_PASS"},
-               }),
-               altsrc.NewStringFlag(&cli.StringFlag{
-                       Name:    mtlsId,
-                       Usage:   "mTLS identity from /etc/ssl/mtls",
-                       EnvVars: []string{"MTLS_ID"},
-               }),
-               altsrc.NewDurationFlag(&cli.DurationFlag{
+                       Sources: cli.EnvVars("LDAP_PASS"),
+               },
+               &cli.DurationFlag{
                        Name:    interval,
                        Value:   30 * time.Second,
                        Usage:   "Scrape interval",
-                       EnvVars: []string{"INTERVAL"},
-               }),
-               altsrc.NewStringFlag(&cli.StringFlag{
+                       Sources: cli.EnvVars("INTERVAL"),
+               },
+               &cli.StringFlag{
                        Name:    webCfgFile,
                        Usage:   "Prometheus metrics web config `FILE` (optional)",
-                       EnvVars: []string{"WEB_CFG_FILE"},
-               }),
-               altsrc.NewBoolFlag(&cli.BoolFlag{
+                       Sources: cli.EnvVars("WEB_CFG_FILE"),
+               },
+               &cli.BoolFlag{
                        Name:    jsonLog,
                        Value:   false,
                        Usage:   "Output logs in JSON format",
-                       EnvVars: []string{"JSON_LOG"},
-               }),
-               altsrc.NewStringSliceFlag(&cli.StringSliceFlag{
+                       Sources: cli.EnvVars("JSON_LOG"),
+               },
+               &cli.StringSliceFlag{
                        Name:  replicationObject,
                        Usage: "Object to watch replication upon",
-               }),
-               &cli.StringFlag{
-                       Name:  config,
-                       Usage: "Optional configuration from a `YAML_FILE`",
                },
+               // &cli.StringFlag{
+               //      Name:  config,
+               //      Usage: "Optional configuration from a `YAML_FILE`",
+               // },
        }
-       app := &cli.App{
-               Name:            "openldap_exporter",
-               Usage:           "Export OpenLDAP metrics to Prometheus",
-               Before:          altsrc.InitInputSourceWithContext(flags, optionalYamlSourceFunc(config)),
+       app := &cli.Command{
+               Name:  "openldap_exporter",
+               Usage: "Export OpenLDAP metrics to Prometheus",
+               // Before:          altsrc.InitInputSourceWithContext(flags, optionalYamlSourceFunc(config)),
                Version:         exporter.GetVersion(),
                HideHelpCommand: true,
                Flags:           flags,
                Action:          runMain,
        }
-       if err := app.Run(os.Args); err != nil {
-               log.WithError(err).Fatal("service failed")
-       }
-       log.Info("service stopped")
-}
-
-func optionalYamlSourceFunc(flagFileName string) func(context *cli.Context) (altsrc.InputSourceContext, error) {
-       return func(c *cli.Context) (altsrc.InputSourceContext, error) {
-               filePath := c.String(flagFileName)
-               if filePath != "" {
-                       return altsrc.NewYamlSourceFromFile(filePath)
+       flag.CommandLine.Parse([]string{})
+       flag.CommandLine.VisitAll(func(f *flag.Flag) {
+               val, ok := f.Value.(cli.Value)
+               if !ok {
+                       log.Default().Errorf("cannot convert flag %q (%s) of type %T to cli.Value", f.Name, f.Usage, f.Value)
+                       app.Flags = append(app.Flags, &cli.StringFlag{
+                               Name:  f.Name,
+                               Usage: f.Usage,
+                               Value: "",
+                               Action: func(ctx context.Context, c *cli.Command, s string) error {
+                                       return f.Value.Set(s)
+                               },
+                       })
+                       return
                }
-               return &altsrc.MapInputSource{}, nil
+               app.Flags = append(app.Flags, &cli.GenericFlag{
+                       Name:  f.Name,
+                       Usage: f.Usage,
+                       Value: val,
+               })
+       })
+       if err := app.Run(ctx, os.Args); err != nil {
+               logrus.WithError(err).Fatal("service failed")
        }
+       logrus.Info("service stopped")
 }
 
-func runMain(c *cli.Context) error {
+// func optionalYamlSourceFunc(flagFileName string) func(context *cli.Context) (altsrc.InputSourceContext, error) {
+//     return func(c *cli.Context) (altsrc.InputSourceContext, error) {
+//             filePath := c.String(flagFileName)
+//             if filePath != "" {
+//                     return altsrc.NewYamlSourceFromFile(filePath)
+//             }
+//             return &altsrc.MapInputSource{}, nil
+//     }
+// }
+
+func runMain(ctx context.Context, c *cli.Command) error {
        if c.Bool(jsonLog) {
-               log.SetFormatter(&log.JSONFormatter{})
+               logrus.SetFormatter(&logrus.JSONFormatter{})
        } else {
-               log.SetFormatter(&log.TextFormatter{})
+               logrus.SetFormatter(&logrus.TextFormatter{})
        }
-       log.Info("service starting")
+       logrus.Info("service starting")
 
        server := exporter.NewMetricsServer(
                c.String(promAddr),
@@ -142,7 +163,7 @@ func runMain(c *cli.Context) error {
                Addr: c.String(ldapAddr),
                User: c.String(ldapUser),
                Pass: c.String(ldapPass),
-               Mtls: mtls.NewServiceIdentity(c.String(mtlsId)),
+               Mtls: mtls.NewServiceIdentity(c.String("mtls.id")),
                Tick: c.Duration(interval),
                Sync: c.StringSlice(replicationObject),
        }
@@ -167,7 +188,7 @@ func runMain(c *cli.Context) error {
                signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
                select {
                case <-signalChan:
-                       log.Info("shutdown received")
+                       logrus.Info("shutdown received")
                        return nil
                case <-ctx.Done():
                        return nil
index 8b46183742fbb5baaba2078c235d6e5afa121a70..42c767c5c3633718620bdadd178aac82e040404f 100644 (file)
@@ -11,7 +11,7 @@ go_multi_library(
     deps = [
         "//mtls",
         "//mtls/certutil",
-        "@com_github_go_kit_log//:log",
+        "//utils/log",
         "@com_github_go_ldap_ldap_v3//:ldap",
         "@com_github_prometheus_client_golang//prometheus",
         "@com_github_prometheus_client_golang//prometheus/promhttp",
index 1b8c467f989f5658561969939639ba8baa296bf2..5eb5edc1e312008fed4a5ceb76b96faac759db07 100644 (file)
@@ -4,17 +4,20 @@ import (
        "context"
        "errors"
        "fmt"
+       "log/slog"
        "net/http"
        "time"
 
-       kitlog "github.com/go-kit/log"
        "github.com/prometheus/client_golang/prometheus/promhttp"
        "github.com/prometheus/exporter-toolkit/web"
-       log "github.com/sirupsen/logrus"
+
+       "go.fuhry.dev/runtime/utils/log"
 )
 
-var commit string
-var tag string
+var (
+       commit string
+       tag    string
+)
 
 func GetVersion() string {
        return fmt.Sprintf("%s (%s)", tag, commit)
@@ -23,7 +26,7 @@ func GetVersion() string {
 type Server struct {
        server     *http.Server
        flagConfig *web.FlagConfig
-       logger     log.FieldLogger
+       logger     log.Logger
 }
 
 func NewMetricsServer(bindAddr, metricsPath, tlsConfigPath string) *Server {
@@ -38,7 +41,7 @@ func NewMetricsServer(bindAddr, metricsPath, tlsConfigPath string) *Server {
                        WebSystemdSocket:   &systemdSocket,
                        WebConfigFile:      &tlsConfigPath,
                },
-               logger: log.WithField("component", "server"),
+               logger: log.Default().WithAttrs([]slog.Attr{{"component", slog.StringValue("server")}}).(log.Logger),
        }
 }
 
@@ -52,8 +55,9 @@ func showVersion(w http.ResponseWriter, r *http.Request) {
 }
 
 func (s *Server) Start() error {
-       s.logger.WithField("addr", s.server.Addr).Info("starting http listener")
-       err := web.ListenAndServe(s.server, s.flagConfig, kitlog.LoggerFunc(s.adaptor))
+       logger := s.logger.WithAttrs([]slog.Attr{{"addr", slog.StringValue(s.server.Addr)}}).(log.Logger)
+       logger.Info("starting http listener")
+       err := web.ListenAndServe(s.server, s.flagConfig, slog.New(s.logger))
        if errors.Is(err, http.ErrServerClosed) {
                return nil
        }
@@ -65,39 +69,3 @@ func (s *Server) Stop() {
        s.server.Shutdown(ctx)
        cancel()
 }
-
-func (s *Server) adaptor(kvs ...interface{}) error {
-       if len(kvs) == 0 {
-               return nil
-       }
-       if len(kvs)%2 != 0 {
-               kvs = append(kvs, nil)
-       }
-       fields := log.Fields{}
-       for i := 0; i < len(kvs); i += 2 {
-               key := fmt.Sprint(kvs[i])
-               fields[key] = kvs[i+1]
-       }
-       var msg string
-       if val, ok := fields["msg"]; ok {
-               delete(fields, "msg")
-               msg = fmt.Sprint(val)
-       }
-       var level string
-       if val, ok := fields["level"]; ok {
-               delete(fields, "level")
-               level = fmt.Sprint(val)
-       }
-       ll := s.logger.WithFields(fields)
-       switch level {
-       case "error":
-               ll.Error(msg)
-       case "warn":
-               ll.Warn(msg)
-       case "debug":
-               ll.Debug(msg)
-       default:
-               ll.Info(msg)
-       }
-       return nil
-}