Compare commits

..

7 Commits
v0.3.1 ... main

25 changed files with 202 additions and 52 deletions

View File

@ -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

View File

@ -11,7 +11,7 @@ import (
"go.ntppool.org/common/logger"
)
//go:generate accessory -type Config
//go:generate go tool github.com/masaushi/accessory -type Config
type Config struct {
deploymentMode string `accessor:"getter"`
@ -50,6 +50,10 @@ 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

View File

@ -7,7 +7,6 @@ import (
)
func TestBaseURL(t *testing.T) {
os.Setenv("web_hostname", "www.ntp.dev, web.ntppool.dev")
os.Setenv("web_tls", "yes")
@ -22,5 +21,4 @@ func TestBaseURL(t *testing.T) {
if u != "https://www.ntp.dev/foo?foo=bar" {
t.Fatalf("unexpected WebURL: %s", u)
}
}

18
config/depenv/context.go Normal file
View 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
View 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
}

View 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])
}

View File

@ -16,6 +16,7 @@ import (
"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"
)
@ -50,7 +51,8 @@ func (ek *Ekko) Start(ctx context.Context) error {
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.Server.ListenAndServe()
err := e.StartH2CServer(e.Server.Addr, &http2.Server{})
if err == http.ErrServerClosed {
return nil
}
@ -120,7 +122,13 @@ func (ek *Ekko) setup(ctx context.Context) (*echo.Echo, error) {
e.Use(middleware.Gzip())
}
e.Use(middleware.Secure())
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 {

4
go.mod
View File

@ -1,8 +1,6 @@
module go.ntppool.org/common
go 1.23
toolchain go1.23.4
go 1.23.5
require (
github.com/abh/certman v0.4.0

View File

@ -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
}

View File

@ -8,7 +8,6 @@ import (
)
func TestHealthHandler(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/__health", nil)
w := httptest.NewRecorder()

View File

@ -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()...),

View File

@ -17,7 +17,6 @@ type logfmt struct {
}
func newLogFmtHandler(next slog.Handler) slog.Handler {
buf := bytes.NewBuffer([]byte{})
h := &logfmt{

View File

@ -9,7 +9,6 @@ import (
)
func TestLogFmt(t *testing.T) {
var buf bytes.Buffer
jsonh := slog.NewJSONHandler(&buf, nil)
h := newLogFmtHandler(jsonh)
@ -39,5 +38,4 @@ func TestLogFmt(t *testing.T) {
t.Log("didn't find message in output")
t.Fail()
}
}

View File

@ -15,17 +15,21 @@ import (
var ConfigPrefix = ""
var textLogger *slog.Logger
var otlpLogger *slog.Logger
var multiLogger *slog.Logger
var (
textLogger *slog.Logger
otlpLogger *slog.Logger
multiLogger *slog.Logger
)
var setupText sync.Once // this sets the default
var setupOtlp sync.Once // this never sets the default
var setupMulti sync.Once // this sets the default, and will always run after the others
var mu sync.Mutex
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
)
func setupStdErrHandler() slog.Handler {
var programLevel = new(slog.LevelVar) // Info by default
programLevel := new(slog.LevelVar) // Info by default
envVar := "DEBUG"
if len(ConfigPrefix) > 0 {

View File

@ -27,15 +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 ...interface{}) {
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?
}

View File

@ -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{

View File

@ -2,7 +2,7 @@
set -euo pipefail
go install github.com/goreleaser/goreleaser/v2@v2.5.0
go install github.com/goreleaser/goreleaser/v2@v2.8.2
if [ ! -z "${harbor_username:-}" ]; then
DOCKER_FILE=~/.docker/config.json

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -196,7 +196,6 @@ func SetupSDK(ctx context.Context, cfg *TracerConfig) (shutdown TpShutdownFunc,
}
func newOLTPExporter(ctx context.Context, cfg *TracerConfig) (sdktrace.SpanExporter, error) {
log := logger.Setup()
var tlsConfig *tls.Config
@ -238,7 +237,7 @@ func newOLTPExporter(ctx context.Context, cfg *TracerConfig) (sdktrace.SpanExpor
}
client = otlptracegrpc.NewClient(opts...)
case "http/protobuf":
case "http/protobuf", "http/json":
opts := []otlptracehttp.Option{
otlptracehttp.WithCompression(otlptracehttp.GzipCompression),
}

View File

@ -7,7 +7,6 @@ import (
)
func TestInit(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
@ -18,5 +17,4 @@ func TestInit(t *testing.T) {
t.FailNow()
}
defer shutdownFn(ctx)
}

View File

@ -29,7 +29,7 @@ func (lsa *LogScoreAttributes) Value() (driver.Value, error) {
return json.Marshal(lsa)
}
func (lsa *LogScoreAttributes) Scan(value interface{}) error {
func (lsa *LogScoreAttributes) Scan(value any) error {
var source []byte
_t := LogScoreAttributes{}

View File

@ -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)

View File

@ -13,10 +13,12 @@ import (
)
// 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
@ -90,6 +91,15 @@ 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, "-", "_")
@ -158,6 +168,9 @@ 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

View File

@ -3,14 +3,12 @@ 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)
}
@ -19,5 +17,4 @@ func TestFastlyIPRanges(t *testing.T) {
t.Logf("only got %d prefixes, expected more", len(data))
t.Fail()
}
}