Compare commits
49 Commits
Author | SHA1 | Date | |
---|---|---|---|
62a7605869 | |||
0996167865 | |||
87344dd601 | |||
39e6611602 | |||
355d246010 | |||
e5836a8b97 | |||
f6d160a7f8 | |||
9e2d6fb74e | |||
0df1154bb5 | |||
b926a85737 | |||
68bd4d8904 | |||
152be9d956 | |||
ab94adb925 | |||
ddb56b3566 | |||
4367ef9c29 | |||
d6a77f4003 | |||
3f3fb29bc9 | |||
8e898d9c59 | |||
1ecd5684e6 | |||
59580b50ba | |||
9a86b2aaf5 | |||
bcf7232154 | |||
9934dc8e36 | |||
a458dcb226 | |||
4ed44c72a4 | |||
8a8ff93996 | |||
1e8785bd32 | |||
5aeaa97c6f | |||
df2285d355 | |||
232a6f98df | |||
4f6b09200f | |||
7085202154 | |||
5c7ae6ab8a | |||
608f05d395 | |||
b5420f9dbd | |||
537ee53384 | |||
0a92ad768e | |||
61d73f7be3 | |||
2bff6d8ef3 | |||
62e28b71f1 | |||
5b033a1f0b | |||
a4447c97f6 | |||
9d136b2502 | |||
7420ad12f4 | |||
cf33a99566 | |||
c2b303bec9 | |||
6fd0728668 | |||
09f963b267 | |||
ad63071f60 |
10
LICENSE
Normal file
10
LICENSE
Normal file
@ -0,0 +1,10 @@
|
||||
Copyright (c) 2012-2024 Develooper LLC
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted (subject to the limitations in the disclaimer below) provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Develooper LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
@ -30,7 +30,6 @@ func CAPool() (*x509.CertPool, error) {
|
||||
// GetCertman sets up certman for the specified cert / key pair. It is
|
||||
// used in the monitor-api and (for now) in the client
|
||||
func GetCertman(certFile, keyFile string) (*certman.CertMan, error) {
|
||||
|
||||
cm, err := certman.New(certFile, keyFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
94
config/config.go
Normal file
94
config/config.go
Normal file
@ -0,0 +1,94 @@
|
||||
// Package config provides NTP Pool specific
|
||||
// configuration tools.
|
||||
package config
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"go.ntppool.org/common/logger"
|
||||
)
|
||||
|
||||
//go:generate go tool github.com/masaushi/accessory -type Config
|
||||
|
||||
type Config struct {
|
||||
deploymentMode string `accessor:"getter"`
|
||||
|
||||
manageHostname string `accessor:"getter"`
|
||||
manageTLS bool
|
||||
|
||||
webHostname string `accessor:"getter"`
|
||||
webHostnames []string
|
||||
webTLS bool
|
||||
|
||||
valid bool `accessor:"getter"`
|
||||
}
|
||||
|
||||
func New() *Config {
|
||||
c := Config{}
|
||||
c.deploymentMode = os.Getenv("deployment_mode")
|
||||
c.manageHostname = os.Getenv("manage_hostname")
|
||||
|
||||
c.webHostnames = strings.Split(os.Getenv("web_hostname"), ",")
|
||||
for i, h := range c.webHostnames {
|
||||
c.webHostnames[i] = strings.TrimSpace(h)
|
||||
}
|
||||
if len(c.webHostnames) > 0 {
|
||||
c.webHostname = c.webHostnames[0]
|
||||
c.valid = true
|
||||
}
|
||||
|
||||
c.manageTLS = parseBool(os.Getenv("manage_tls"))
|
||||
c.webTLS = parseBool(os.Getenv("web_tls"))
|
||||
|
||||
return &c
|
||||
}
|
||||
|
||||
func (c *Config) WebURL(path string, query *url.Values) string {
|
||||
return baseURL(c.webHostname, c.webTLS, path, query)
|
||||
}
|
||||
|
||||
func (c *Config) ManageURL(path string, query *url.Values) string {
|
||||
return baseURL(c.manageHostname, c.webTLS, path, query)
|
||||
}
|
||||
|
||||
func baseURL(host string, tls bool, path string, query *url.Values) string {
|
||||
uri := url.URL{}
|
||||
uri.Host = host
|
||||
if tls {
|
||||
uri.Scheme = "https"
|
||||
} else {
|
||||
uri.Scheme = "http"
|
||||
}
|
||||
uri.Path = path
|
||||
if query != nil {
|
||||
uri.RawQuery = query.Encode()
|
||||
}
|
||||
|
||||
return uri.String()
|
||||
}
|
||||
|
||||
func parseBool(s string) bool {
|
||||
switch strings.ToLower(s) {
|
||||
case "yes":
|
||||
return true
|
||||
case "y":
|
||||
return true
|
||||
case "no":
|
||||
return false
|
||||
case "n":
|
||||
return false
|
||||
case "":
|
||||
return false
|
||||
}
|
||||
|
||||
t, err := strconv.ParseBool(s)
|
||||
if err != nil {
|
||||
logger.Setup().Error("could not parse bool", "string", s, "err", err)
|
||||
return false
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
31
config/config_accessor.go
Normal file
31
config/config_accessor.go
Normal file
@ -0,0 +1,31 @@
|
||||
// Code generated by accessory; DO NOT EDIT.
|
||||
|
||||
package config
|
||||
|
||||
func (c *Config) DeploymentMode() string {
|
||||
if c == nil {
|
||||
return ""
|
||||
}
|
||||
return c.deploymentMode
|
||||
}
|
||||
|
||||
func (c *Config) ManageHostname() string {
|
||||
if c == nil {
|
||||
return ""
|
||||
}
|
||||
return c.manageHostname
|
||||
}
|
||||
|
||||
func (c *Config) WebHostname() string {
|
||||
if c == nil {
|
||||
return ""
|
||||
}
|
||||
return c.webHostname
|
||||
}
|
||||
|
||||
func (c *Config) Valid() bool {
|
||||
if c == nil {
|
||||
return false
|
||||
}
|
||||
return c.valid
|
||||
}
|
24
config/config_test.go
Normal file
24
config/config_test.go
Normal file
@ -0,0 +1,24 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBaseURL(t *testing.T) {
|
||||
os.Setenv("web_hostname", "www.ntp.dev, web.ntppool.dev")
|
||||
os.Setenv("web_tls", "yes")
|
||||
|
||||
c := New()
|
||||
if !c.Valid() {
|
||||
t.Fatalf("config not valid")
|
||||
}
|
||||
|
||||
q := url.Values{}
|
||||
q.Set("foo", "bar")
|
||||
u := c.WebURL("/foo", &q)
|
||||
if u != "https://www.ntp.dev/foo?foo=bar" {
|
||||
t.Fatalf("unexpected WebURL: %s", u)
|
||||
}
|
||||
}
|
18
config/depenv/context.go
Normal file
18
config/depenv/context.go
Normal file
@ -0,0 +1,18 @@
|
||||
package depenv
|
||||
|
||||
import "context"
|
||||
|
||||
type contextKey struct{}
|
||||
|
||||
// NewContext adds the deployment environment to the context
|
||||
func NewContext(ctx context.Context, d DeploymentEnvironment) context.Context {
|
||||
return context.WithValue(ctx, contextKey{}, d)
|
||||
}
|
||||
|
||||
// FromContext retrieves the deployment environment from the context
|
||||
func FromContext(ctx context.Context) DeploymentEnvironment {
|
||||
if d, ok := ctx.Value(contextKey{}).(DeploymentEnvironment); ok {
|
||||
return d
|
||||
}
|
||||
return DeployUndefined
|
||||
}
|
87
config/depenv/depenv.go
Normal file
87
config/depenv/depenv.go
Normal file
@ -0,0 +1,87 @@
|
||||
package depenv
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
var manageServers = map[DeploymentEnvironment]string{
|
||||
DeployDevel: "https://manage.askdev.grundclock.com",
|
||||
DeployTest: "https://manage.beta.grundclock.com",
|
||||
DeployProd: "https://manage.ntppool.org",
|
||||
}
|
||||
|
||||
var apiServers = map[DeploymentEnvironment]string{
|
||||
DeployDevel: "https://dev-api.ntppool.dev",
|
||||
DeployTest: "https://beta-api.ntppool.dev",
|
||||
DeployProd: "https://api.ntppool.dev",
|
||||
}
|
||||
|
||||
// var validationServers = map[DeploymentEnvironment]string{
|
||||
// DeployDevel: "https://v.ntp.dev/d/",
|
||||
// DeployTest: "https://v.ntp.dev/b/",
|
||||
// DeployProd: "https://v.ntp.dev/p/",
|
||||
// }
|
||||
|
||||
const (
|
||||
DeployUndefined DeploymentEnvironment = iota
|
||||
DeployDevel
|
||||
DeployTest
|
||||
DeployProd
|
||||
)
|
||||
|
||||
type DeploymentEnvironment uint8
|
||||
|
||||
func DeploymentEnvironmentFromString(s string) DeploymentEnvironment {
|
||||
switch s {
|
||||
case "devel", "dev":
|
||||
return DeployDevel
|
||||
case "test", "beta":
|
||||
return DeployTest
|
||||
case "prod":
|
||||
return DeployProd
|
||||
default:
|
||||
return DeployUndefined
|
||||
}
|
||||
}
|
||||
|
||||
func (d DeploymentEnvironment) String() string {
|
||||
switch d {
|
||||
case DeployProd:
|
||||
return "prod"
|
||||
case DeployTest:
|
||||
return "test"
|
||||
case DeployDevel:
|
||||
return "devel"
|
||||
default:
|
||||
panic("invalid DeploymentEnvironment")
|
||||
}
|
||||
}
|
||||
|
||||
func (d DeploymentEnvironment) APIHost() string {
|
||||
if apiHost := os.Getenv("API_HOST"); apiHost != "" {
|
||||
return apiHost
|
||||
}
|
||||
return apiServers[d]
|
||||
}
|
||||
|
||||
func (d DeploymentEnvironment) ManageURL(path string) string {
|
||||
return manageServers[d] + path
|
||||
}
|
||||
|
||||
func (d DeploymentEnvironment) MonitorDomain() string {
|
||||
return d.String() + ".mon.ntppool.dev"
|
||||
}
|
||||
|
||||
func (d *DeploymentEnvironment) UnmarshalText(text []byte) error {
|
||||
s := string(text)
|
||||
if s == "" {
|
||||
return nil
|
||||
}
|
||||
env := DeploymentEnvironmentFromString(s)
|
||||
if env == DeployUndefined {
|
||||
return fmt.Errorf("invalid deployment environment: %s", s)
|
||||
}
|
||||
*d = env
|
||||
return nil
|
||||
}
|
40
config/depenv/monitor_names.go
Normal file
40
config/depenv/monitor_names.go
Normal file
@ -0,0 +1,40 @@
|
||||
package depenv
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var monitorApiServers = map[DeploymentEnvironment]string{
|
||||
DeployDevel: "https://api.devel.mon.ntppool.dev",
|
||||
DeployTest: "https://api.test.mon.ntppool.dev",
|
||||
DeployProd: "https://api.mon.ntppool.dev",
|
||||
}
|
||||
|
||||
func (d DeploymentEnvironment) MonitorAPIHost() string {
|
||||
return monitorApiServers[d]
|
||||
}
|
||||
|
||||
func GetDeploymentEnvironmentFromName(clientName string) (DeploymentEnvironment, error) {
|
||||
clientName = strings.ToLower(clientName)
|
||||
|
||||
if !strings.HasSuffix(clientName, ".mon.ntppool.dev") {
|
||||
return DeployUndefined, fmt.Errorf("invalid client name %s", clientName)
|
||||
}
|
||||
|
||||
if clientName == "api.mon.ntppool.dev" {
|
||||
return DeployProd, nil
|
||||
}
|
||||
|
||||
prefix := clientName[:strings.Index(clientName, ".mon.ntppool.dev")]
|
||||
parts := strings.Split(prefix, ".")
|
||||
if len(parts) != 2 {
|
||||
return DeployUndefined, fmt.Errorf("invalid client name %s", clientName)
|
||||
}
|
||||
|
||||
if d := DeploymentEnvironmentFromString(parts[1]); d != DeployUndefined {
|
||||
return d, nil
|
||||
}
|
||||
|
||||
return DeployUndefined, fmt.Errorf("invalid client name %s (unknown environment %s)", clientName, parts[1])
|
||||
}
|
170
ekko/ekko.go
Normal file
170
ekko/ekko.go
Normal file
@ -0,0 +1,170 @@
|
||||
package ekko
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/labstack/echo-contrib/echoprometheus"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/echo/v4/middleware"
|
||||
slogecho "github.com/samber/slog-echo"
|
||||
"go.ntppool.org/common/logger"
|
||||
"go.ntppool.org/common/version"
|
||||
"go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"golang.org/x/net/http2"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
func New(name string, options ...func(*Ekko)) (*Ekko, error) {
|
||||
ek := &Ekko{
|
||||
writeTimeout: 60 * time.Second,
|
||||
readHeaderTimeout: 30 * time.Second,
|
||||
}
|
||||
|
||||
for _, o := range options {
|
||||
o(ek)
|
||||
}
|
||||
return ek, nil
|
||||
}
|
||||
|
||||
// Setup Echo; only intended for testing
|
||||
func (ek *Ekko) SetupEcho(ctx context.Context) (*echo.Echo, error) {
|
||||
return ek.setup(ctx)
|
||||
}
|
||||
|
||||
// Setup Echo and start the server. Will return if the http server
|
||||
// returns or the context is done.
|
||||
func (ek *Ekko) Start(ctx context.Context) error {
|
||||
log := logger.Setup()
|
||||
|
||||
e, err := ek.setup(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
g, ctx := errgroup.WithContext(ctx)
|
||||
g.Go(func() error {
|
||||
e.Server.Addr = fmt.Sprintf(":%d", ek.port)
|
||||
log.Info("server starting", "port", ek.port)
|
||||
// err := e.Server.ListenAndServe()
|
||||
err := e.StartH2CServer(e.Server.Addr, &http2.Server{})
|
||||
if err == http.ErrServerClosed {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
})
|
||||
|
||||
g.Go(func() error {
|
||||
<-ctx.Done()
|
||||
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
return e.Shutdown(shutdownCtx)
|
||||
})
|
||||
|
||||
return g.Wait()
|
||||
}
|
||||
|
||||
func (ek *Ekko) setup(ctx context.Context) (*echo.Echo, error) {
|
||||
log := logger.Setup()
|
||||
|
||||
e := echo.New()
|
||||
|
||||
e.Server.ReadHeaderTimeout = ek.readHeaderTimeout
|
||||
e.Server.WriteTimeout = ek.writeTimeout
|
||||
|
||||
e.Server.BaseContext = func(_ net.Listener) context.Context {
|
||||
return ctx
|
||||
}
|
||||
|
||||
trustOptions := []echo.TrustOption{
|
||||
echo.TrustLoopback(true),
|
||||
echo.TrustLinkLocal(false),
|
||||
echo.TrustPrivateNet(true),
|
||||
}
|
||||
e.IPExtractor = echo.ExtractIPFromXFFHeader(trustOptions...)
|
||||
|
||||
if ek.otelmiddleware == nil {
|
||||
e.Use(otelecho.Middleware(ek.name))
|
||||
} else {
|
||||
e.Use(ek.otelmiddleware)
|
||||
}
|
||||
|
||||
e.Use(middleware.RecoverWithConfig(middleware.RecoverConfig{
|
||||
LogErrorFunc: func(c echo.Context, err error, stack []byte) error {
|
||||
log.ErrorContext(c.Request().Context(), err.Error(), "stack", string(stack))
|
||||
fmt.Println(string(stack))
|
||||
return err
|
||||
},
|
||||
}))
|
||||
|
||||
e.Use(slogecho.NewWithConfig(log,
|
||||
slogecho.Config{
|
||||
WithTraceID: false, // done by logger already
|
||||
Filters: ek.logFilters,
|
||||
},
|
||||
))
|
||||
|
||||
if ek.prom != nil {
|
||||
e.Use(echoprometheus.NewMiddlewareWithConfig(echoprometheus.MiddlewareConfig{
|
||||
Subsystem: ek.name,
|
||||
Registerer: ek.prom,
|
||||
}))
|
||||
}
|
||||
|
||||
if ek.gzipConfig != nil {
|
||||
e.Use(middleware.GzipWithConfig(*ek.gzipConfig))
|
||||
} else {
|
||||
e.Use(middleware.Gzip())
|
||||
}
|
||||
|
||||
secureConfig := middleware.DefaultSecureConfig
|
||||
// secureConfig.ContentSecurityPolicy = "default-src *"
|
||||
secureConfig.ContentSecurityPolicy = ""
|
||||
secureConfig.HSTSMaxAge = int(time.Hour * 168 * 30 / time.Second)
|
||||
secureConfig.HSTSPreloadEnabled = true
|
||||
|
||||
e.Use(middleware.SecureWithConfig(secureConfig))
|
||||
|
||||
e.Use(
|
||||
func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
request := c.Request()
|
||||
|
||||
span := trace.SpanFromContext(request.Context())
|
||||
if span.IsRecording() {
|
||||
|
||||
span.SetAttributes(attribute.String("http.real_ip", c.RealIP()))
|
||||
span.SetAttributes(attribute.String("url.path", c.Request().RequestURI))
|
||||
if q := c.QueryString(); len(q) > 0 {
|
||||
span.SetAttributes(attribute.String("url.query", q))
|
||||
}
|
||||
c.Response().Header().Set("Traceparent", span.SpanContext().TraceID().String())
|
||||
}
|
||||
return next(c)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
e.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
vinfo := version.VersionInfo()
|
||||
v := ek.name + "/" + vinfo.Version + "+" + vinfo.GitRevShort
|
||||
return func(c echo.Context) error {
|
||||
c.Response().Header().Set(echo.HeaderServer, v)
|
||||
return next(c)
|
||||
}
|
||||
})
|
||||
|
||||
if ek.routeFn != nil {
|
||||
err := ek.routeFn(e)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return e, nil
|
||||
}
|
73
ekko/options.go
Normal file
73
ekko/options.go
Normal file
@ -0,0 +1,73 @@
|
||||
package ekko
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/echo/v4/middleware"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
slogecho "github.com/samber/slog-echo"
|
||||
)
|
||||
|
||||
type Ekko struct {
|
||||
name string
|
||||
prom prometheus.Registerer
|
||||
port int
|
||||
routeFn func(e *echo.Echo) error
|
||||
logFilters []slogecho.Filter
|
||||
otelmiddleware echo.MiddlewareFunc
|
||||
gzipConfig *middleware.GzipConfig
|
||||
|
||||
writeTimeout time.Duration
|
||||
readHeaderTimeout time.Duration
|
||||
}
|
||||
|
||||
type RouteFn func(e *echo.Echo) error
|
||||
|
||||
func WithPort(port int) func(*Ekko) {
|
||||
return func(ek *Ekko) {
|
||||
ek.port = port
|
||||
}
|
||||
}
|
||||
|
||||
func WithPrometheus(reg prometheus.Registerer) func(*Ekko) {
|
||||
return func(ek *Ekko) {
|
||||
ek.prom = reg
|
||||
}
|
||||
}
|
||||
|
||||
func WithEchoSetup(rfn RouteFn) func(*Ekko) {
|
||||
return func(ek *Ekko) {
|
||||
ek.routeFn = rfn
|
||||
}
|
||||
}
|
||||
|
||||
func WithLogFilters(f []slogecho.Filter) func(*Ekko) {
|
||||
return func(ek *Ekko) {
|
||||
ek.logFilters = f
|
||||
}
|
||||
}
|
||||
|
||||
func WithOtelMiddleware(mw echo.MiddlewareFunc) func(*Ekko) {
|
||||
return func(ek *Ekko) {
|
||||
ek.otelmiddleware = mw
|
||||
}
|
||||
}
|
||||
|
||||
func WithWriteTimeout(t time.Duration) func(*Ekko) {
|
||||
return func(ek *Ekko) {
|
||||
ek.writeTimeout = t
|
||||
}
|
||||
}
|
||||
|
||||
func WithReadHeaderTimeout(t time.Duration) func(*Ekko) {
|
||||
return func(ek *Ekko) {
|
||||
ek.readHeaderTimeout = t
|
||||
}
|
||||
}
|
||||
|
||||
func WithGzipConfig(gzipConfig *middleware.GzipConfig) func(*Ekko) {
|
||||
return func(ek *Ekko) {
|
||||
ek.gzipConfig = gzipConfig
|
||||
}
|
||||
}
|
83
go.mod
83
go.mod
@ -1,34 +1,77 @@
|
||||
module go.ntppool.org/common
|
||||
|
||||
go 1.21
|
||||
go 1.23.5
|
||||
|
||||
require (
|
||||
github.com/abh/certman v0.4.0
|
||||
github.com/labstack/echo-contrib v0.17.2
|
||||
github.com/labstack/echo/v4 v4.13.3
|
||||
github.com/oklog/ulid/v2 v2.1.0
|
||||
github.com/prometheus/client_golang v1.16.0
|
||||
github.com/segmentio/kafka-go v0.4.42
|
||||
github.com/spf13/cobra v1.7.0
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9
|
||||
golang.org/x/mod v0.12.0
|
||||
golang.org/x/sync v0.3.0
|
||||
github.com/prometheus/client_golang v1.20.5
|
||||
github.com/remychantenay/slog-otel v1.3.2
|
||||
github.com/samber/slog-echo v1.14.8
|
||||
github.com/samber/slog-multi v1.2.4
|
||||
github.com/segmentio/kafka-go v0.4.47
|
||||
github.com/spf13/cobra v1.8.1
|
||||
go.opentelemetry.io/contrib/bridges/otelslog v0.8.0
|
||||
go.opentelemetry.io/contrib/exporters/autoexport v0.58.0
|
||||
go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho v0.58.0
|
||||
go.opentelemetry.io/otel v1.33.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0
|
||||
go.opentelemetry.io/otel/log v0.9.0
|
||||
go.opentelemetry.io/otel/sdk v1.33.0
|
||||
go.opentelemetry.io/otel/sdk/log v0.9.0
|
||||
go.opentelemetry.io/otel/trace v1.33.0
|
||||
golang.org/x/mod v0.22.0
|
||||
golang.org/x/net v0.33.0
|
||||
golang.org/x/sync v0.10.0
|
||||
google.golang.org/grpc v1.69.2
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.8.0 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.16.7 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.18 // indirect
|
||||
github.com/klauspost/compress v1.17.11 // indirect
|
||||
github.com/labstack/gommon v0.4.2 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.22 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/prometheus/client_model v0.4.0 // indirect
|
||||
github.com/prometheus/common v0.44.0 // indirect
|
||||
github.com/prometheus/procfs v0.11.1 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.61.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/samber/lo v1.47.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/stretchr/testify v1.8.4 // indirect
|
||||
golang.org/x/net v0.11.0 // indirect
|
||||
golang.org/x/sys v0.12.0 // indirect
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/bridges/prometheus v0.58.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.9.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.9.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.33.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.33.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.55.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.9.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.33.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.33.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.33.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.33.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.4.0 // indirect
|
||||
golang.org/x/crypto v0.31.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/time v0.8.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241223144023-3abc09e42ca8 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 // indirect
|
||||
google.golang.org/protobuf v1.36.1 // indirect
|
||||
)
|
||||
|
222
go.sum
222
go.sum
@ -2,67 +2,92 @@ github.com/abh/certman v0.4.0 h1:XHoDtb0YyRQPclaHMrBDlKTVZpNjTK6vhB0S3Bd/Sbs=
|
||||
github.com/abh/certman v0.4.0/go.mod h1:x8QhpKVZifmV1Hdiwdg9gLo2GMPAxezz1s3zrVnPs+I=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
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/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
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/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/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/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
||||
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
|
||||
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
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/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||
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/labstack/echo-contrib v0.17.2 h1:K1zivqmtcC70X9VdBFdLomjPDEVHlrcAObqmuFj1c6w=
|
||||
github.com/labstack/echo-contrib v0.17.2/go.mod h1:NeDh3PX7j/u+jR4iuDt1zHmWZSCz9c/p9mxXcDpyS8E=
|
||||
github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY=
|
||||
github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g=
|
||||
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
||||
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/oklog/ulid/v2 v2.1.0 h1:+9lhoxAP56we25tyYETBBY1YLA2SaoLvUFgrP2miPJU=
|
||||
github.com/oklog/ulid/v2 v2.1.0/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=
|
||||
github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
|
||||
github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ=
|
||||
github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
|
||||
github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8=
|
||||
github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc=
|
||||
github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY=
|
||||
github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
|
||||
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
|
||||
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
|
||||
github.com/prometheus/procfs v0.11.0 h1:5EAgkfkMl659uZPbe9AS2N68a7Cc1TJbPEuGzFuRbyk=
|
||||
github.com/prometheus/procfs v0.11.0/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
|
||||
github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI=
|
||||
github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY=
|
||||
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
|
||||
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||
github.com/prometheus/common v0.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ=
|
||||
github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s=
|
||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||
github.com/remychantenay/slog-otel v1.3.2 h1:ZBx8qnwfLJ6e18Vba4e9Xp9B7khTmpIwFsU1sAmActw=
|
||||
github.com/remychantenay/slog-otel v1.3.2/go.mod h1:gKW4tQ8cGOKoA+bi7wtYba/tcJ6Tc9XyQ/EW8gHA/2E=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/segmentio/kafka-go v0.4.42 h1:qffhBZCz4WcWyNuHEclHjIMLs2slp6mZO8px+5W5tfU=
|
||||
github.com/segmentio/kafka-go v0.4.42/go.mod h1:d0g15xPMqoUookug0OU75DhGZxXwCFxSLeJ4uphwJzg=
|
||||
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
|
||||
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
|
||||
github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc=
|
||||
github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU=
|
||||
github.com/samber/slog-echo v1.14.8 h1:R7RF2LWEepsKtC7i6A6o9peS3Rz5HO8+H8OD+8mPD1I=
|
||||
github.com/samber/slog-echo v1.14.8/go.mod h1:K21nbusPmai/MYm8PFactmZoFctkMmkeaTdXXyvhY1c=
|
||||
github.com/samber/slog-multi v1.2.4 h1:k9x3JAWKJFPKffx+oXZ8TasaNuorIW4tG+TXxkt6Ry4=
|
||||
github.com/samber/slog-multi v1.2.4/go.mod h1:ACuZ5B6heK57TfMVkVknN2UZHoFfjCwRxR0Q2OXKHlo=
|
||||
github.com/segmentio/kafka-go v0.4.47 h1:IqziR4pA3vrZq7YdRxaT3w1/5fvIH5qpCwstUanQQB0=
|
||||
github.com/segmentio/kafka-go v0.4.47/go.mod h1:HjF6XbOKh0Pjlkr5GVZxt6CsjjwnmhVOfURM5KMd8qg=
|
||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
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/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.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
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/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
||||
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
|
||||
@ -70,61 +95,122 @@ github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3k
|
||||
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
||||
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/bridges/otelslog v0.8.0 h1:G3sKsNueSdxuACINFxKrQeimAIst0A5ytA2YJH+3e1c=
|
||||
go.opentelemetry.io/contrib/bridges/otelslog v0.8.0/go.mod h1:ptJm3wizguEPurZgarDAwOeX7O0iMR7l+QvIVenhYdE=
|
||||
go.opentelemetry.io/contrib/bridges/prometheus v0.58.0 h1:gQFwWiqm4JUvOjpdmyU0di+2pVQ8QNpk1Ak/54Y6NcY=
|
||||
go.opentelemetry.io/contrib/bridges/prometheus v0.58.0/go.mod h1:CNyFi9PuvHtEJNmMFHaXZMuA4XmgRXIqpFcHdqzLvVU=
|
||||
go.opentelemetry.io/contrib/exporters/autoexport v0.58.0 h1:qVsDVgZd/bC6ZKDOHSjILpm0T/BWvASC9cQU3GYga78=
|
||||
go.opentelemetry.io/contrib/exporters/autoexport v0.58.0/go.mod h1:bAv7mY+5qTsFPFaRpr75vDOocX09I36QH4Rg0slEG/U=
|
||||
go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho v0.58.0 h1:DBk8Zh+Yn3WtWCdGSx1pbEV9/naLtjG16c1zwQA2MBI=
|
||||
go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho v0.58.0/go.mod h1:DFx32LPclW1MNdSKIMrjjetsk0tJtYhAvuGjDIG2SKE=
|
||||
go.opentelemetry.io/contrib/propagators/b3 v1.33.0 h1:ig/IsHyyoQ1F1d6FUDIIW5oYpsuTVtN16AyGOgdjAHQ=
|
||||
go.opentelemetry.io/contrib/propagators/b3 v1.33.0/go.mod h1:EsVYoNy+Eol5znb6wwN3XQTILyjl040gUpEnUSNZfsk=
|
||||
go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw=
|
||||
go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.9.0 h1:gA2gh+3B3NDvRFP30Ufh7CC3TtJRbUSf2TTD0LbCagw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.9.0/go.mod h1:smRTR+02OtrVGjvWE1sQxhuazozKc/BXvvqqnmOxy+s=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.9.0 h1:Za0Z/j9Gf3Z9DKQ1choU9xI2noCxlkcyFFP2Ob3miEQ=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.9.0/go.mod h1:jMRB8N75meTNjDFQyJBA/2Z9en21CsxwMctn08NHY6c=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.33.0 h1:7F29RDmnlqk6B5d+sUqemt8TBfDqxryYW5gX6L74RFA=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.33.0/go.mod h1:ZiGDq7xwDMKmWDrN1XsXAj0iC7hns+2DhxBFSncNHSE=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.33.0 h1:bSjzTvsXZbLSWU8hnZXcKmEVaJjjnandxD0PxThhVU8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.33.0/go.mod h1:aj2rilHL8WjXY1I5V+ra+z8FELtk681deydgYT8ikxU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 h1:wpMfgF8E1rkrT1Z6meFh1NDtownE9Ii3n3X2GJYjsaU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0/go.mod h1:wAy0T/dUbs468uOlkT31xjvqQgEVXv58BRFWEgn5v/0=
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.55.0 h1:sSPw658Lk2NWAv74lkD3B/RSDb+xRFx46GjkrL3VUZo=
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.55.0/go.mod h1:nC00vyCmQixoeaxF6KNyP42II/RHa9UdruK02qBmHvI=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.9.0 h1:iI15wfQb5ZtAVTdS5WROxpYmw6Kjez3hT9SuzXhrgGQ=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.9.0/go.mod h1:yepwlNzVVxHWR5ugHIrll+euPQPq4pvysHTDr/daV9o=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.33.0 h1:FiOTYABOX4tdzi8A0+mtzcsTmi6WBOxk66u0f1Mj9Gs=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.33.0/go.mod h1:xyo5rS8DgzV0Jtsht+LCEMwyiDbjpsxBpWETwFRF0/4=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.33.0 h1:W5AWUn/IVe8RFb5pZx1Uh9Laf/4+Qmm4kJL5zPuvR+0=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.33.0/go.mod h1:mzKxJywMNBdEX8TSJais3NnsVZUaJ+bAy6UxPTng2vk=
|
||||
go.opentelemetry.io/otel/log v0.9.0 h1:0OiWRefqJ2QszpCiqwGO0u9ajMPe17q6IscQvvp3czY=
|
||||
go.opentelemetry.io/otel/log v0.9.0/go.mod h1:WPP4OJ+RBkQ416jrFCQFuFKtXKD6mOoYCQm6ykK8VaU=
|
||||
go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ=
|
||||
go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M=
|
||||
go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM=
|
||||
go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM=
|
||||
go.opentelemetry.io/otel/sdk/log v0.9.0 h1:YPCi6W1Eg0vwT/XJWsv2/PaQ2nyAJYuF7UUjQSBe3bc=
|
||||
go.opentelemetry.io/otel/sdk/log v0.9.0/go.mod h1:y0HdrOz7OkXQBuc2yjiqnEHc+CRKeVhRE3hx4RwTmV4=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.33.0 h1:Gs5VK9/WUJhNXZgn8MR6ITatvAmKeIuCtNbsP3JkNqU=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.33.0/go.mod h1:dL5ykHZmm1B1nVRk9dDjChwDmt81MjVp3gLkQRwKf/Q=
|
||||
go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s=
|
||||
go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck=
|
||||
go.opentelemetry.io/proto/otlp v1.4.0 h1:TA9WRvW6zMwP+Ssb6fLoUIuirti1gGbP28GcKG1jgeg=
|
||||
go.opentelemetry.io/proto/otlp v1.4.0/go.mod h1:PPBWZIP98o2ElSqI35IHfu7hIhSwvc5N38Jw8pXuGFY=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/exp v0.0.0-20230711023510-fffb14384f22 h1:FqrVOBQxQ8r/UwwXibI0KMolVhvFiGobSfdE33deHJM=
|
||||
golang.org/x/exp v0.0.0-20230711023510-fffb14384f22/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
|
||||
golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b h1:r+vk0EmXNmekl0S0BascoeeoHk/L7wmaW2QF90K+kYI=
|
||||
golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
|
||||
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
||||
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241223144023-3abc09e42ca8 h1:st3LcW/BPi75W4q1jJTEor/QWwbNlPlDG0JTn6XhZu0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:klhJGKFyG8Tn50enBn7gizg4nXGXJ+jqEREdCWaPcV4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 h1:TqExAhdPaB60Ux47Cn0oLV07rGnxZzIsaRhQaqS666A=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:lcTa1sDdWEIHMWlITnIczmw5w60CF9ffkb8Z+DVmmjA=
|
||||
google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU=
|
||||
google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
|
||||
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
|
||||
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
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=
|
||||
|
@ -32,7 +32,7 @@ func (srv *Server) SetLogger(log *slog.Logger) {
|
||||
}
|
||||
|
||||
func (srv *Server) Listen(ctx context.Context, port int) error {
|
||||
srv.log.Info("Starting health listener", "port", port)
|
||||
srv.log.Info("starting health listener", "port", port)
|
||||
|
||||
serveMux := http.NewServeMux()
|
||||
|
||||
@ -59,11 +59,10 @@ func (srv *Server) Listen(ctx context.Context, port int) error {
|
||||
|
||||
<-ctx.Done()
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, 2*time.Second)
|
||||
defer cancel()
|
||||
|
||||
g.Go(func() error {
|
||||
if err := hsrv.Shutdown(ctx); err != nil {
|
||||
shCtx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
defer cancel()
|
||||
if err := hsrv.Shutdown(shCtx); err != nil {
|
||||
srv.log.Error("health check server shutdown failed", "err", err)
|
||||
return err
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ import (
|
||||
)
|
||||
|
||||
func TestHealthHandler(t *testing.T) {
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/__health", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
|
@ -42,11 +42,9 @@ type Kafka struct {
|
||||
l *log.Logger
|
||||
|
||||
// wr *kafka.Writer
|
||||
|
||||
}
|
||||
|
||||
func (k *Kafka) tlsConfig() (*tls.Config, error) {
|
||||
|
||||
cm, err := certman.New(k.tls.Cert, k.tls.Key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -189,7 +187,6 @@ func (k *Kafka) brokerAddrs() []string {
|
||||
}
|
||||
|
||||
func (k *Kafka) NewWriter(topic string) (*kafka.Writer, error) {
|
||||
|
||||
// https://pkg.go.dev/github.com/segmentio/kafka-go#Writer
|
||||
w := &kafka.Writer{
|
||||
Addr: kafka.TCP(k.brokerAddrs()...),
|
||||
|
78
logger/logfmt.go
Normal file
78
logger/logfmt.go
Normal file
@ -0,0 +1,78 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"log/slog"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type logfmt struct {
|
||||
buf *bytes.Buffer
|
||||
txt slog.Handler
|
||||
next slog.Handler
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func newLogFmtHandler(next slog.Handler) slog.Handler {
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
h := &logfmt{
|
||||
buf: buf,
|
||||
next: next,
|
||||
txt: slog.NewTextHandler(buf, &slog.HandlerOptions{
|
||||
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
|
||||
if a.Key == slog.TimeKey && len(groups) == 0 {
|
||||
return slog.Attr{}
|
||||
}
|
||||
if a.Key == slog.LevelKey && len(groups) == 0 {
|
||||
return slog.Attr{}
|
||||
}
|
||||
return a
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *logfmt) Enabled(ctx context.Context, lvl slog.Level) bool {
|
||||
return h.next.Enabled(ctx, lvl)
|
||||
}
|
||||
|
||||
func (h *logfmt) WithAttrs(attrs []slog.Attr) slog.Handler {
|
||||
return &logfmt{
|
||||
buf: bytes.NewBuffer([]byte{}),
|
||||
next: h.next.WithAttrs(slices.Clone(attrs)),
|
||||
txt: h.txt.WithAttrs(slices.Clone(attrs)),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *logfmt) WithGroup(g string) slog.Handler {
|
||||
if g == "" {
|
||||
return h
|
||||
}
|
||||
return &logfmt{
|
||||
buf: bytes.NewBuffer([]byte{}),
|
||||
next: h.next.WithGroup(g),
|
||||
txt: h.txt.WithGroup(g),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *logfmt) Handle(ctx context.Context, r slog.Record) error {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
if h.buf.Len() > 0 {
|
||||
panic("buffer wasn't empty")
|
||||
}
|
||||
|
||||
h.txt.Handle(ctx, r)
|
||||
r.Message = h.buf.String()
|
||||
r.Message = strings.TrimSuffix(r.Message, "\n")
|
||||
h.buf.Reset()
|
||||
|
||||
return h.next.Handle(ctx, r)
|
||||
}
|
41
logger/logfmt_test.go
Normal file
41
logger/logfmt_test.go
Normal file
@ -0,0 +1,41 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"log/slog"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLogFmt(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
jsonh := slog.NewJSONHandler(&buf, nil)
|
||||
h := newLogFmtHandler(jsonh)
|
||||
|
||||
log := slog.New(h)
|
||||
log.Info("test message", "id", 1010)
|
||||
t.Logf("buf: %s", buf.String())
|
||||
|
||||
msg := map[string]any{}
|
||||
|
||||
err := json.Unmarshal(buf.Bytes(), &msg)
|
||||
if err != nil {
|
||||
t.Logf("couldn't unmarshal json log: %s", err)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if msgTxt, ok := msg["msg"].(string); ok {
|
||||
if !strings.Contains(msgTxt, "id=1010") {
|
||||
t.Log("didn't find id in msg value")
|
||||
t.Fail()
|
||||
}
|
||||
if strings.Contains(msgTxt, "level=") {
|
||||
t.Log("msg value contains level=")
|
||||
t.Fail()
|
||||
}
|
||||
} else {
|
||||
t.Log("didn't find message in output")
|
||||
t.Fail()
|
||||
}
|
||||
}
|
150
logger/logger.go
150
logger/logger.go
@ -3,80 +3,152 @@ package logger
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"log/slog"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"log/slog"
|
||||
slogtraceid "github.com/remychantenay/slog-otel"
|
||||
slogmulti "github.com/samber/slog-multi"
|
||||
"go.opentelemetry.io/contrib/bridges/otelslog"
|
||||
)
|
||||
|
||||
var ConfigPrefix = ""
|
||||
|
||||
var rootLogger *slog.Logger
|
||||
var setup sync.Once
|
||||
var (
|
||||
textLogger *slog.Logger
|
||||
otlpLogger *slog.Logger
|
||||
multiLogger *slog.Logger
|
||||
)
|
||||
|
||||
func Setup() *slog.Logger {
|
||||
var (
|
||||
setupText sync.Once // this sets the default
|
||||
setupOtlp sync.Once // this never sets the default
|
||||
setupMulti sync.Once // this sets the default, and will always run after the others
|
||||
mu sync.Mutex
|
||||
)
|
||||
|
||||
setup.Do(func() {
|
||||
func setupStdErrHandler() slog.Handler {
|
||||
programLevel := new(slog.LevelVar) // Info by default
|
||||
|
||||
var programLevel = new(slog.LevelVar) // Info by default
|
||||
envVar := "DEBUG"
|
||||
if len(ConfigPrefix) > 0 {
|
||||
envVar = ConfigPrefix + "_" + envVar
|
||||
}
|
||||
|
||||
envVar := "DEBUG"
|
||||
if len(ConfigPrefix) > 0 {
|
||||
envVar = ConfigPrefix + "_" + envVar
|
||||
if opt := os.Getenv(envVar); len(opt) > 0 {
|
||||
if debug, _ := strconv.ParseBool(opt); debug {
|
||||
programLevel.Set(slog.LevelDebug)
|
||||
}
|
||||
}
|
||||
|
||||
if opt := os.Getenv(envVar); len(opt) > 0 {
|
||||
if debug, _ := strconv.ParseBool(opt); debug {
|
||||
programLevel.Set(slog.LevelDebug)
|
||||
}
|
||||
}
|
||||
logOptions := &slog.HandlerOptions{Level: programLevel}
|
||||
|
||||
logOptions := &slog.HandlerOptions{Level: programLevel}
|
||||
if len(os.Getenv("INVOCATION_ID")) > 0 {
|
||||
// don't add timestamps when running under systemd
|
||||
log.Default().SetFlags(0)
|
||||
|
||||
if len(os.Getenv("INVOCATION_ID")) > 0 {
|
||||
// don't add timestamps when running under systemd
|
||||
log.Default().SetFlags(0)
|
||||
logOptions.ReplaceAttr = logRemoveTime
|
||||
}
|
||||
|
||||
logReplace := func(groups []string, a slog.Attr) slog.Attr {
|
||||
// Remove time
|
||||
if a.Key == slog.TimeKey && len(groups) == 0 {
|
||||
a.Key = ""
|
||||
a.Value = slog.AnyValue(nil)
|
||||
}
|
||||
return a
|
||||
}
|
||||
logHandler := slogtraceid.OtelHandler{
|
||||
Next: slog.NewTextHandler(os.Stderr, logOptions),
|
||||
}
|
||||
|
||||
logOptions.ReplaceAttr = logReplace
|
||||
}
|
||||
return logHandler
|
||||
}
|
||||
|
||||
logHandler := slog.NewTextHandler(os.Stderr, logOptions)
|
||||
func setupOtlpLogger() *slog.Logger {
|
||||
setupOtlp.Do(func() {
|
||||
otlpLogger = slog.New(
|
||||
newLogFmtHandler(otelslog.NewHandler("common")),
|
||||
)
|
||||
})
|
||||
return otlpLogger
|
||||
}
|
||||
|
||||
// https://github.com/cyrusaf/ctxlog/pull/1
|
||||
// log := slog.New(ctxlog.NewHandler(logHandler))
|
||||
log := slog.New(logHandler)
|
||||
|
||||
slog.SetDefault(log)
|
||||
|
||||
rootLogger = log
|
||||
// SetupMultiLogger will setup and make default a logger that
|
||||
// logs as described in Setup() as well as an OLTP logger.
|
||||
// The "multi logger" is made the default the first time
|
||||
// this function is called
|
||||
func SetupMultiLogger() *slog.Logger {
|
||||
setupMulti.Do(func() {
|
||||
textHandler := Setup().Handler()
|
||||
otlpHandler := setupOtlpLogger().Handler()
|
||||
|
||||
multiHandler := slogmulti.Fanout(
|
||||
textHandler,
|
||||
otlpHandler,
|
||||
)
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
multiLogger = slog.New(multiHandler)
|
||||
slog.SetDefault(multiLogger)
|
||||
})
|
||||
|
||||
return rootLogger
|
||||
return multiLogger
|
||||
}
|
||||
|
||||
// SetupOLTP configures and returns a logger sending logs
|
||||
// via OpenTelemetry (configured via the tracing package).
|
||||
//
|
||||
// This was made to work with Loki + Grafana that makes it
|
||||
// hard to view the log attributes in the UI, so the log
|
||||
// message is formatted similarly to the text logger. The
|
||||
// attributes are duplicated as OLTP attributes in the
|
||||
// log messages. https://github.com/grafana/loki/issues/14788
|
||||
func SetupOLTP() *slog.Logger {
|
||||
return setupOtlpLogger()
|
||||
}
|
||||
|
||||
// Setup returns an slog.Logger configured for text formatting
|
||||
// to stderr.
|
||||
// OpenTelemetry trace_id and span_id's are logged as attributes
|
||||
// when available.
|
||||
// When the application is running under systemd timestamps are
|
||||
// omitted. On first call the slog default logger is set to this
|
||||
// logger as well.
|
||||
//
|
||||
// If SetupMultiLogger has been called Setup() will return
|
||||
// the "multi logger"
|
||||
func Setup() *slog.Logger {
|
||||
setupText.Do(func() {
|
||||
h := setupStdErrHandler()
|
||||
textLogger = slog.New(h)
|
||||
slog.SetDefault(textLogger)
|
||||
})
|
||||
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
if multiLogger != nil {
|
||||
return multiLogger
|
||||
}
|
||||
return textLogger
|
||||
}
|
||||
|
||||
type loggerKey struct{}
|
||||
|
||||
// NewContext adds the logger to the context.
|
||||
// NewContext adds the logger to the context. Use this
|
||||
// to for example make a request specific logger available
|
||||
// to other functions through the context
|
||||
func NewContext(ctx context.Context, l *slog.Logger) context.Context {
|
||||
return context.WithValue(ctx, loggerKey{}, l)
|
||||
}
|
||||
|
||||
// FromContext retrieves a logger from the context. If there is none,
|
||||
// it returns the default logger.
|
||||
// it returns the default logger
|
||||
func FromContext(ctx context.Context) *slog.Logger {
|
||||
if l, ok := ctx.Value(loggerKey{}).(*slog.Logger); ok {
|
||||
return l
|
||||
}
|
||||
return Setup()
|
||||
}
|
||||
|
||||
func logRemoveTime(groups []string, a slog.Attr) slog.Attr {
|
||||
// Remove time
|
||||
if a.Key == slog.TimeKey && len(groups) == 0 {
|
||||
return slog.Attr{}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
@ -27,10 +27,15 @@ func NewStdLog(key string, debug bool, log *slog.Logger) *stdLoggerish {
|
||||
return sl
|
||||
}
|
||||
|
||||
func (l stdLoggerish) Println(msg ...interface{}) {
|
||||
func (l stdLoggerish) Println(msg ...any) {
|
||||
l.f(l.key, "msg", msg)
|
||||
}
|
||||
|
||||
func (l stdLoggerish) Printf(msg string, args ...interface{}) {
|
||||
func (l stdLoggerish) Printf(msg string, args ...any) {
|
||||
l.f(l.key, "msg", fmt.Sprintf(msg, args...))
|
||||
}
|
||||
|
||||
func (l stdLoggerish) Fatalf(msg string, args ...any) {
|
||||
l.log.Error(l.key, "msg", fmt.Sprintf(msg, args...))
|
||||
panic("fatal error") // todo: does this make sense at all?
|
||||
}
|
||||
|
@ -32,7 +32,6 @@ func (m *Metrics) Registry() *prometheus.Registry {
|
||||
}
|
||||
|
||||
func (m *Metrics) Handler() http.Handler {
|
||||
|
||||
log := logger.NewStdLog("prom http", false, nil)
|
||||
|
||||
return promhttp.HandlerFor(m.r, promhttp.HandlerOpts{
|
||||
@ -46,7 +45,6 @@ func (m *Metrics) Handler() http.Handler {
|
||||
// the specified port. The server will shutdown and return when
|
||||
// the provided context is done
|
||||
func (m *Metrics) ListenAndServe(ctx context.Context, port int) error {
|
||||
|
||||
log := logger.Setup()
|
||||
|
||||
srv := &http.Server{
|
||||
|
@ -3,10 +3,10 @@
|
||||
all: Makefile scripts
|
||||
|
||||
Makefile: .PHONY
|
||||
cp ~/src/go/common/scripts/Makefile .
|
||||
cp ~/src/go/ntp/common/scripts/Makefile .
|
||||
|
||||
scripts: .PHONY
|
||||
cp ~/src/go/common/scripts/{fury-publish,run-goreleaser,download-release} .
|
||||
cp ~/src/go/ntp/common/scripts/{fury-publish,run-goreleaser,download-release} .
|
||||
|
||||
|
||||
.PHONY:
|
||||
|
@ -2,7 +2,16 @@
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
go install github.com/goreleaser/goreleaser@v1.20.0
|
||||
go install github.com/goreleaser/goreleaser/v2@v2.8.2
|
||||
|
||||
if [ ! -z "${harbor_username:-}" ]; then
|
||||
DOCKER_FILE=~/.docker/config.json
|
||||
if [ ! -e $DOCKER_FILE ]; then
|
||||
mkdir -p ~/.docker/
|
||||
export harbor_auth=`cat /dev/null | jq -s -r '[ env.harbor_username, env.harbor_password ] | join(":") | @base64'`
|
||||
echo '{"auths":{"harbor.ntppool.org":{"auth":""}}}' | jq '.auths["harbor.ntppool.org"].auth=env.harbor_auth' > $DOCKER_FILE
|
||||
fi
|
||||
fi
|
||||
|
||||
DRONE_TAG=${DRONE_TAG-""}
|
||||
|
||||
@ -12,4 +21,4 @@ if [ -z "$DRONE_TAG" ]; then
|
||||
is_snapshot="--snapshot"
|
||||
fi
|
||||
|
||||
goreleaser release $is_snapshot -p 6 --skip-publish
|
||||
goreleaser release $is_snapshot -p 6 --skip=publish
|
||||
|
@ -15,7 +15,7 @@ func (d Duration) MarshalJSON() ([]byte, error) {
|
||||
}
|
||||
|
||||
func (d *Duration) UnmarshalJSON(b []byte) error {
|
||||
var v interface{}
|
||||
var v any
|
||||
if err := json.Unmarshal(b, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -18,5 +18,4 @@ func TestDuration(t *testing.T) {
|
||||
if foo.Foo.Seconds() != 30 {
|
||||
t.Fatalf("parsed time.Duration wasn't 30 seconds: %s", foo.Foo)
|
||||
}
|
||||
|
||||
}
|
||||
|
281
tracing/tracing.go
Normal file
281
tracing/tracing.go
Normal file
@ -0,0 +1,281 @@
|
||||
package tracing
|
||||
|
||||
// todo, review:
|
||||
// https://github.com/ttys3/tracing-go/blob/main/tracing.go#L136
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"os"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"go.ntppool.org/common/logger"
|
||||
"go.ntppool.org/common/version"
|
||||
"google.golang.org/grpc/credentials"
|
||||
|
||||
"go.opentelemetry.io/contrib/exporters/autoexport"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
|
||||
logglobal "go.opentelemetry.io/otel/log/global"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
sdklog "go.opentelemetry.io/otel/sdk/log"
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
const (
|
||||
// svcNameKey is the environment variable name that Service Name information will be read from.
|
||||
svcNameKey = "OTEL_SERVICE_NAME"
|
||||
|
||||
otelExporterOTLPProtoEnvKey = "OTEL_EXPORTER_OTLP_PROTOCOL"
|
||||
otelExporterOTLPTracesProtoEnvKey = "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL"
|
||||
)
|
||||
|
||||
var errInvalidOTLPProtocol = errors.New("invalid OTLP protocol - should be one of ['grpc', 'http/protobuf']")
|
||||
|
||||
// https://github.com/open-telemetry/opentelemetry-go/blob/main/exporters/otlp/otlptrace/otlptracehttp/example_test.go
|
||||
|
||||
type TpShutdownFunc func(ctx context.Context) error
|
||||
|
||||
func Tracer() trace.Tracer {
|
||||
traceProvider := otel.GetTracerProvider()
|
||||
return traceProvider.Tracer("ntppool-tracer")
|
||||
}
|
||||
|
||||
func Start(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, trace.Span) {
|
||||
return Tracer().Start(ctx, spanName, opts...)
|
||||
}
|
||||
|
||||
type GetClientCertificate func(*tls.CertificateRequestInfo) (*tls.Certificate, error)
|
||||
|
||||
type TracerConfig struct {
|
||||
ServiceName string
|
||||
Environment string
|
||||
Endpoint string
|
||||
EndpointURL string
|
||||
|
||||
CertificateProvider GetClientCertificate
|
||||
RootCAs *x509.CertPool
|
||||
}
|
||||
|
||||
func InitTracer(ctx context.Context, cfg *TracerConfig) (TpShutdownFunc, error) {
|
||||
// todo: setup environment from cfg
|
||||
return SetupSDK(ctx, cfg)
|
||||
}
|
||||
|
||||
func SetupSDK(ctx context.Context, cfg *TracerConfig) (shutdown TpShutdownFunc, err error) {
|
||||
if cfg == nil {
|
||||
cfg = &TracerConfig{}
|
||||
}
|
||||
|
||||
log := logger.Setup()
|
||||
|
||||
if serviceName := os.Getenv(svcNameKey); len(serviceName) == 0 {
|
||||
if len(cfg.ServiceName) > 0 {
|
||||
os.Setenv(svcNameKey, cfg.ServiceName)
|
||||
}
|
||||
}
|
||||
|
||||
resources := []resource.Option{
|
||||
resource.WithFromEnv(), // Discover and provide attributes from OTEL_RESOURCE_ATTRIBUTES and OTEL_SERVICE_NAME environment variables.
|
||||
resource.WithTelemetrySDK(), // Discover and provide information about the OpenTelemetry SDK used.
|
||||
resource.WithProcess(), // Discover and provide process information.
|
||||
resource.WithOS(), // Discover and provide OS information.
|
||||
resource.WithContainer(), // Discover and provide container information.
|
||||
resource.WithHost(), // Discover and provide host information.
|
||||
|
||||
// set above via os.Setenv() for WithFromEnv to find
|
||||
// resource.WithAttributes(semconv.ServiceNameKey.String(cfg.ServiceName)),
|
||||
|
||||
resource.WithAttributes(semconv.ServiceVersionKey.String(version.Version())),
|
||||
}
|
||||
|
||||
if len(cfg.Environment) > 0 {
|
||||
resources = append(resources,
|
||||
resource.WithAttributes(attribute.String("environment", cfg.Environment)),
|
||||
)
|
||||
}
|
||||
|
||||
res, err := resource.New(
|
||||
context.Background(),
|
||||
resources...,
|
||||
)
|
||||
if errors.Is(err, resource.ErrPartialResource) || errors.Is(err, resource.ErrSchemaURLConflict) {
|
||||
log.Warn("otel resource setup", "err", err) // Log non-fatal issues.
|
||||
} else if err != nil {
|
||||
log.Error("otel resource setup", "err", err)
|
||||
return
|
||||
}
|
||||
|
||||
var shutdownFuncs []func(context.Context) error
|
||||
shutdown = func(ctx context.Context) error {
|
||||
var err error
|
||||
// need to shutdown the providers first,
|
||||
// exporters after which is the opposite
|
||||
// order they are setup.
|
||||
slices.Reverse(shutdownFuncs)
|
||||
for _, fn := range shutdownFuncs {
|
||||
// log.Warn("shutting down", "fn", fn)
|
||||
err = errors.Join(err, fn(ctx))
|
||||
}
|
||||
shutdownFuncs = nil
|
||||
if err != nil {
|
||||
log.Warn("shutdown returned errors", "err", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// handleErr calls shutdown for cleanup and makes sure that all errors are returned.
|
||||
handleErr := func(inErr error) {
|
||||
err = errors.Join(inErr, shutdown(ctx))
|
||||
}
|
||||
|
||||
prop := newPropagator()
|
||||
otel.SetTextMapPropagator(prop)
|
||||
|
||||
var spanExporter sdktrace.SpanExporter
|
||||
|
||||
switch os.Getenv("OTEL_TRACES_EXPORTER") {
|
||||
case "":
|
||||
spanExporter, err = newOLTPExporter(ctx, cfg)
|
||||
case "otlp":
|
||||
spanExporter, err = newOLTPExporter(ctx, cfg)
|
||||
default:
|
||||
// log.Debug("OTEL_TRACES_EXPORTER", "fallback", os.Getenv("OTEL_TRACES_EXPORTER"))
|
||||
spanExporter, err = autoexport.NewSpanExporter(ctx)
|
||||
}
|
||||
if err != nil {
|
||||
handleErr(err)
|
||||
return
|
||||
}
|
||||
shutdownFuncs = append(shutdownFuncs, spanExporter.Shutdown)
|
||||
|
||||
logExporter, err := autoexport.NewLogExporter(ctx)
|
||||
if err != nil {
|
||||
handleErr(err)
|
||||
return
|
||||
}
|
||||
shutdownFuncs = append(shutdownFuncs, logExporter.Shutdown)
|
||||
|
||||
// Set up trace provider.
|
||||
tracerProvider, err := newTraceProvider(spanExporter, res)
|
||||
if err != nil {
|
||||
handleErr(err)
|
||||
return
|
||||
}
|
||||
shutdownFuncs = append(shutdownFuncs, tracerProvider.Shutdown)
|
||||
otel.SetTracerProvider(tracerProvider)
|
||||
|
||||
logProvider := sdklog.NewLoggerProvider(sdklog.WithResource(res),
|
||||
sdklog.WithProcessor(
|
||||
sdklog.NewBatchProcessor(logExporter, sdklog.WithExportBufferSize(10)),
|
||||
),
|
||||
)
|
||||
|
||||
logglobal.SetLoggerProvider(logProvider)
|
||||
shutdownFuncs = append(shutdownFuncs, func(ctx context.Context) error {
|
||||
logProvider.ForceFlush(ctx)
|
||||
return logProvider.Shutdown(ctx)
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
handleErr(err)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func newOLTPExporter(ctx context.Context, cfg *TracerConfig) (sdktrace.SpanExporter, error) {
|
||||
log := logger.Setup()
|
||||
|
||||
var tlsConfig *tls.Config
|
||||
|
||||
if cfg.CertificateProvider != nil {
|
||||
tlsConfig = &tls.Config{
|
||||
GetClientCertificate: cfg.CertificateProvider,
|
||||
RootCAs: cfg.RootCAs,
|
||||
}
|
||||
}
|
||||
|
||||
proto := os.Getenv(otelExporterOTLPTracesProtoEnvKey)
|
||||
if proto == "" {
|
||||
proto = os.Getenv(otelExporterOTLPProtoEnvKey)
|
||||
}
|
||||
|
||||
// Fallback to default, http/protobuf.
|
||||
if proto == "" {
|
||||
proto = "http/protobuf"
|
||||
}
|
||||
|
||||
var client otlptrace.Client
|
||||
|
||||
switch proto {
|
||||
case "grpc":
|
||||
opts := []otlptracegrpc.Option{
|
||||
otlptracegrpc.WithCompressor("gzip"),
|
||||
}
|
||||
if tlsConfig != nil {
|
||||
opts = append(opts, otlptracegrpc.WithTLSCredentials(credentials.NewTLS(tlsConfig)))
|
||||
}
|
||||
if len(cfg.Endpoint) > 0 {
|
||||
log.Info("adding option", "Endpoint", cfg.Endpoint)
|
||||
opts = append(opts, otlptracegrpc.WithEndpoint(cfg.Endpoint))
|
||||
}
|
||||
if len(cfg.EndpointURL) > 0 {
|
||||
log.Info("adding option", "EndpointURL", cfg.EndpointURL)
|
||||
opts = append(opts, otlptracegrpc.WithEndpointURL(cfg.EndpointURL))
|
||||
}
|
||||
|
||||
client = otlptracegrpc.NewClient(opts...)
|
||||
case "http/protobuf", "http/json":
|
||||
opts := []otlptracehttp.Option{
|
||||
otlptracehttp.WithCompression(otlptracehttp.GzipCompression),
|
||||
}
|
||||
if tlsConfig != nil {
|
||||
opts = append(opts, otlptracehttp.WithTLSClientConfig(tlsConfig))
|
||||
}
|
||||
if len(cfg.Endpoint) > 0 {
|
||||
opts = append(opts, otlptracehttp.WithEndpoint(cfg.Endpoint))
|
||||
}
|
||||
if len(cfg.EndpointURL) > 0 {
|
||||
opts = append(opts, otlptracehttp.WithEndpointURL(cfg.EndpointURL))
|
||||
}
|
||||
|
||||
client = otlptracehttp.NewClient(opts...)
|
||||
default:
|
||||
return nil, errInvalidOTLPProtocol
|
||||
}
|
||||
|
||||
exporter, err := otlptrace.New(ctx, client)
|
||||
if err != nil {
|
||||
log.ErrorContext(ctx, "creating OTLP trace exporter", "err", err)
|
||||
}
|
||||
return exporter, err
|
||||
}
|
||||
|
||||
func newTraceProvider(traceExporter sdktrace.SpanExporter, res *resource.Resource) (*sdktrace.TracerProvider, error) {
|
||||
traceProvider := sdktrace.NewTracerProvider(
|
||||
sdktrace.WithResource(res),
|
||||
sdktrace.WithBatcher(traceExporter,
|
||||
sdktrace.WithBatchTimeout(time.Second*3),
|
||||
),
|
||||
)
|
||||
return traceProvider, nil
|
||||
}
|
||||
|
||||
func newPropagator() propagation.TextMapPropagator {
|
||||
return propagation.NewCompositeTextMapPropagator(
|
||||
propagation.TraceContext{},
|
||||
propagation.Baggage{},
|
||||
)
|
||||
}
|
20
tracing/tracing_test.go
Normal file
20
tracing/tracing_test.go
Normal file
@ -0,0 +1,20 @@
|
||||
package tracing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestInit(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
os.Setenv("OTEL_EXPORTER_OTLP_ENDPOINT", "localhost")
|
||||
|
||||
shutdownFn, err := InitTracer(ctx, &TracerConfig{})
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
defer shutdownFn(ctx)
|
||||
}
|
52
types/log_scores.go
Normal file
52
types/log_scores.go
Normal file
@ -0,0 +1,52 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
)
|
||||
|
||||
type LogScoreAttributes struct {
|
||||
Leap int8 `json:"leap,omitempty"`
|
||||
Stratum int8 `json:"stratum,omitempty"`
|
||||
NoResponse bool `json:"no_response,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Warning string `json:"warning,omitempty"`
|
||||
|
||||
FromLSID int `json:"from_ls_id,omitempty"`
|
||||
FromSSID int `json:"from_ss_id,omitempty"`
|
||||
}
|
||||
|
||||
func (lsa *LogScoreAttributes) String() string {
|
||||
b, err := json.Marshal(lsa)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func (lsa *LogScoreAttributes) Value() (driver.Value, error) {
|
||||
return json.Marshal(lsa)
|
||||
}
|
||||
|
||||
func (lsa *LogScoreAttributes) Scan(value any) error {
|
||||
var source []byte
|
||||
_t := LogScoreAttributes{}
|
||||
|
||||
switch v := value.(type) {
|
||||
case []uint8:
|
||||
source = v
|
||||
case string:
|
||||
source = []byte(v)
|
||||
case nil:
|
||||
return nil
|
||||
default:
|
||||
return errors.New("incompatible type for StringInterfaceMap")
|
||||
}
|
||||
err := json.Unmarshal(source, &_t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*lsa = _t
|
||||
return nil
|
||||
}
|
@ -14,8 +14,7 @@ import (
|
||||
)
|
||||
|
||||
var monotonicPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
|
||||
New: func() any {
|
||||
log := logger.Setup()
|
||||
|
||||
var seed int64
|
||||
@ -39,7 +38,6 @@ var monotonicPool = sync.Pool{
|
||||
}
|
||||
|
||||
func MakeULID(t time.Time) (*oklid.ULID, error) {
|
||||
|
||||
mono := monotonicPool.Get().(io.Reader)
|
||||
|
||||
id, err := oklid.New(oklid.Timestamp(t), mono)
|
||||
|
@ -2,21 +2,23 @@ package version
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/spf13/cobra"
|
||||
"go.ntppool.org/common/logger"
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
// VERSION has the current software version (set in the build process)
|
||||
var VERSION string
|
||||
var buildTime string
|
||||
var gitVersion string
|
||||
var gitModified bool
|
||||
var (
|
||||
VERSION string
|
||||
buildTime string
|
||||
gitVersion string
|
||||
gitModified bool
|
||||
)
|
||||
|
||||
var info Info
|
||||
|
||||
@ -28,7 +30,6 @@ type Info struct {
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
info.BuildTime = buildTime
|
||||
info.GitRev = gitVersion
|
||||
|
||||
@ -39,7 +40,7 @@ func init() {
|
||||
VERSION = "v" + VERSION
|
||||
}
|
||||
if !semver.IsValid(VERSION) {
|
||||
logger.Setup().Warn("invalid version number", "version", VERSION)
|
||||
slog.Default().Warn("invalid version number", "version", VERSION)
|
||||
}
|
||||
}
|
||||
if bi, ok := debug.ReadBuildInfo(); ok {
|
||||
@ -90,8 +91,18 @@ func VersionCmd(name string) *cobra.Command {
|
||||
return versionCmd
|
||||
}
|
||||
|
||||
type KongVersionCmd struct {
|
||||
Name string `kong:"-"`
|
||||
}
|
||||
|
||||
func (cmd *KongVersionCmd) Run() error {
|
||||
fmt.Printf("%s %s\n", cmd.Name, Version())
|
||||
return nil
|
||||
}
|
||||
|
||||
func RegisterMetric(name string, registry prometheus.Registerer) {
|
||||
if len(name) > 0 {
|
||||
name = strings.ReplaceAll(name, "-", "_")
|
||||
name = name + "_build_info"
|
||||
} else {
|
||||
name = "build_info"
|
||||
@ -152,3 +163,17 @@ func Version() string {
|
||||
v = fmt.Sprintf("%s (%s)", v, strings.Join(extra, ", "))
|
||||
return v
|
||||
}
|
||||
|
||||
func CheckVersion(version, minimumVersion string) bool {
|
||||
if version == "dev-snapshot" {
|
||||
return true
|
||||
}
|
||||
if idx := strings.Index(version, "/"); idx >= 0 {
|
||||
version = version[0:idx]
|
||||
}
|
||||
if semver.Compare(version, minimumVersion) < 0 {
|
||||
// log.Debug("version too old", "v", cl.Version.Version)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
1
xff/fastlyxff/fastly.json
Normal file
1
xff/fastlyxff/fastly.json
Normal file
@ -0,0 +1 @@
|
||||
{"addresses":["23.235.32.0/20","43.249.72.0/22","103.244.50.0/24","103.245.222.0/23","103.245.224.0/24","104.156.80.0/20","140.248.64.0/18","140.248.128.0/17","146.75.0.0/17","151.101.0.0/16","157.52.64.0/18","167.82.0.0/17","167.82.128.0/20","167.82.160.0/20","167.82.224.0/20","172.111.64.0/18","185.31.16.0/22","199.27.72.0/21","199.232.0.0/16"],"ipv6_addresses":["2a04:4e40::/32","2a04:4e42::/32"]}
|
51
xff/fastlyxff/xff.go
Normal file
51
xff/fastlyxff/xff.go
Normal file
@ -0,0 +1,51 @@
|
||||
package fastlyxff
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type FastlyXFF struct {
|
||||
IPv4 []string `json:"addresses"`
|
||||
IPv6 []string `json:"ipv6_addresses"`
|
||||
}
|
||||
|
||||
type TrustedNets struct {
|
||||
prefixes []netip.Prefix
|
||||
}
|
||||
|
||||
func New(fileName string) (*FastlyXFF, error) {
|
||||
b, err := os.ReadFile(fileName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d := FastlyXFF{}
|
||||
|
||||
err = json.Unmarshal(b, &d)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &d, nil
|
||||
}
|
||||
|
||||
func (xff *FastlyXFF) EchoTrustOption() ([]echo.TrustOption, error) {
|
||||
ranges := []echo.TrustOption{}
|
||||
|
||||
for _, s := range append(xff.IPv4, xff.IPv6...) {
|
||||
_, cidr, err := net.ParseCIDR(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
trust := echo.TrustIPRange(cidr)
|
||||
ranges = append(ranges, trust)
|
||||
}
|
||||
|
||||
return ranges, nil
|
||||
}
|
20
xff/fastlyxff/xff_test.go
Normal file
20
xff/fastlyxff/xff_test.go
Normal file
@ -0,0 +1,20 @@
|
||||
package fastlyxff
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestFastlyIPRanges(t *testing.T) {
|
||||
fastlyxff, err := New("fastly.json")
|
||||
if err != nil {
|
||||
t.Fatalf("could not load test data: %s", err)
|
||||
}
|
||||
|
||||
data, err := fastlyxff.EchoTrustOption()
|
||||
if err != nil {
|
||||
t.Fatalf("could not parse test data: %s", err)
|
||||
}
|
||||
|
||||
if len(data) < 10 {
|
||||
t.Logf("only got %d prefixes, expected more", len(data))
|
||||
t.Fail()
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user