diff --git a/go.mod b/go.mod index ad4792c..1bc8c40 100644 --- a/go.mod +++ b/go.mod @@ -4,13 +4,13 @@ go 1.23 require ( github.com/avast/retry-go/v4 v4.6.0 + github.com/cilium/cilium v1.15.9 github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.6.0 github.com/metal-stack/metal-go v0.37.2 github.com/metal-stack/metal-lib v0.18.4 github.com/metal-stack/v v1.0.3 github.com/spf13/pflag v1.0.5 - github.com/stretchr/testify v1.9.0 go.universe.tf/metallb v0.14.8 k8s.io/api v0.30.2 k8s.io/apimachinery v0.31.0 @@ -19,7 +19,6 @@ require ( k8s.io/component-base v0.30.2 k8s.io/klog/v2 v2.130.1 sigs.k8s.io/controller-runtime v0.18.4 - sigs.k8s.io/yaml v1.4.0 ) require ( @@ -31,13 +30,15 @@ require ( github.com/blang/semver/v4 v4.0.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cilium/ebpf v0.12.3 // indirect + github.com/cilium/proxy v0.0.0-20231031145409-f19708f3d018 // indirect github.com/coreos/go-oidc/v3 v3.11.0 // indirect github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/emicklei/go-restful/v3 v3.12.1 // indirect - github.com/evanphx/json-patch v5.6.0+incompatible // indirect + github.com/evanphx/json-patch v5.7.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect @@ -45,6 +46,7 @@ require ( github.com/go-jose/go-jose/v4 v4.0.4 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-openapi/analysis v0.23.0 // indirect github.com/go-openapi/errors v0.22.0 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect @@ -63,14 +65,19 @@ require ( github.com/google/cel-go v0.17.8 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/gofuzz v1.2.0 // indirect + github.com/google/gopacket v1.1.19 // indirect github.com/gorilla/mux v1.8.1 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect github.com/imdario/mergo v0.3.16 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.9 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/lestrrat-go/blackmagic v1.0.2 // indirect github.com/lestrrat-go/httpcc v1.0.1 // indirect @@ -78,6 +85,8 @@ require ( github.com/lestrrat-go/iter v1.0.2 // indirect github.com/lestrrat-go/jwx/v2 v2.1.0 // indirect github.com/lestrrat-go/option v1.0.1 // indirect + github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed // indirect + github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/metal-stack/security v0.8.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect @@ -86,20 +95,39 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/oklog/ulid v1.3.1 // indirect - github.com/opentracing/opentracing-go v1.2.0 // indirect + github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect github.com/prometheus/client_golang v1.20.5 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.60.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect + github.com/rogpeppe/go-internal v1.13.1 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sasha-s/go-deadlock v0.3.1 // indirect github.com/segmentio/asm v1.2.0 // indirect + github.com/shirou/gopsutil/v3 v3.23.12 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect github.com/spf13/cobra v1.8.1 // indirect + github.com/spf13/viper v1.19.0 // indirect github.com/stoewer/go-strcase v1.3.0 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/tklauser/go-sysconf v0.3.13 // indirect + github.com/tklauser/numcpus v0.7.0 // indirect + github.com/vishvananda/netlink v1.2.1-beta.2.0.20240524165444-4d4ba1473f21 // indirect + github.com/vishvananda/netns v0.0.4 // indirect github.com/x448/float16 v0.8.4 // indirect - go.etcd.io/etcd/api/v3 v3.5.10 // indirect - go.etcd.io/etcd/client/pkg/v3 v3.5.10 // indirect - go.etcd.io/etcd/client/v3 v3.5.10 // indirect + github.com/yusufpapurcu/wmi v1.2.3 // indirect + go.etcd.io/etcd/api/v3 v3.5.12 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.5.12 // indirect + go.etcd.io/etcd/client/v3 v3.5.12 // indirect go.mongodb.org/mongo-driver v1.16.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect @@ -110,8 +138,10 @@ require ( go.opentelemetry.io/otel/sdk v1.31.0 // indirect go.opentelemetry.io/otel/trace v1.31.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect + go.uber.org/dig v1.17.1 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect + go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect golang.org/x/crypto v0.28.0 // indirect golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect golang.org/x/net v0.30.0 // indirect @@ -127,6 +157,7 @@ require ( google.golang.org/grpc v1.67.1 // indirect google.golang.org/protobuf v1.35.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect @@ -139,4 +170,5 @@ require ( sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.29.0 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/go.sum b/go.sum index 53bbe6d..a5d3e8e 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,7 @@ +cel.dev/expr v0.16.0 h1:yloc84fytn4zmJX2GU3TkXGsaieaV7dQ057Qs4sIG2Y= +cel.dev/expr v0.16.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= @@ -14,8 +18,20 @@ github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= 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/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= +github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= 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/cilium/checkmate v1.0.3 h1:CQC5eOmlAZeEjPrVZY3ZwEBH64lHlx9mXYdUehEwI5w= +github.com/cilium/checkmate v1.0.3/go.mod h1:KiBTasf39/F2hf2yAmHw21YFl3hcEyP4Yk6filxc12A= +github.com/cilium/cilium v1.15.9 h1:edIhmRF4v9LuWUfZbdFT7Vu/NaVSvvDHtLzXM9bBXEs= +github.com/cilium/cilium v1.15.9/go.mod h1:c7Pk1BmmZBUdYYxBGc/T4mLOocxmXx6K6idB7Iw012c= +github.com/cilium/ebpf v0.12.3 h1:8ht6F9MquybnY97at+VDZb3eQQr8ev79RueWeVaEcG4= +github.com/cilium/ebpf v0.12.3/go.mod h1:TctK1ivibvI3znr66ljgi4hqOT8EYQjz1KWBfb1UVgM= +github.com/cilium/proxy v0.0.0-20231031145409-f19708f3d018 h1:R/QlThqx099hS6req1k2Q87fvLSRgCEicQGate9vxO4= +github.com/cilium/proxy v0.0.0-20231031145409-f19708f3d018/go.mod h1:p044XccCmONGIUbx3bJ7qvHXK0RcrdvIvbTGiu/RjUA= +github.com/cncf/xds/go v0.0.0-20240723142845-024c85f92f20 h1:N+3sFI5GUjRKBi+i0TxYVST9h4Ie192jJWpHvthBBgg= +github.com/cncf/xds/go v0.0.0-20240723142845-024c85f92f20/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/coreos/go-oidc/v3 v3.11.0 h1:Ia3MxdwpSw702YW0xgfmP1GVCMA9aEFWu12XUZ3/OtI= github.com/coreos/go-oidc/v3 v3.11.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0= github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= @@ -23,6 +39,7 @@ github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03V github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -35,12 +52,16 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= -github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM= +github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4= +github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI= +github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= @@ -54,6 +75,9 @@ 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/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU= github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo= github.com/go-openapi/errors v0.22.0 h1:c4xY/OLxUBSTiepAg3j/MHuAv5mJhnf53LLMWFB+u/w= @@ -94,20 +118,23 @@ github.com/google/cel-go v0.17.8 h1:j9m730pMZt1Fc4oKhCLUHfjj6527LuhYcYw0Rl8gqto= github.com/google/cel-go v0.17.8/go.mod h1:HXZKzB0LXqer5lHHgfWAnlYwJaQBDKMjxjulNQzhwhY= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= +github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM= github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= 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/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= @@ -116,6 +143,10 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -148,6 +179,11 @@ github.com/lestrrat-go/jwx/v2 v2.1.0 h1:0zs7Ya6+39qoit7gwAf+cYm1zzgS3fceIdo7RmQ5 github.com/lestrrat-go/jwx/v2 v2.1.0/go.mod h1:Xpw9QIaUGiIUD1Wx0NcY1sIHwFf8lDuZn/cmxtXYRys= github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed h1:036IscGBfJsFIgJQzlui7nK1Ncm0tp2ktmPj8xO4N/0= +github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/metal-stack/metal-go v0.37.2 h1:SDIuV43y09kmwtHfsReOZoZ7c2F+lNP4iIhazfJL5tQ= @@ -171,17 +207,26 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA= github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= -github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= -github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A= +github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ= +github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b h1:0LFwY6Q3gMACTjAbMZBjXAqTOzOwFaj2Ld6cjeQ7Rig= +github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= 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= @@ -190,19 +235,38 @@ github.com/prometheus/common v0.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJN github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= 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/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0= +github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= +github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= +github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= +github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= +github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= 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/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -212,29 +276,46 @@ github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 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.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4= +github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4= +github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY= github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE= github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk= +github.com/vishvananda/netlink v1.2.1-beta.2.0.20240524165444-4d4ba1473f21 h1:tcHUxOT8j/R+0S+A1j8D2InqguXFNxAiij+8QFOlX7Y= +github.com/vishvananda/netlink v1.2.1-beta.2.0.20240524165444-4d4ba1473f21/go.mod h1:whJevzBpTrid75eZy99s3DqCmy05NfibNaF2Ol5Ox5A= +github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= +github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= +github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= +github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= -go.etcd.io/etcd/api/v3 v3.5.10 h1:szRajuUUbLyppkhs9K6BRtjY37l66XQQmw7oZRANE4k= -go.etcd.io/etcd/api/v3 v3.5.10/go.mod h1:TidfmT4Uycad3NM/o25fG3J07odo4GBB9hoxaodFCtI= -go.etcd.io/etcd/client/pkg/v3 v3.5.10 h1:kfYIdQftBnbAq8pUWFXfpuuxFSKzlmM5cSn76JByiT0= -go.etcd.io/etcd/client/pkg/v3 v3.5.10/go.mod h1:DYivfIviIuQ8+/lCq4vcxuseg2P2XbHygkKwFo9fc8U= -go.etcd.io/etcd/client/v2 v2.305.10 h1:MrmRktzv/XF8CvtQt+P6wLUlURaNpSDJHFZhe//2QE4= -go.etcd.io/etcd/client/v2 v2.305.10/go.mod h1:m3CKZi69HzilhVqtPDcjhSGp+kA1OmbNn0qamH80xjA= -go.etcd.io/etcd/client/v3 v3.5.10 h1:W9TXNZ+oB3MCd/8UjxHTWK5J9Nquw9fQBLJd5ne5/Ao= -go.etcd.io/etcd/client/v3 v3.5.10/go.mod h1:RVeBnDz2PUEZqTpgqwAtUd8nAPf5kjyFyND7P1VkOKc= +go.etcd.io/etcd/api/v3 v3.5.12 h1:W4sw5ZoU2Juc9gBWuLk5U6fHfNVyY1WC5g9uiXZio/c= +go.etcd.io/etcd/api/v3 v3.5.12/go.mod h1:Ot+o0SWSyT6uHhA56al1oCED0JImsRiU9Dc26+C2a+4= +go.etcd.io/etcd/client/pkg/v3 v3.5.12 h1:EYDL6pWwyOsylrQyLp2w+HkQ46ATiOvoEdMarindU2A= +go.etcd.io/etcd/client/pkg/v3 v3.5.12/go.mod h1:seTzl2d9APP8R5Y2hFL3NVlD6qC/dOT+3kvrqPyTas4= +go.etcd.io/etcd/client/v2 v2.305.12 h1:0m4ovXYo1CHaA/Mp3X/Fak5sRNIWf01wk/X1/G3sGKI= +go.etcd.io/etcd/client/v2 v2.305.12/go.mod h1:aQ/yhsxMu+Oht1FOupSr60oBvcS9cKXHrzBpDsPTf9E= +go.etcd.io/etcd/client/v3 v3.5.12 h1:v5lCPXn1pf1Uu3M4laUE2hp/geOTc5uPcYYsNe1lDxg= +go.etcd.io/etcd/client/v3 v3.5.12/go.mod h1:tSbBCakoWmmddL+BKVAJHa9km+O/E+bumDe9mSbPiqw= go.etcd.io/etcd/pkg/v3 v3.5.10 h1:WPR8K0e9kWl1gAhB5A7gEa5ZBTNkT9NdNWrR8Qpo1CM= go.etcd.io/etcd/pkg/v3 v3.5.10/go.mod h1:TKTuCKKcF1zxmfKWDkfz5qqYaE3JncKKZPFf8c1nFUs= go.etcd.io/etcd/raft/v3 v3.5.10 h1:cgNAYe7xrsrn/5kXMSaH8kM/Ky8mAdMqGOxyYwpP0LA= @@ -261,6 +342,8 @@ go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HY go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= +go.uber.org/dig v1.17.1 h1:Tga8Lz8PcYNsWsyHMZ1Vm0OQOUaJNDyvPImgbAu9YSc= +go.uber.org/dig v1.17.1/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= @@ -269,6 +352,8 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.universe.tf/metallb v0.14.8 h1:MBW6lLJqRgu32s7httfceh1vw8h9qp26ZkI4WaT35zI= go.universe.tf/metallb v0.14.8/go.mod h1:F08YRmgnuiMyvZ8/21dYnh11CFFmYimnm9vafWyvyGs= +go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= +go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -276,6 +361,8 @@ golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY= golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -293,8 +380,17 @@ golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= @@ -307,6 +403,7 @@ golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= golang.org/x/time v0.7.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.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= @@ -330,6 +427,8 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/metal/cloud.go b/metal/cloud.go index 15d7594..ca866ed 100644 --- a/metal/cloud.go +++ b/metal/cloud.go @@ -12,6 +12,8 @@ import ( "github.com/metal-stack/metal-ccm/pkg/controllers/housekeeping" "github.com/metal-stack/metal-ccm/pkg/controllers/instances" "github.com/metal-stack/metal-ccm/pkg/controllers/loadbalancer" + "github.com/metal-stack/metal-ccm/pkg/controllers/loadbalancer/cilium" + "github.com/metal-stack/metal-ccm/pkg/controllers/loadbalancer/metallb" "github.com/metal-stack/metal-ccm/pkg/controllers/zones" "github.com/metal-stack/metal-ccm/pkg/resources/constants" "github.com/metal-stack/metal-ccm/pkg/resources/metal" @@ -23,6 +25,7 @@ import ( "k8s.io/klog/v2" "sigs.k8s.io/controller-runtime/pkg/client" + ciliumv2alpha1 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1" metallbv1beta1 "go.universe.tf/metallb/api/v1beta1" metallbv1beta2 "go.universe.tf/metallb/api/v1beta2" ) @@ -112,6 +115,7 @@ func (c *cloud) Initialize(clientBuilder cloudprovider.ControllerClientBuilder, projectID := os.Getenv(constants.MetalProjectIDEnvVar) sshPublicKey := os.Getenv(constants.MetalSSHPublicKey) clusterID := os.Getenv(constants.MetalClusterIDEnvVar) + loadbalancerType := os.Getenv(constants.Loadbalancer) k8sClientSet := clientBuilder.ClientOrDie("cloud-controller-manager") k8sRestConfig, err := clientBuilder.Config("cloud-controller-manager") @@ -127,11 +131,25 @@ func (c *cloud) Initialize(clientBuilder cloudprovider.ControllerClientBuilder, if err != nil { klog.Fatalf("unable to add metallb v1beta2 to scheme: %v", err) } + err = ciliumv2alpha1.AddToScheme(scheme) + if err != nil { + klog.Fatalf("unable to add cilium v2alpha1 to scheme: %v", err) + } k8sClient, err := client.New(k8sRestConfig, client.Options{Scheme: scheme}) if err != nil { klog.Fatalf("unable to create k8s client: %v", err) } + var config loadbalancer.LoadBalancerConfig + switch loadbalancerType { + case "metallb": + config = metallb.NewMetalLBConfig() + case "cilium": + config = cilium.NewCiliumConfig(k8sClientSet) + default: + config = metallb.NewMetalLBConfig() + } + housekeeper := housekeeping.New(metalclient, stop, c.loadBalancer, k8sClientSet, projectID, sshPublicKey, clusterID) ms := metal.New(metalclient, k8sClientSet, projectID) @@ -139,6 +157,7 @@ func (c *cloud) Initialize(clientBuilder cloudprovider.ControllerClientBuilder, c.loadBalancer.K8sClientSet = k8sClientSet c.loadBalancer.K8sClient = k8sClient c.loadBalancer.MetalService = ms + c.loadBalancer.Config = config c.zones.MetalService = ms go housekeeper.Run() diff --git a/pkg/controllers/housekeeping/housekeeper.go b/pkg/controllers/housekeeping/housekeeper.go index 5a3215e..209a64c 100644 --- a/pkg/controllers/housekeeping/housekeeper.go +++ b/pkg/controllers/housekeeping/housekeeper.go @@ -16,19 +16,19 @@ import ( "github.com/metal-stack/metal-ccm/pkg/resources/metal" ) -// Housekeeper periodically updates nodes, loadbalancers and metallb +// Housekeeper periodically updates nodes and load balancers type Housekeeper struct { - client metalgo.Client - stop <-chan struct{} - k8sClient clientset.Interface - ticker *tickerSyncer - lbController *loadbalancer.LoadBalancerController - lastTagSync time.Time - lastMetalLBConfigSync time.Time - metalAPIErrors int32 - ms *metal.MetalService - sshPublicKey string - clusterID string + client metalgo.Client + stop <-chan struct{} + k8sClient clientset.Interface + ticker *tickerSyncer + lbController *loadbalancer.LoadBalancerController + lastTagSync time.Time + lastLoadBalancerConfigSync time.Time + metalAPIErrors int32 + ms *metal.MetalService + sshPublicKey string + clusterID string } // New returns a new house keeper @@ -48,7 +48,7 @@ func New(metalClient metalgo.Client, stop <-chan struct{}, lbController *loadbal // Run runs the housekeeper... func (h *Housekeeper) Run() { h.startTagSynching() - h.startMetalLBConfigSynching() + h.startLoadBalancerConfigSynching() h.startSSHKeysSynching() h.watchNodes() h.runHealthCheck() @@ -85,20 +85,20 @@ func (h *Housekeeper) watchNodes() { return } if oldTunnelAddress == newTunnelAddress { - // node was not modified and ip address has not changed, not updating metallb config + // node was not modified and ip address has not changed, not updating load balancer config return } - klog.Info("node was modified and ip address has changed, updating metallb config") + klog.Info("node was modified and ip address has changed, updating load balancer config") nodes, err := kubernetes.GetNodes(context.Background(), h.k8sClient) if err != nil { klog.Errorf("error listing nodes: %v", err) return } - err = h.lbController.UpdateMetalLBConfig(context.Background(), nodes) + err = h.lbController.UpdateLoadBalancerConfig(context.Background(), nodes) if err != nil { - klog.Errorf("error updating metallb config: %v", err) + klog.Errorf("error updating load balancer config: %v", err) } }, }, diff --git a/pkg/controllers/housekeeping/loadbalancer.go b/pkg/controllers/housekeeping/loadbalancer.go new file mode 100644 index 0000000..c589b53 --- /dev/null +++ b/pkg/controllers/housekeeping/loadbalancer.go @@ -0,0 +1,34 @@ +package housekeeping + +import ( + "context" + "fmt" + "time" + + "github.com/metal-stack/metal-ccm/pkg/resources/kubernetes" +) + +const ( + syncLoadBalancerInterval = 1 * time.Minute + syncLoadBalancerMinimalInternval = 5 * time.Second +) + +func (h *Housekeeper) startLoadBalancerConfigSynching() { + go h.ticker.Start("load balancer syncher", syncLoadBalancerInterval, h.stop, h.updateLoadBalancerConfig) +} + +func (h *Housekeeper) updateLoadBalancerConfig() error { + if time.Since(h.lastLoadBalancerConfigSync) < syncLoadBalancerMinimalInternval { + return nil + } + nodes, err := kubernetes.GetNodes(context.Background(), h.k8sClient) + if err != nil { + return fmt.Errorf("error listing nodes: %w", err) + } + err = h.lbController.UpdateLoadBalancerConfig(context.Background(), nodes) + if err != nil { + return fmt.Errorf("error updating load balancer config: %w", err) + } + h.lastLoadBalancerConfigSync = time.Now() + return nil +} diff --git a/pkg/controllers/housekeeping/metallb.go b/pkg/controllers/housekeeping/metallb.go deleted file mode 100644 index e5f2b12..0000000 --- a/pkg/controllers/housekeeping/metallb.go +++ /dev/null @@ -1,34 +0,0 @@ -package housekeeping - -import ( - "context" - "fmt" - "time" - - "github.com/metal-stack/metal-ccm/pkg/resources/kubernetes" -) - -const ( - syncMetalLBInterval = 1 * time.Minute - syncMetalLBMinimalInternval = 5 * time.Second -) - -func (h *Housekeeper) startMetalLBConfigSynching() { - go h.ticker.Start("metallb syncher", syncMetalLBInterval, h.stop, h.updateMetalLBConfig) -} - -func (h *Housekeeper) updateMetalLBConfig() error { - if time.Since(h.lastMetalLBConfigSync) < syncMetalLBMinimalInternval { - return nil - } - nodes, err := kubernetes.GetNodes(context.Background(), h.k8sClient) - if err != nil { - return fmt.Errorf("error listing nodes: %w", err) - } - err = h.lbController.UpdateMetalLBConfig(context.Background(), nodes) - if err != nil { - return fmt.Errorf("error updating metallb config: %w", err) - } - h.lastMetalLBConfigSync = time.Now() - return nil -} diff --git a/pkg/controllers/loadbalancer/addresspool.go b/pkg/controllers/loadbalancer/addresspool.go index 0c63c1a..bfe79f6 100644 --- a/pkg/controllers/loadbalancer/addresspool.go +++ b/pkg/controllers/loadbalancer/addresspool.go @@ -35,7 +35,7 @@ func (pool *AddressPool) containsCIDR(cidr string) bool { return false } -func (pool *AddressPool) appendIP(ip string) error { +func (pool *AddressPool) AppendIP(ip string) error { parsed, err := netip.ParseAddr(ip) if err != nil { return err diff --git a/pkg/controllers/loadbalancer/cilium/config.go b/pkg/controllers/loadbalancer/cilium/config.go new file mode 100644 index 0000000..ed0fca3 --- /dev/null +++ b/pkg/controllers/loadbalancer/cilium/config.go @@ -0,0 +1,257 @@ +package cilium + +import ( + "context" + "fmt" + "time" + + "github.com/metal-stack/metal-ccm/pkg/controllers/loadbalancer" + "github.com/metal-stack/metal-ccm/pkg/resources/kubernetes" + "github.com/metal-stack/metal-lib/pkg/pointer" + + slimv1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/apimachinery/pkg/util/wait" + clientset "k8s.io/client-go/kubernetes" + "k8s.io/klog/v2" + + "github.com/metal-stack/metal-go/api/models" + + ciliumv2alpha1 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" +) + +type ciliumConfig struct { + loadbalancer.Config + Peers []*loadbalancer.Peer `json:"peers,omitempty" yaml:"peers,omitempty"` + k8sClient clientset.Interface +} + +func NewCiliumConfig(k8sClient clientset.Interface) *ciliumConfig { + return &ciliumConfig{k8sClient: k8sClient} +} + +func (cfg *ciliumConfig) PrepareConfig(ips []*models.V1IPResponse, nws sets.Set[string], nodes []v1.Node) error { + err := cfg.ComputeAddressPools(ips, nws) + if err != nil { + return err + } + err = cfg.computePeers(nodes) + if err != nil { + return err + } + return nil +} + +func (cfg *ciliumConfig) computePeers(nodes []v1.Node) error { + cfg.Peers = []*loadbalancer.Peer{} // we want an empty array of peers and not nil if there are no nodes + for _, n := range nodes { + asn, err := cfg.GetASNFromNodeLabels(n) + if err != nil { + return err + } + + peer, err := loadbalancer.NewPeer(n, asn) + if err != nil { + klog.Warningf("skipping peer: %v", err) + continue + } + + cfg.Peers = append(cfg.Peers, peer) + } + return nil +} + +func (cfg *ciliumConfig) WriteCRs(ctx context.Context, c client.Client) error { + err := cfg.writeCiliumBGPPeeringPolicies(ctx, c) + if err != nil { + return fmt.Errorf("failed to write ciliumbgppeeringpolicy resources %w", err) + } + + err = cfg.writeCiliumLoadBalancerIPPools(ctx, c) + if err != nil { + return fmt.Errorf("failed to write ciliumloadbalancerippool resources %w", err) + } + + err = cfg.writeNodeAnnotations(ctx) + if err != nil { + return fmt.Errorf("failed to write node annotations %w", err) + } + + return nil +} + +func (cfg *ciliumConfig) writeCiliumBGPPeeringPolicies(ctx context.Context, c client.Client) error { + existingPolicies := ciliumv2alpha1.CiliumBGPPeeringPolicyList{} + err := c.List(ctx, &existingPolicies) + if err != nil { + return err + } + for _, existingPolicy := range existingPolicies.Items { + existingPolicy := existingPolicy + found := false + for _, peer := range cfg.Peers { + if fmt.Sprintf("%d", peer.ASN) == existingPolicy.Name { + found = true + break + } + } + if !found { + err := c.Delete(ctx, &existingPolicy) + if err != nil { + return err + } + } + } + + for _, peer := range cfg.Peers { + bgpPeeringPolicy := &ciliumv2alpha1.CiliumBGPPeeringPolicy{ + TypeMeta: metav1.TypeMeta{ + APIVersion: ciliumv2alpha1.CustomResourceDefinitionGroup + "/" + ciliumv2alpha1.CustomResourceDefinitionVersion, + Kind: ciliumv2alpha1.BGPPKindDefinition, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%d", peer.ASN), + }, + } + res, err := controllerutil.CreateOrUpdate(ctx, c, bgpPeeringPolicy, func() error { + bgpPeeringPolicy.Spec = ciliumv2alpha1.CiliumBGPPeeringPolicySpec{ + NodeSelector: convertNodeSelector(&peer.NodeSelector), + VirtualRouters: []ciliumv2alpha1.CiliumBGPVirtualRouter{ + { + LocalASN: int64(peer.MyASN), + ExportPodCIDR: pointer.Pointer(true), + Neighbors: []ciliumv2alpha1.CiliumBGPNeighbor{ + { + PeerAddress: "127.0.0.1/32", + PeerASN: int64(peer.ASN), + GracefulRestart: &ciliumv2alpha1.CiliumBGPNeighborGracefulRestart{Enabled: true}, + }, + }, + // A NotIn match expression with a dummy key and value have to be used to announce ALL services. + ServiceSelector: pointer.Pointer(slimv1.LabelSelector{ + MatchExpressions: []slimv1.LabelSelectorRequirement{ + { + Key: ciliumv2alpha1.BGPLoadBalancerClass, + Operator: slimv1.LabelSelectorOpNotIn, + Values: []string{"ignore"}, + }, + }, + }), + }, + }, + } + return nil + }) + if err != nil { + return err + } + if res != controllerutil.OperationResultNone { + klog.Infof("bgppeer: %v", res) + } + } + + return nil +} + +func (cfg *ciliumConfig) writeCiliumLoadBalancerIPPools(ctx context.Context, c client.Client) error { + existingPools := ciliumv2alpha1.CiliumLoadBalancerIPPoolList{} + err := c.List(ctx, &existingPools) + if err != nil { + return err + } + for _, existingPool := range existingPools.Items { + existingPool := existingPool + found := false + for _, pool := range cfg.AddressPools { + if pool.Name == existingPool.Name { + found = true + break + } + } + if !found { + err := c.Delete(ctx, &existingPool) + if err != nil { + return err + } + } + } + + for _, pool := range cfg.AddressPools { + ipPool := &ciliumv2alpha1.CiliumLoadBalancerIPPool{ + TypeMeta: metav1.TypeMeta{ + APIVersion: ciliumv2alpha1.CustomResourceDefinitionGroup + "/" + ciliumv2alpha1.CustomResourceDefinitionVersion, + Kind: ciliumv2alpha1.PoolKindDefinition, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: pool.Name, + }, + } + res, err := controllerutil.CreateOrUpdate(ctx, c, ipPool, func() error { + cidrs := make([]ciliumv2alpha1.CiliumLoadBalancerIPPoolIPBlock, 0) + for _, cidr := range pool.CIDRs { + ipPoolBlock := ciliumv2alpha1.CiliumLoadBalancerIPPoolIPBlock{ + Cidr: ciliumv2alpha1.IPv4orIPv6CIDR(cidr), + } + cidrs = append(cidrs, ipPoolBlock) + } + ipPool.Spec = ciliumv2alpha1.CiliumLoadBalancerIPPoolSpec{ + Blocks: cidrs, + } + return nil + }) + if err != nil { + return err + } + if res != controllerutil.OperationResultNone { + klog.Infof("ipaddresspool: %v", res) + } + } + + return nil +} + +func (cfg *ciliumConfig) writeNodeAnnotations(ctx context.Context) error { + nodes, err := kubernetes.GetNodes(ctx, cfg.k8sClient) + if err != nil { + return fmt.Errorf("failed to write node annotations: %w", err) + } + backoff := wait.Backoff{ + Steps: 20, + Duration: 50 * time.Millisecond, + Jitter: 1.0, + } + for _, n := range nodes { + asn, err := cfg.GetASNFromNodeLabels(n) + if err != nil { + return fmt.Errorf("failed to write node annotations for node %s: %w", n.Name, err) + } + annotations := map[string]string{ + fmt.Sprintf("cilium.io/bgp-virtual-router.%d", asn): "router-id=127.0.0.1", + } + err = kubernetes.UpdateNodeAnnotationsWithBackoff(ctx, cfg.k8sClient, n.Name, annotations, backoff) + if err != nil { + return fmt.Errorf("failed to write node annotations for node %s: %w", n.Name, err) + } + } + + return nil +} + +func convertNodeSelector(s *metav1.LabelSelector) *slimv1.LabelSelector { + var machExpressions []slimv1.LabelSelectorRequirement + for _, me := range s.MatchExpressions { + machExpressions = append(machExpressions, slimv1.LabelSelectorRequirement{ + Key: me.Key, + Operator: slimv1.LabelSelectorOperator(me.Operator), + Values: me.Values, + }) + } + return &slimv1.LabelSelector{ + MatchExpressions: machExpressions, + } +} diff --git a/pkg/controllers/loadbalancer/metallb_test.go b/pkg/controllers/loadbalancer/cilium/config_test.go similarity index 65% rename from pkg/controllers/loadbalancer/metallb_test.go rename to pkg/controllers/loadbalancer/cilium/config_test.go index e649c03..342fa6c 100644 --- a/pkg/controllers/loadbalancer/metallb_test.go +++ b/pkg/controllers/loadbalancer/cilium/config_test.go @@ -1,17 +1,17 @@ -package loadbalancer +package cilium import ( "fmt" "testing" "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/metal-stack/metal-ccm/pkg/controllers/loadbalancer" "github.com/metal-stack/metal-go/api/models" "github.com/metal-stack/metal-lib/pkg/pointer" "github.com/metal-stack/metal-lib/pkg/tag" - "github.com/stretchr/testify/require" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/util/sets" - "sigs.k8s.io/yaml" ) var ( @@ -23,14 +23,14 @@ var ( ) ) -func TestMetalLBConfig_CalculateConfig(t *testing.T) { +func TestCiliumConfig_PrepareConfig(t *testing.T) { tests := []struct { name string nws sets.Set[string] ips []*models.V1IPResponse nodes []v1.Node wantErr error - want map[string]interface{} + want *ciliumConfig }{ { name: "one ip acquired, no nodes", @@ -49,17 +49,20 @@ func TestMetalLBConfig_CalculateConfig(t *testing.T) { }, nodes: []v1.Node{}, wantErr: nil, - want: map[string]interface{}{ - "address-pools": []map[string]interface{}{ - { - "addresses": []string{ - "84.1.1.1/32", + want: &ciliumConfig{ + Config: loadbalancer.Config{ + AddressPools: []*loadbalancer.AddressPool{ + { + Name: "internet-ephemeral", + Protocol: "bgp", + AutoAssign: pointer.Pointer(false), + CIDRs: []string{ + "84.1.1.1/32", + }, }, - "auto-assign": false, - "name": "internet-ephemeral", - "protocol": "bgp", }, }, + Peers: []*loadbalancer.Peer{}, }, }, { @@ -89,18 +92,21 @@ func TestMetalLBConfig_CalculateConfig(t *testing.T) { }, nodes: []v1.Node{}, wantErr: nil, - want: map[string]interface{}{ - "address-pools": []map[string]interface{}{ - { - "addresses": []string{ - "84.1.1.1/32", - "84.1.1.2/32", + want: &ciliumConfig{ + Config: loadbalancer.Config{ + AddressPools: []*loadbalancer.AddressPool{ + { + Name: "internet-ephemeral", + Protocol: "bgp", + AutoAssign: pointer.Pointer(false), + CIDRs: []string{ + "84.1.1.1/32", + "84.1.1.2/32", + }, }, - "auto-assign": false, - "name": "internet-ephemeral", - "protocol": "bgp", }, }, + Peers: []*loadbalancer.Peer{}, }, }, { @@ -140,29 +146,31 @@ func TestMetalLBConfig_CalculateConfig(t *testing.T) { }, nodes: []v1.Node{}, wantErr: nil, - want: map[string]interface{}{ - "address-pools": []map[string]interface{}{ - { - "addresses": []string{ - "84.1.1.1/32", - "84.1.1.2/32", + want: &ciliumConfig{ + Config: loadbalancer.Config{ + AddressPools: []*loadbalancer.AddressPool{ + { + Name: "internet-ephemeral", + Protocol: "bgp", + AutoAssign: pointer.Pointer(false), + CIDRs: []string{ + "84.1.1.1/32", + "84.1.1.2/32", + }, }, - "auto-assign": false, - "name": "internet-ephemeral", - "protocol": "bgp", - }, - { - "addresses": []string{ - "84.1.1.3/32", + { + Name: "internet-static", + Protocol: "bgp", + AutoAssign: pointer.Pointer(false), + CIDRs: []string{ + "84.1.1.3/32", + }, }, - "auto-assign": false, - "name": "internet-static", - "protocol": "bgp", }, }, + Peers: []*loadbalancer.Peer{}, }, }, - { name: "connected to internet,storage,dmz and mpls, two ips acquired, one static ip, no nodes", nws: testNetworks, @@ -240,83 +248,77 @@ func TestMetalLBConfig_CalculateConfig(t *testing.T) { }, nodes: []v1.Node{}, wantErr: nil, - want: map[string]interface{}{ - "address-pools": []map[string]interface{}{ - { - "addresses": []string{ - "84.1.1.1/32", - "84.1.1.2/32", + want: &ciliumConfig{ + Config: loadbalancer.Config{ + AddressPools: []*loadbalancer.AddressPool{ + { + Name: "internet-ephemeral", + Protocol: "bgp", + AutoAssign: pointer.Pointer(false), + CIDRs: []string{ + "84.1.1.1/32", + "84.1.1.2/32", + }, }, - "auto-assign": false, - "name": "internet-ephemeral", - "protocol": "bgp", - }, - { - "addresses": []string{ - "84.1.1.3/32", + { + Name: "internet-static", + Protocol: "bgp", + AutoAssign: pointer.Pointer(false), + CIDRs: []string{ + "84.1.1.3/32", + }, }, - "auto-assign": false, - "name": "internet-static", - "protocol": "bgp", - }, - { - "addresses": []string{ - "10.131.44.2/32", + { + Name: "shared-storage-network-static", + Protocol: "bgp", + AutoAssign: pointer.Pointer(false), + CIDRs: []string{ + "10.131.44.2/32", + }, }, - "auto-assign": false, - "name": "shared-storage-network-static", - "protocol": "bgp", - }, - { - "addresses": []string{ - "100.127.130.2/32", + { + Name: "mpls-network-static", + Protocol: "bgp", + AutoAssign: pointer.Pointer(false), + CIDRs: []string{ + "100.127.130.2/32", + }, }, - "auto-assign": false, - "name": "mpls-network-static", - "protocol": "bgp", - }, - { - "addresses": []string{ - "100.127.130.3/32", + { + Name: "mpls-network-ephemeral", + Protocol: "bgp", + AutoAssign: pointer.Pointer(false), + CIDRs: []string{ + "100.127.130.3/32", + }, }, - "auto-assign": false, - "name": "mpls-network-ephemeral", - "protocol": "bgp", - }, - { - "addresses": []string{ - "10.129.172.2/32", + { + Name: "dmz-network-static", + Protocol: "bgp", + AutoAssign: pointer.Pointer(false), + CIDRs: []string{ + "10.129.172.2/32", + }, }, - "auto-assign": false, - "name": "dmz-network-static", - "protocol": "bgp", }, }, - }, - }, + Peers: []*loadbalancer.Peer{}, + }}, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { - cfg := &MetalLBConfig{} + cfg := &ciliumConfig{} - err := cfg.CalculateConfig(tt.ips, tt.nws, tt.nodes) + err := cfg.PrepareConfig(tt.ips, tt.nws, tt.nodes) if diff := cmp.Diff(err, tt.wantErr); diff != "" { - t.Errorf("MetalLBConfig.CalculateConfig() error = %v", diff) + t.Errorf("CiliumConfig.CalculateConfig() error = %v", diff) return } - yaml, err := cfg.ToYAML() - require.NoError(t, err) - - if diff := cmp.Diff(yaml, mustYAML(tt.want)); diff != "" { - t.Errorf("MetalLBConfig.CalculateConfig() = %v", diff) + if diff := cmp.Diff(cfg, tt.want, cmpopts.IgnoreUnexported(ciliumConfig{})); diff != "" { + t.Errorf("CiliumConfig.CalculateConfig() = %v", diff) } }) } } - -func mustYAML(data interface{}) string { - res, _ := yaml.Marshal(data) - return string(res) -} diff --git a/pkg/controllers/loadbalancer/config.go b/pkg/controllers/loadbalancer/config.go new file mode 100644 index 0000000..840f24a --- /dev/null +++ b/pkg/controllers/loadbalancer/config.go @@ -0,0 +1,85 @@ +package loadbalancer + +import ( + "context" + "errors" + "fmt" + "strconv" + "strings" + + "github.com/metal-stack/metal-go/api/models" + "github.com/metal-stack/metal-lib/pkg/tag" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/klog/v2" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +type LoadBalancerConfig interface { + PrepareConfig(ips []*models.V1IPResponse, nws sets.Set[string], nodes []v1.Node) error + WriteCRs(ctx context.Context, c client.Client) error +} + +type Config struct { + AddressPools []*AddressPool `json:"address-pools,omitempty" yaml:"address-pools,omitempty"` +} + +func (cfg *Config) ComputeAddressPools(ips []*models.V1IPResponse, nws sets.Set[string]) error { + var errs []error + for _, ip := range ips { + if !nws.Has(*ip.Networkid) { + klog.Infof("skipping ip %q: not part of cluster networks", *ip.Ipaddress) + continue + } + net := *ip.Networkid + err := cfg.addIPToPool(net, *ip) + if err != nil { + errs = append(errs, err) + } + } + if len(errs) > 0 { + return errors.Join(errs...) + } + return nil +} + +func (cfg *Config) addIPToPool(network string, ip models.V1IPResponse) error { + t := ip.Type + poolType := models.V1IPBaseTypeEphemeral + if t != nil && *t == models.V1IPBaseTypeStatic { + poolType = models.V1IPBaseTypeStatic + } + poolName := fmt.Sprintf("%s-%s", strings.ToLower(network), poolType) + pool := cfg.getOrCreateAddressPool(poolName) + err := pool.AppendIP(*ip.Ipaddress) + if err != nil { + return err + } + return nil +} + +func (cfg *Config) getOrCreateAddressPool(poolName string) *AddressPool { + for _, pool := range cfg.AddressPools { + if pool.Name == poolName { + return pool + } + } + + pool := NewBGPAddressPool(poolName) + cfg.AddressPools = append(cfg.AddressPools, pool) + + return pool +} + +func (cfg *Config) GetASNFromNodeLabels(node v1.Node) (int64, error) { + labels := node.GetLabels() + asnString, ok := labels[tag.MachineNetworkPrimaryASN] + if !ok { + return 0, fmt.Errorf("node %q misses label: %s", node.GetName(), tag.MachineNetworkPrimaryASN) + } + asn, err := strconv.ParseInt(asnString, 10, 64) + if err != nil { + return 0, fmt.Errorf("unable to parse valid integer from asn annotation: %w", err) + } + return asn, nil +} diff --git a/pkg/controllers/loadbalancer/loadbalancer.go b/pkg/controllers/loadbalancer/loadbalancer.go index 22679d4..43039c5 100644 --- a/pkg/controllers/loadbalancer/loadbalancer.go +++ b/pkg/controllers/loadbalancer/loadbalancer.go @@ -30,6 +30,7 @@ import ( ) type LoadBalancerController struct { + Config LoadBalancerConfig MetalService *metal.MetalService partitionID string projectID string @@ -175,7 +176,7 @@ func (l *LoadBalancerController) EnsureLoadBalancer(ctx context.Context, cluster ingressStatus = append(ingressStatus, v1.LoadBalancerIngress{IP: ip}) - err = l.UpdateMetalLBConfig(ctx, ns) + err = l.UpdateLoadBalancerConfig(ctx, ns) if err != nil { return nil, rollback(err) } @@ -193,7 +194,7 @@ func (l *LoadBalancerController) UpdateLoadBalancer(ctx context.Context, cluster for i := range nodes { ns = append(ns, *nodes[i]) } - return l.UpdateMetalLBConfig(ctx, ns) + return l.UpdateLoadBalancerConfig(ctx, ns) } // EnsureLoadBalancerDeleted deletes the cluster load balancer if it @@ -269,8 +270,8 @@ func (l *LoadBalancerController) removeServiceTag(ip models.V1IPResponse, servic return newTags, len(newTags) == 0 } -// UpdateMetalLBConfig the metallb config for given nodes -func (l *LoadBalancerController) UpdateMetalLBConfig(ctx context.Context, nodes []v1.Node) error { +// UpdateLoadBalancerConfig updates the load balancer config for the given nodes +func (l *LoadBalancerController) UpdateLoadBalancerConfig(ctx context.Context, nodes []v1.Node) error { l.configWriteMutex.Lock() defer l.configWriteMutex.Unlock() @@ -279,7 +280,7 @@ func (l *LoadBalancerController) UpdateMetalLBConfig(ctx context.Context, nodes return err } - klog.Info("metallb config updated successfully") + klog.Info("load balancer config updated successfully") return nil } @@ -338,13 +339,12 @@ func (l *LoadBalancerController) updateLoadBalancerConfig(ctx context.Context, n return fmt.Errorf("could not find ips of this project's cluster: %w", err) } - config := newMetalLBConfig() - err = config.CalculateConfig(ips, l.additionalNetworks, nodes) + err = l.Config.PrepareConfig(ips, l.additionalNetworks, nodes) if err != nil { return err } - err = config.WriteCRs(ctx, l.K8sClient) + err = l.Config.WriteCRs(ctx, l.K8sClient) if err != nil { return err } diff --git a/pkg/controllers/loadbalancer/metallb.go b/pkg/controllers/loadbalancer/metallb/config.go similarity index 54% rename from pkg/controllers/loadbalancer/metallb.go rename to pkg/controllers/loadbalancer/metallb/config.go index 9c5f9aa..b715b54 100644 --- a/pkg/controllers/loadbalancer/metallb.go +++ b/pkg/controllers/loadbalancer/metallb/config.go @@ -1,14 +1,11 @@ -package loadbalancer +package metallb import ( "context" - "errors" "fmt" - "strconv" - "strings" "time" - "github.com/metal-stack/metal-lib/pkg/tag" + "github.com/metal-stack/metal-ccm/pkg/controllers/loadbalancer" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -19,7 +16,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - "sigs.k8s.io/yaml" metallbv1beta1 "go.universe.tf/metallb/api/v1beta1" metallbv1beta2 "go.universe.tf/metallb/api/v1beta2" @@ -29,19 +25,18 @@ const ( metallbNamespace = "metallb-system" ) -// MetalLBConfig is a struct containing a config for metallb -type MetalLBConfig struct { - Peers []*Peer `json:"peers,omitempty" yaml:"peers,omitempty"` - AddressPools []*AddressPool `json:"address-pools,omitempty" yaml:"address-pools,omitempty"` +type metalLBConfig struct { + loadbalancer.Config + Peers []*loadbalancer.Peer `json:"peers,omitempty" yaml:"peers,omitempty"` + namespace string } -func newMetalLBConfig() *MetalLBConfig { - return &MetalLBConfig{} +func NewMetalLBConfig() *metalLBConfig { + return &metalLBConfig{namespace: metallbNamespace} } -// CalculateConfig computes the metallb config from given parameter input. -func (cfg *MetalLBConfig) CalculateConfig(ips []*models.V1IPResponse, nws sets.Set[string], nodes []v1.Node) error { - err := cfg.computeAddressPools(ips, nws) +func (cfg *metalLBConfig) PrepareConfig(ips []*models.V1IPResponse, nws sets.Set[string], nodes []v1.Node) error { + err := cfg.ComputeAddressPools(ips, nws) if err != nil { return err } @@ -52,41 +47,15 @@ func (cfg *MetalLBConfig) CalculateConfig(ips []*models.V1IPResponse, nws sets.S return nil } -func (cfg *MetalLBConfig) computeAddressPools(ips []*models.V1IPResponse, nws sets.Set[string]) error { - var errs []error - for _, ip := range ips { - if !nws.Has(*ip.Networkid) { - klog.Infof("skipping ip %q: not part of cluster networks", *ip.Ipaddress) - continue - } - net := *ip.Networkid - err := cfg.addIPToPool(net, *ip) - if err != nil { - errs = append(errs, err) - } - } - if len(errs) > 0 { - return errors.Join(errs...) - } - return nil -} - -func (cfg *MetalLBConfig) computePeers(nodes []v1.Node) error { - cfg.Peers = []*Peer{} // we want an empty array of peers and not nil if there are no nodes +func (cfg *metalLBConfig) computePeers(nodes []v1.Node) error { + cfg.Peers = []*loadbalancer.Peer{} // we want an empty array of peers and not nil if there are no nodes for _, n := range nodes { - labels := n.GetLabels() - asnString, ok := labels[tag.MachineNetworkPrimaryASN] - if !ok { - return fmt.Errorf("node %q misses label: %s", n.GetName(), tag.MachineNetworkPrimaryASN) - } - asn, err := strconv.ParseInt(asnString, 10, 64) + asn, err := cfg.GetASNFromNodeLabels(n) if err != nil { - return fmt.Errorf("unable to parse valid integer from asn annotation: %w", err) + return err } - // we can safely cast the asn to a uint32 because its max value is defined as such - // see: https://en.wikipedia.org/wiki/Autonomous_system_(Internet) - peer, err := newPeer(n, uint32(asn)) // nolint:gosec + peer, err := loadbalancer.NewPeer(n, asn) if err != nil { klog.Warningf("skipping peer: %v", err) continue @@ -97,52 +66,9 @@ func (cfg *MetalLBConfig) computePeers(nodes []v1.Node) error { return nil } -// getOrCreateAddressPool returns the address pool of the given network. -// It will be created if it does not exist yet. -func (cfg *MetalLBConfig) getOrCreateAddressPool(poolName string) *AddressPool { - for _, pool := range cfg.AddressPools { - if pool.Name == poolName { - return pool - } - } - - pool := NewBGPAddressPool(poolName) - cfg.AddressPools = append(cfg.AddressPools, pool) - - return pool -} - -// announceIPs appends the given IPs to the network address pools. -func (cfg *MetalLBConfig) addIPToPool(network string, ip models.V1IPResponse) error { - t := ip.Type - poolType := models.V1IPBaseTypeEphemeral - if t != nil && *t == models.V1IPBaseTypeStatic { - poolType = models.V1IPBaseTypeStatic - } - poolName := fmt.Sprintf("%s-%s", strings.ToLower(network), poolType) - pool := cfg.getOrCreateAddressPool(poolName) - err := pool.appendIP(*ip.Ipaddress) - if err != nil { - return err - } - return nil -} - -// ToYAML returns this config in YAML format. -func (cfg *MetalLBConfig) ToYAML() (string, error) { - bb, err := yaml.Marshal(cfg) - if err != nil { - return "", err - } - return string(bb), nil -} - -// Write inserts or updates the Metal-LB custom resources. -func (cfg *MetalLBConfig) WriteCRs(ctx context.Context, c client.Client) error { - - // BGPPeers +func (cfg *metalLBConfig) WriteCRs(ctx context.Context, c client.Client) error { bgpPeerList := metallbv1beta2.BGPPeerList{} - err := c.List(ctx, &bgpPeerList, client.InNamespace(metallbNamespace)) + err := c.List(ctx, &bgpPeerList, client.InNamespace(cfg.namespace)) if err != nil { return err } @@ -171,7 +97,7 @@ func (cfg *MetalLBConfig) WriteCRs(ctx context.Context, c client.Client) error { }, ObjectMeta: metav1.ObjectMeta{ Name: fmt.Sprintf("peer-%d", peer.ASN), - Namespace: metallbNamespace, + Namespace: cfg.namespace, }, } res, err := controllerutil.CreateOrUpdate(ctx, c, bgpPeer, func() error { @@ -181,7 +107,7 @@ func (cfg *MetalLBConfig) WriteCRs(ctx context.Context, c client.Client) error { HoldTime: metav1.Duration{Duration: 90 * time.Second}, KeepaliveTime: metav1.Duration{Duration: 0 * time.Second}, Address: peer.Address, - NodeSelectors: peer.NodeSelectors, + NodeSelectors: []metav1.LabelSelector{peer.NodeSelector}, } return nil }) @@ -193,9 +119,8 @@ func (cfg *MetalLBConfig) WriteCRs(ctx context.Context, c client.Client) error { } } - // IPAddressPools addressPoolList := metallbv1beta1.IPAddressPoolList{} - err = c.List(ctx, &addressPoolList, client.InNamespace(metallbNamespace)) + err = c.List(ctx, &addressPoolList, client.InNamespace(cfg.namespace)) if err != nil { return err } @@ -224,7 +149,7 @@ func (cfg *MetalLBConfig) WriteCRs(ctx context.Context, c client.Client) error { }, ObjectMeta: metav1.ObjectMeta{ Name: pool.Name, - Namespace: metallbNamespace, + Namespace: cfg.namespace, }, } res, err := controllerutil.CreateOrUpdate(ctx, c, ipAddressPool, func() error { @@ -242,10 +167,9 @@ func (cfg *MetalLBConfig) WriteCRs(ctx context.Context, c client.Client) error { } } - // BGPAdvertisements for _, pool := range cfg.AddressPools { bgpAdvertisementList := metallbv1beta1.BGPAdvertisementList{} - err = c.List(ctx, &bgpAdvertisementList, client.InNamespace(metallbNamespace)) + err = c.List(ctx, &bgpAdvertisementList, client.InNamespace(cfg.namespace)) if err != nil { return err } @@ -273,7 +197,7 @@ func (cfg *MetalLBConfig) WriteCRs(ctx context.Context, c client.Client) error { }, ObjectMeta: metav1.ObjectMeta{ Name: pool.Name, - Namespace: metallbNamespace, + Namespace: cfg.namespace, }, } res, err := controllerutil.CreateOrUpdate(ctx, c, bgpAdvertisement, func() error { diff --git a/pkg/controllers/loadbalancer/metallb/config_test.go b/pkg/controllers/loadbalancer/metallb/config_test.go new file mode 100644 index 0000000..f3d1239 --- /dev/null +++ b/pkg/controllers/loadbalancer/metallb/config_test.go @@ -0,0 +1,327 @@ +package metallb + +import ( + "fmt" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/metal-stack/metal-ccm/pkg/controllers/loadbalancer" + "github.com/metal-stack/metal-go/api/models" + "github.com/metal-stack/metal-lib/pkg/pointer" + "github.com/metal-stack/metal-lib/pkg/tag" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/sets" +) + +var ( + testNetworks = sets.New( + "internet", + "shared-storage-network", + "mpls-network", + "dmz-network", + ) +) + +func TestMetalLBConfig_CalculateConfig(t *testing.T) { + tests := []struct { + name string + nws sets.Set[string] + ips []*models.V1IPResponse + nodes []v1.Node + wantErr error + want *metalLBConfig + }{ + { + name: "one ip acquired, no nodes", + nws: testNetworks, + ips: []*models.V1IPResponse{ + { + Ipaddress: pointer.Pointer("84.1.1.1"), + Name: "acquired-before", + Networkid: pointer.Pointer("internet"), + Projectid: pointer.Pointer("project-a"), + Tags: []string{ + fmt.Sprintf("%s=%s", tag.ClusterID, "this-cluster"), + }, + Type: pointer.Pointer("ephemeral"), + }, + }, + nodes: []v1.Node{}, + wantErr: nil, + want: &metalLBConfig{ + Config: loadbalancer.Config{ + AddressPools: []*loadbalancer.AddressPool{ + { + Name: "internet-ephemeral", + Protocol: "bgp", + AutoAssign: pointer.Pointer(false), + CIDRs: []string{"84.1.1.1/32"}, + }, + }, + }, + Peers: []*loadbalancer.Peer{}, + namespace: metallbNamespace, + }, + }, + { + name: "two ips acquired, no nodes", + nws: testNetworks, + ips: []*models.V1IPResponse{ + { + Ipaddress: pointer.Pointer("84.1.1.1"), + Name: "acquired-before", + Networkid: pointer.Pointer("internet"), + Projectid: pointer.Pointer("project-a"), + Tags: []string{ + fmt.Sprintf("%s=%s", tag.ClusterID, "this-cluster"), + }, + Type: pointer.Pointer("ephemeral"), + }, + { + Ipaddress: pointer.Pointer("84.1.1.2"), + Name: "acquired-before-2", + Networkid: pointer.Pointer("internet"), + Projectid: pointer.Pointer("project-a"), + Tags: []string{ + fmt.Sprintf("%s=%s", tag.ClusterID, "this-cluster"), + }, + Type: pointer.Pointer("ephemeral"), + }, + }, + nodes: []v1.Node{}, + wantErr: nil, + want: &metalLBConfig{ + Config: loadbalancer.Config{ + AddressPools: []*loadbalancer.AddressPool{ + { + Name: "internet-ephemeral", + Protocol: "bgp", + AutoAssign: pointer.Pointer(false), + CIDRs: []string{ + "84.1.1.1/32", + "84.1.1.2/32", + }, + }, + }, + }, + Peers: []*loadbalancer.Peer{}, + namespace: metallbNamespace, + }, + }, + { + name: "two ips acquired, one static ip, no nodes", + nws: testNetworks, + ips: []*models.V1IPResponse{ + { + Ipaddress: pointer.Pointer("84.1.1.1"), + Name: "acquired-before", + Networkid: pointer.Pointer("internet"), + Projectid: pointer.Pointer("project-a"), + Tags: []string{ + fmt.Sprintf("%s=%s", tag.ClusterID, "this-cluster"), + }, + Type: pointer.Pointer("ephemeral"), + }, + { + Ipaddress: pointer.Pointer("84.1.1.2"), + Name: "acquired-before-2", + Networkid: pointer.Pointer("internet"), + Projectid: pointer.Pointer("project-a"), + Tags: []string{ + fmt.Sprintf("%s=%s", tag.ClusterID, "this-cluster"), + }, + Type: pointer.Pointer("ephemeral"), + }, + { + Ipaddress: pointer.Pointer("84.1.1.3"), + Name: "static-ip", + Networkid: pointer.Pointer("internet"), + Projectid: pointer.Pointer("project-a"), + Tags: []string{ + fmt.Sprintf("%s=%s", tag.ClusterID, "this-cluster"), + }, + Type: pointer.Pointer("static"), + }, + }, + nodes: []v1.Node{}, + wantErr: nil, + want: &metalLBConfig{ + Config: loadbalancer.Config{ + AddressPools: []*loadbalancer.AddressPool{ + { + Name: "internet-ephemeral", + Protocol: "bgp", + AutoAssign: pointer.Pointer(false), + CIDRs: []string{ + "84.1.1.1/32", + "84.1.1.2/32", + }, + }, + { + Name: "internet-static", + Protocol: "bgp", + AutoAssign: pointer.Pointer(false), + CIDRs: []string{ + "84.1.1.3/32", + }, + }, + }, + }, + Peers: []*loadbalancer.Peer{}, + namespace: metallbNamespace, + }, + }, + { + name: "connected to internet,storage,dmz and mpls, two ips acquired, one static ip, no nodes", + nws: testNetworks, + ips: []*models.V1IPResponse{ + { + Ipaddress: pointer.Pointer("84.1.1.1"), + Name: "acquired-before", + Networkid: pointer.Pointer("internet"), + Projectid: pointer.Pointer("project-a"), + Tags: []string{ + fmt.Sprintf("%s=%s", tag.ClusterID, "this-cluster"), + }, + Type: pointer.Pointer("ephemeral"), + }, + { + Ipaddress: pointer.Pointer("84.1.1.2"), + Name: "acquired-before-2", + Networkid: pointer.Pointer("internet"), + Projectid: pointer.Pointer("project-a"), + Tags: []string{ + fmt.Sprintf("%s=%s", tag.ClusterID, "this-cluster"), + }, + Type: pointer.Pointer("ephemeral"), + }, + { + Ipaddress: pointer.Pointer("84.1.1.3"), + Name: "static-ip", + Networkid: pointer.Pointer("internet"), + Projectid: pointer.Pointer("project-a"), + Tags: []string{ + fmt.Sprintf("%s=%s", tag.ClusterID, "this-cluster"), + }, + Type: pointer.Pointer("static"), + }, + { + Ipaddress: pointer.Pointer("10.131.44.2"), + Name: "static-ip", + Networkid: pointer.Pointer("shared-storage-network"), + Projectid: pointer.Pointer("project-a"), + Tags: []string{ + fmt.Sprintf("%s=%s", tag.ClusterID, "this-cluster"), + }, + Type: pointer.Pointer("static"), + }, + { + Ipaddress: pointer.Pointer("100.127.130.2"), + Name: "static-ip", + Networkid: pointer.Pointer("mpls-network"), + Projectid: pointer.Pointer("project-a"), + Tags: []string{ + fmt.Sprintf("%s=%s", tag.ClusterID, "this-cluster"), + }, + Type: pointer.Pointer("static"), + }, + { + Ipaddress: pointer.Pointer("100.127.130.3"), + Name: "ephemeral-mpls-ip", + Networkid: pointer.Pointer("mpls-network"), + Projectid: pointer.Pointer("project-a"), + Tags: []string{ + fmt.Sprintf("%s=%s", tag.ClusterID, "this-cluster"), + }, + Type: pointer.Pointer("ephemeral"), + }, + { + Ipaddress: pointer.Pointer("10.129.172.2"), + Name: "static-ip", + Networkid: pointer.Pointer("dmz-network"), + Projectid: pointer.Pointer("project-a"), + Tags: []string{ + fmt.Sprintf("%s=%s", tag.ClusterID, "this-cluster"), + }, + Type: pointer.Pointer("static"), + }, + }, + nodes: []v1.Node{}, + wantErr: nil, + want: &metalLBConfig{ + Config: loadbalancer.Config{ + AddressPools: []*loadbalancer.AddressPool{ + { + Name: "internet-ephemeral", + Protocol: "bgp", + AutoAssign: pointer.Pointer(false), + CIDRs: []string{ + "84.1.1.1/32", + "84.1.1.2/32", + }, + }, + { + Name: "internet-static", + Protocol: "bgp", + AutoAssign: pointer.Pointer(false), + CIDRs: []string{ + "84.1.1.3/32", + }, + }, + { + Name: "shared-storage-network-static", + Protocol: "bgp", + AutoAssign: pointer.Pointer(false), + CIDRs: []string{ + "10.131.44.2/32", + }, + }, + { + Name: "mpls-network-static", + Protocol: "bgp", + AutoAssign: pointer.Pointer(false), + CIDRs: []string{ + "100.127.130.2/32", + }, + }, + { + Name: "mpls-network-ephemeral", + Protocol: "bgp", + AutoAssign: pointer.Pointer(false), + CIDRs: []string{ + "100.127.130.3/32", + }, + }, + { + Name: "dmz-network-static", + Protocol: "bgp", + AutoAssign: pointer.Pointer(false), + CIDRs: []string{ + "10.129.172.2/32", + }, + }, + }, + }, + Peers: []*loadbalancer.Peer{}, + namespace: metallbNamespace, + }, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + cfg := &metalLBConfig{} + + err := cfg.PrepareConfig(tt.ips, tt.nws, tt.nodes) + if diff := cmp.Diff(err, tt.wantErr); diff != "" { + t.Errorf("metalLBConfig.PrepareConfig() error = %v", diff) + return + } + + if diff := cmp.Diff(cfg, tt.want, cmpopts.IgnoreUnexported(metalLBConfig{})); diff != "" { + t.Errorf("metalLBConfig.PrepareConfig() = %v", diff) + } + }) + } +} diff --git a/pkg/controllers/loadbalancer/peer.go b/pkg/controllers/loadbalancer/peer.go index 38fb989..80705fd 100644 --- a/pkg/controllers/loadbalancer/peer.go +++ b/pkg/controllers/loadbalancer/peer.go @@ -8,13 +8,13 @@ import ( ) type Peer struct { - MyASN uint32 `json:"my-asn" yaml:"my-asn"` - ASN uint32 `json:"peer-asn" yaml:"peer-asn"` - Address string `json:"peer-address" yaml:"peer-address"` - NodeSelectors []metav1.LabelSelector `json:"node-selectors,omitempty" yaml:"node-selectors,omitempty"` + MyASN uint32 `json:"my-asn" yaml:"my-asn"` + ASN uint32 `json:"peer-asn" yaml:"peer-asn"` + Address string `json:"peer-address" yaml:"peer-address"` + NodeSelector metav1.LabelSelector `json:"node-selectors,omitempty" yaml:"node-selectors,omitempty"` } -func newPeer(node v1.Node, asn uint32) (*Peer, error) { +func NewPeer(node v1.Node, asn int64) (*Peer, error) { hostname := node.GetName() matchExpression := metav1.LabelSelectorRequirement{ @@ -29,15 +29,16 @@ func newPeer(node v1.Node, asn uint32) (*Peer, error) { if err != nil { return nil, err } + return &Peer{ - MyASN: asn, - ASN: asn, + // we can safely cast the asn to an uint32 because its max value is defined as such + // see: https://en.wikipedia.org/wiki/Autonomous_system_(Internet) + MyASN: uint32(asn), // nolint:gosec + ASN: uint32(asn), // nolint:gosec Address: address, - NodeSelectors: []metav1.LabelSelector{ - { - MatchExpressions: []metav1.LabelSelectorRequirement{ - matchExpression, - }, + NodeSelector: metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + matchExpression, }, }, }, nil diff --git a/pkg/resources/constants/constants.go b/pkg/resources/constants/constants.go index b817569..32866a0 100644 --- a/pkg/resources/constants/constants.go +++ b/pkg/resources/constants/constants.go @@ -19,4 +19,6 @@ const ( MetalLBSpecificAddressPool = "metallb.universe.tf/address-pool" IPPrefix = "metallb-" + + Loadbalancer = "LOADBALANCER" ) diff --git a/pkg/resources/kubernetes/node.go b/pkg/resources/kubernetes/node.go index 007b95d..3e0a1be 100644 --- a/pkg/resources/kubernetes/node.go +++ b/pkg/resources/kubernetes/node.go @@ -41,6 +41,24 @@ func UpdateNodeLabelsWithBackoff(ctx context.Context, client clientset.Interface }) } +// UpdateNodeAnnotationsWithBackoff updates labels on a given node with a given backoff retry. +func UpdateNodeAnnotationsWithBackoff(ctx context.Context, client clientset.Interface, nodeName string, annotations map[string]string, backoff wait.Backoff) error { + return retry.RetryOnConflict(backoff, func() error { + + node, err := client.CoreV1().Nodes().Get(ctx, nodeName, metav1.GetOptions{}) + if err != nil { + return err + } + + for key, value := range annotations { + node.Annotations[key] = value + } + + _, err = client.CoreV1().Nodes().Update(ctx, node, metav1.UpdateOptions{}) + return err + }) +} + // NodeNamesOfNodes returns the node names of the nodes func NodeNamesOfNodes(nodes []v1.Node) string { var nn []string diff --git a/test/deploy.sh b/test/deploy.sh index 0723b3e..867ee44 100755 --- a/test/deploy.sh +++ b/test/deploy.sh @@ -76,7 +76,7 @@ kubectl describe svc -n kube-system echo echo "kubectl describe cm config -n metallb-system" kubectl describe cm config -n metallb-system -echo "Test echo service via loadbalancer..." +echo "Test echo service via load balancer..." LB_INGRESS_IP=$(kubectl describe svc -n kube-system echo | grep Ingress | cut -d: -f2 | sed -e 's/^[ \t]*//') for i in {1..5}; do echo "docker exec -t kind-control-plane curl ${LB_INGRESS_IP}:8080/echo"