From 4e0b0544fcdec233b750b95b2f65e46adb7b654a Mon Sep 17 00:00:00 2001 From: Khachatur Ashotyan Date: Thu, 30 Apr 2026 17:10:10 +0400 Subject: [PATCH] feat: update core dependencies and operator config boundary Update core to the current tractatus and Kubernetes API module line, remove controller-runtime from the shared core module, and keep controller-runtime wiring in operator repos. Also include Release Please action maintenance plus test/doc fixes for core issues #8, #10, and #11. --- .github/workflows/release-please.yml | 6 +- README.md | 2 +- go.mod | 52 ++------ go.sum | 144 ++++------------------- runtime/operatorconfig/manager.go | 89 ++++++-------- runtime/operatorconfig/manager_test.go | 85 ++++++------- runtime/transport/connector/dial.go | 8 ++ runtime/transport/connector/dial_test.go | 8 +- templating/funcs.go | 2 + templating/funcs_test.go | 100 +++++++++++++++- 10 files changed, 233 insertions(+), 263 deletions(-) diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 288ca31..277eece 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -26,7 +26,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v6 with: - go-version: '1.26.x' + go-version: "1.26.x" check-latest: true cache: true cache-dependency-path: go.sum @@ -54,7 +54,7 @@ jobs: steps: - name: Release Please id: release - uses: googleapis/release-please-action@v4 + uses: googleapis/release-please-action@v5 with: token: ${{ secrets.RELEASE_PLEASE_TOKEN }} config-file: .github/release-please-config.json @@ -68,7 +68,7 @@ jobs: if: ${{ steps.release.outputs.release_created }} uses: actions/setup-go@v6 with: - go-version: '1.26.x' + go-version: "1.26.x" check-latest: true cache: true cache-dependency-path: go.sum diff --git a/README.md b/README.md index f036070..e86f620 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ and **bubu-sdk-go** (runtime SDK). | `runtime/featuretoggles` | Shared telemetry, trace propagation, logging, and metrics toggle wiring. | | `runtime/identity` | StoryRun-safe label and service-account naming helpers. | | `runtime/naming` | DNS-safe resource name composition helpers. | -| `runtime/operatorconfig` | Shared ConfigMap-backed operator config manager for controller-runtime consumers. | +| `runtime/operatorconfig` | Shared ConfigMap-backed operator config manager with a Kubernetes-only reader/reconcile contract. | | `runtime/stage` | Structured StoryRun/step metadata helpers for logs and related shared metadata flow. | | `runtime/storage` | Storage provider env, secret, timeout, and volume wiring helpers. | | `runtime/transport` | Binding envelopes, protocol checks, and shared transport env helpers. | diff --git a/go.mod b/go.mod index 175d929..69ac689 100644 --- a/go.mod +++ b/go.mod @@ -4,44 +4,22 @@ go 1.26.2 require ( github.com/Masterminds/sprig/v3 v3.3.0 - github.com/bubustack/tractatus v0.1.4 + github.com/bubustack/tractatus v0.2.0 github.com/go-logr/logr v1.4.3 github.com/stretchr/testify v1.11.1 google.golang.org/grpc v1.80.0 - google.golang.org/protobuf v1.36.11 - k8s.io/api v0.35.4 - k8s.io/apimachinery v0.35.4 - sigs.k8s.io/controller-runtime v0.23.3 + google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af + k8s.io/api v0.36.0 + k8s.io/apimachinery v0.36.0 ) require ( + buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260415201107-50325440f8f2.1 // indirect dario.cat/mergo v1.0.2 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.4.0 // indirect - github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/emicklei/go-restful/v3 v3.13.0 // indirect - github.com/evanphx/json-patch/v5 v5.9.11 // indirect - github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/fxamacker/cbor/v2 v2.9.1 // indirect - github.com/go-openapi/jsonpointer v0.23.1 // indirect - github.com/go-openapi/jsonreference v0.21.5 // indirect - github.com/go-openapi/swag v0.26.0 // indirect - github.com/go-openapi/swag/cmdutils v0.26.0 // indirect - github.com/go-openapi/swag/conv v0.26.0 // indirect - github.com/go-openapi/swag/fileutils v0.26.0 // indirect - github.com/go-openapi/swag/jsonname v0.26.0 // indirect - github.com/go-openapi/swag/jsonutils v0.26.0 // indirect - github.com/go-openapi/swag/loading v0.26.0 // indirect - github.com/go-openapi/swag/mangling v0.26.0 // indirect - github.com/go-openapi/swag/netutils v0.26.0 // indirect - github.com/go-openapi/swag/stringutils v0.26.0 // indirect - github.com/go-openapi/swag/typeutils v0.26.0 // indirect - github.com/go-openapi/swag/yamlutils v0.26.0 // indirect - github.com/google/btree v1.1.3 // indirect - github.com/google/gnostic-models v0.7.1 // indirect - github.com/google/go-cmp v0.7.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/huandu/xstrings v1.5.0 // indirect github.com/json-iterator/go v1.1.12 // indirect @@ -49,38 +27,24 @@ require ( github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect - github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.23.2 // indirect - github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.67.5 // indirect - github.com/prometheus/procfs v0.20.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/shopspring/decimal v1.4.0 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/x448/float16 v0.8.4 // indirect + go.opentelemetry.io/otel v1.41.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/crypto v0.50.0 // indirect golang.org/x/net v0.53.0 // indirect - golang.org/x/oauth2 v0.36.0 // indirect - golang.org/x/sync v0.20.0 // indirect golang.org/x/sys v0.43.0 // indirect - golang.org/x/term v0.42.0 // indirect golang.org/x/text v0.36.0 // indirect - golang.org/x/time v0.15.0 // indirect - gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 // indirect - gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiextensions-apiserver v0.35.4 // indirect - k8s.io/client-go v0.35.4 // indirect k8s.io/klog/v2 v2.140.0 // indirect k8s.io/kube-openapi v0.0.0-20260414162039-ec9c827d403f // indirect k8s.io/utils v0.0.0-20260319190234-28399d86e0b5 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v6 v6.4.0 // indirect - sigs.k8s.io/yaml v1.6.0 // indirect ) diff --git a/go.sum b/go.sum index df41ca6..059e067 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,9 @@ +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260415201107-50325440f8f2.1 h1:s6hzCXtND/ICdGPTMGk7C+/BFlr2Jg5GyH0NKf4XGXg= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260415201107-50325440f8f2.1/go.mod h1:tvtbpgaVXZX4g6Pn+AnzFycuRK3MOz5HJfEGeEllXYM= +buf.build/go/protovalidate v1.2.0 h1:DQVrUWkmGTBij+kOYv/x2LLxwcLaGKMdzShj1/6/3H0= +buf.build/go/protovalidate v1.2.0/go.mod h1:7rYiQEhqvAipoazpVNBBH2S2f8bjG4huMVy1V2Yofn4= +cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= +cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= @@ -6,96 +12,41 @@ github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1 github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bubustack/tractatus v0.1.4 h1:D+iHxcF+XkQGr54juFtUwY0VsA/8wu199qexFSGFc60= -github.com/bubustack/tractatus v0.1.4/go.mod h1:lktwjA/7B+CA761yYBIJQrT8SHaX1ibI2l240ZipXuc= +github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= +github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= +github.com/bubustack/tractatus v0.2.0 h1:/BRRsqLHYxQh0QTTAOCbCkP9O+r1PtKOphnKX1+byKU= +github.com/bubustack/tractatus v0.2.0/go.mod h1:HdwCyImmI9GXLvBju9YVxSeh+DzHRvG0haDvJT//myo= 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/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes= -github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k= -github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= -github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= -github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= -github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fxamacker/cbor/v2 v2.9.1 h1:2rWm8B193Ll4VdjsJY28jxs70IdDsHRWgQYAI80+rMQ= github.com/fxamacker/cbor/v2 v2.9.1/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/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-openapi/jsonpointer v0.23.1 h1:1HBACs7XIwR2RcmItfdSFlALhGbe6S92p0ry4d1GWg4= -github.com/go-openapi/jsonpointer v0.23.1/go.mod h1:iWRmZTrGn7XwYhtPt/fvdSFj1OfNBngqRT2UG3BxSqY= -github.com/go-openapi/jsonreference v0.21.5 h1:6uCGVXU/aNF13AQNggxfysJ+5ZcU4nEAe+pJyVWRdiE= -github.com/go-openapi/jsonreference v0.21.5/go.mod h1:u25Bw85sX4E2jzFodh1FOKMTZLcfifd1Q+iKKOUxExw= -github.com/go-openapi/swag v0.26.0 h1:GVDXCmfvhfu1BxiHo8/FA+BbKmhecHnG3varjON5/RI= -github.com/go-openapi/swag v0.26.0/go.mod h1:82g3193sZJRbocs7bNCqGfIgq8pkuwVwCfhKIRlEQF0= -github.com/go-openapi/swag/cmdutils v0.26.0 h1:iowihOcvq7y4egO8cOq0dmfohz6wfeQ63U1EnuhO2TU= -github.com/go-openapi/swag/cmdutils v0.26.0/go.mod h1:Sm1MVFMkF6guJJ+pQqHnQA3N0j9qALV3NxzDSv6bETM= -github.com/go-openapi/swag/conv v0.26.0 h1:5yGGsPYI1ZCva93U0AoKi/iZrNhaJEjr324YVsiD89I= -github.com/go-openapi/swag/conv v0.26.0/go.mod h1:tpAmIL7X58VPnHHiSO4uE3jBeRamGsFsfdDeDtb5ECE= -github.com/go-openapi/swag/fileutils v0.26.0 h1:WJoPRvsA7QRiiWluowkLJa9jaYR7FCuxmDvnCgaRRxU= -github.com/go-openapi/swag/fileutils v0.26.0/go.mod h1:0WDJ7lp67eNjPMO50wAWYlKvhOb6CQ37rzR7wrgI8Tc= -github.com/go-openapi/swag/jsonname v0.26.0 h1:gV1NFX9M8avo0YSpmWogqfQISigCmpaiNci8cGECU5w= -github.com/go-openapi/swag/jsonname v0.26.0/go.mod h1:urBBR8bZNoDYGr653ynhIx+gTeIz0ARZxHkAPktJK2M= -github.com/go-openapi/swag/jsonutils v0.26.0 h1:FawFML2iAXsPqmERscuMPIHmFsoP1tOqWkxBaKNMsnA= -github.com/go-openapi/swag/jsonutils v0.26.0/go.mod h1:2VmA0CJlyFqgawOaPI9psnjFDqzyivIqLYN34t9p91E= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.0 h1:apqeINu/ICHouqiRZbyFvuDge5jCmmLTqGQ9V95EaOM= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.0/go.mod h1:AyM6QT8uz5IdKxk5akv0y6u4QvcL9GWERt0Jx/F/R8Y= -github.com/go-openapi/swag/loading v0.26.0 h1:Apg6zaKhCJurpJer0DCxq99qwmhFddBhaMX7kilDcko= -github.com/go-openapi/swag/loading v0.26.0/go.mod h1:dBxQ/6V2uBaAQdevN18VELE6xSpJWZxLX4txe12JwDg= -github.com/go-openapi/swag/mangling v0.26.0 h1:Du2YC4YLA/Y5m/YKQd7AnY5qq0wRKSFZTTt8ktFaXcQ= -github.com/go-openapi/swag/mangling v0.26.0/go.mod h1:jifS7W9vbg+pw63bT+GI53otluMQL3CeemuyCHKwVx0= -github.com/go-openapi/swag/netutils v0.26.0 h1:CmZp+ZT7HrmFwrC3GdGsXBq2+42T1bjKBapcqVpIs3c= -github.com/go-openapi/swag/netutils v0.26.0/go.mod h1:5iK+Ok3ZohWWex1C50BFTPexi03UaPwjW4Oj8kgrpwo= -github.com/go-openapi/swag/stringutils v0.26.0 h1:qZQngLxs5s7SLijc3N2ZO+fUq2o8LjuWAASSrJuh+xg= -github.com/go-openapi/swag/stringutils v0.26.0/go.mod h1:sWn5uY+QIIspwPhvgnqJsH8xqFT2ZbYcvbcFanRyhFE= -github.com/go-openapi/swag/typeutils v0.26.0 h1:2kdEwdiNWy+JJdOvu5MA2IIg2SylWAFuuyQIKYybfq4= -github.com/go-openapi/swag/typeutils v0.26.0/go.mod h1:oovDuIUvTrEHVMqWilQzKzV4YlSKgyZmFh7AlfABNVE= -github.com/go-openapi/swag/yamlutils v0.26.0 h1:H7O8l/8NJJQ/oiReEN+oMpnGMyt8G0hl460nRZxhLMQ= -github.com/go-openapi/swag/yamlutils v0.26.0/go.mod h1:1evKEGAtP37Pkwcc7EWMF0hedX0/x3Rkvei2wtG/TbU= -github.com/go-openapi/testify/enable/yaml/v2 v2.4.2 h1:5zRca5jw7lzVREKCZVNBpysDNBjj74rBh0N2BGQbSR0= -github.com/go-openapi/testify/enable/yaml/v2 v2.4.2/go.mod h1:XVevPw5hUXuV+5AkI1u1PeAm27EQVrhXTTCPAF85LmE= -github.com/go-openapi/testify/v2 v2.4.2 h1:tiByHpvE9uHrrKjOszax7ZvKB7QOgizBWGBLuq0ePx4= -github.com/go-openapi/testify/v2 v2.4.2/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw= -github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= -github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= -github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -github.com/google/gnostic-models v0.7.1 h1:SisTfuFKJSKM5CPZkffwi6coztzzeYUhc3v4yxLWH8c= -github.com/google/gnostic-models v0.7.1/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= +github.com/google/cel-go v0.28.0 h1:KjSWstCpz/MN5t4a8gnGJNIYUsJRpdi/r97xWDphIQc= +github.com/google/cel-go v0.28.0/go.mod h1:X0bD6iVNR8pkROSOoHVdgTkzmRcosof7WQqCD6wcMc8= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 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/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= -github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= 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/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= -github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= @@ -106,24 +57,9 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns= -github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= -github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= -github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= -github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= -github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= -github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4= -github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= -github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= -github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= +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/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= @@ -133,8 +69,6 @@ github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qq github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -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.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= @@ -152,71 +86,45 @@ go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2W go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= go.opentelemetry.io/otel/trace v1.41.0 h1:Vbk2co6bhj8L59ZJ6/xFTskY+tGAbOnCtQGVVa9TIN0= go.opentelemetry.io/otel/trace v1.41.0/go.mod h1:U1NU4ULCoxeDKc09yCWdWe+3QoyweJcISEVa1RBzOis= -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= -go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q= -golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= -golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= +golang.org/x/exp v0.0.0-20250813145105-42675adae3e6 h1:SbTAbRFnd5kjQXbczszQ0hdk3ctwYf3qBNH9jIsGclE= +golang.org/x/exp v0.0.0-20250813145105-42675adae3e6/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4= golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA= golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs= -golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= -golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= -golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= -golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY= -golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY= golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= -golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= -golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= -golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s= -golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= -gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0= -gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= +google.golang.org/genproto/googleapis/api v0.0.0-20260120221211-b8f7ae30c516 h1:vmC/ws+pLzWjj/gzApyoZuSVrDtF1aod4u/+bbj8hgM= +google.golang.org/genproto/googleapis/api v0.0.0-20260120221211-b8f7ae30c516/go.mod h1:p3MLuOwURrGBRoEyFHBT3GjUwaCQVKeNqqWxlcISGdw= google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 h1:RmoJA1ujG+/lRGNfUnOMfhCy5EipVMyvUE+KNbPbTlw= google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM= google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4= -google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= -google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af h1:+5/Sw3GsDNlEmu7TfklWKPdQ0Ykja5VEmq2i817+jbI= +google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo= -gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.35.4 h1:P7nFYKl5vo9AGUp1Z+Pmd3p2tA7bX2wbFWCvDeRv988= -k8s.io/api v0.35.4/go.mod h1:yl4lqySWOgYJJf9RERXKUwE9g2y+CkuwG+xmcOK8wXU= -k8s.io/apiextensions-apiserver v0.35.4 h1:HeP+Upp7ItdvnyGmub0yoix+2z5+ev4M5cE5TCgtOUU= -k8s.io/apiextensions-apiserver v0.35.4/go.mod h1:ogQlk+stIE8mnoRthSYCwlOS12fVqgWFiErMwPaXA7c= -k8s.io/apimachinery v0.35.4 h1:xtdom9RG7e+yDp71uoXoJDWEE2eOiHgeO4GdBzwWpds= -k8s.io/apimachinery v0.35.4/go.mod h1:NNi1taPOpep0jOj+oRha3mBJPqvi0hGdaV8TCqGQ+cc= -k8s.io/client-go v0.35.4 h1:DN6fyaGuzK64UvnKO5fOA6ymSjvfGAnCAHAR0C66kD8= -k8s.io/client-go v0.35.4/go.mod h1:2Pg9WpsS4NeOpoYTfHHfMxBG8zFMSAUi4O/qoiJC3nY= +k8s.io/api v0.36.0 h1:SgqDhZzHdOtMk40xVSvCXkP9ME0H05hPM3p9AB1kL80= +k8s.io/api v0.36.0/go.mod h1:m1LVrGPNYax5NBHdO+QuAedXyuzTt4RryI/qnmNvs34= +k8s.io/apimachinery v0.36.0 h1:jZyPzhd5Z+3h9vJLt0z9XdzW9VzNzWAUw+P1xZ9PXtQ= +k8s.io/apimachinery v0.36.0/go.mod h1:FklypaRJt6n5wUIwWXIP6GJlIpUizTgfo1T/As+Tyxc= k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc= k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0= k8s.io/kube-openapi v0.0.0-20260414162039-ec9c827d403f h1:4Qiq0YAoQATdgmHALJWz9rJ4fj20pB3xebpB4CFNhYM= k8s.io/kube-openapi v0.0.0-20260414162039-ec9c827d403f/go.mod h1:uGBT7iTA6c6MvqUvSXIaYZo9ukscABYi2btjhvgKGZ0= k8s.io/utils v0.0.0-20260319190234-28399d86e0b5 h1:kBawHLSnx/mYHmRnNUf9d4CpjREbeZuxoSGOX/J+aYM= k8s.io/utils v0.0.0-20260319190234-28399d86e0b5/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= -sigs.k8s.io/controller-runtime v0.23.3 h1:VjB/vhoPoA9l1kEKZHBMnQF33tdCLQKJtydy4iqwZ80= -sigs.k8s.io/controller-runtime v0.23.3/go.mod h1:B6COOxKptp+YaUT5q4l6LqUJTRpizbgf9KSRNdQGns0= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= diff --git a/runtime/operatorconfig/manager.go b/runtime/operatorconfig/manager.go index ed43364..8f01373 100644 --- a/runtime/operatorconfig/manager.go +++ b/runtime/operatorconfig/manager.go @@ -10,13 +10,8 @@ import ( "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/event" - "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/predicate" - "sigs.k8s.io/controller-runtime/pkg/reconcile" ) // ReloadReason identifies why the configuration was reloaded. @@ -29,9 +24,30 @@ const ( ReloadReasonReconcile ReloadReason = "reconcile" ) +// ConfigMapReader is the minimal read contract needed by Manager. +type ConfigMapReader interface { + Get(ctx context.Context, key types.NamespacedName, into *corev1.ConfigMap) error +} + +// ConfigMapReaderFunc adapts a function to ConfigMapReader. +type ConfigMapReaderFunc func(context.Context, types.NamespacedName, *corev1.ConfigMap) error + +// Get implements ConfigMapReader. +func (f ConfigMapReaderFunc) Get(ctx context.Context, key types.NamespacedName, into *corev1.ConfigMap) error { + return f(ctx, key, into) +} + +// Request identifies a reconcile target. +type Request struct { + NamespacedName types.NamespacedName +} + +// Result is reserved for future reconcile scheduling metadata. +type Result struct{} + // Options configures a Manager instance for a concrete operator config type. type Options[T any] struct { - Client client.Client + Client ConfigMapReader Logger logr.Logger ConfigMapKey types.NamespacedName ControllerName string @@ -46,7 +62,7 @@ type Options[T any] struct { // for operator configuration structs. type Manager[T any] struct { opts Options[T] - apiReader client.Reader + apiReader ConfigMapReader currentConfig *T defaultConfig *T lastSync time.Time @@ -67,9 +83,6 @@ func NewManager[T any](opts Options[T]) (*Manager[T], error) { if opts.CloneConfig == nil { return nil, fmt.Errorf("operatorconfig: CloneConfig must be provided") } - if opts.Logger.GetSink() == nil { - opts.Logger = log.Log.WithName("operator-config-manager") - } if opts.ControllerName == "" { opts.ControllerName = "operator-config-manager" } @@ -93,7 +106,7 @@ func NewManager[T any](opts Options[T]) (*Manager[T], error) { } // SetAPIReader injects a non-cached reader for startup scenarios. -func (m *Manager[T]) SetAPIReader(reader client.Reader) { +func (m *Manager[T]) SetAPIReader(reader ConfigMapReader) { m.mu.Lock() defer m.mu.Unlock() m.apiReader = reader @@ -128,20 +141,10 @@ func (m *Manager[T]) LoadInitial(ctx context.Context) error { return nil } -// SetupWithManager wires the manager into controller-runtime so ConfigMap -// changes trigger reconciles. -func (m *Manager[T]) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - Named(m.opts.ControllerName). - For(&corev1.ConfigMap{}). - WithEventFilter(m.configMapPredicate()). - Complete(m) -} - // Reconcile reacts to ConfigMap updates/deletes and refreshes the cache. -func (m *Manager[T]) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) { +func (m *Manager[T]) Reconcile(ctx context.Context, req Request) (Result, error) { if req.NamespacedName != m.opts.ConfigMapKey { - return reconcile.Result{}, nil + return Result{}, nil } cfg, err := m.loadAndParse(ctx) @@ -152,17 +155,17 @@ func (m *Manager[T]) Reconcile(ctx context.Context, req reconcile.Request) (reco if changed { m.applyAndNotify(ReloadReasonReconcile, snapshot) } - return reconcile.Result{}, nil + return Result{}, nil } m.opts.Logger.Error(err, "failed to refresh operator configuration") - return reconcile.Result{}, err + return Result{}, err } snapshot, changed := m.storeConfigIfChanged(cfg) if changed { m.applyAndNotify(ReloadReasonReconcile, snapshot) } - return reconcile.Result{}, nil + return Result{}, nil } func (m *Manager[T]) loadAndParse(ctx context.Context) (*T, error) { @@ -186,7 +189,7 @@ func (m *Manager[T]) loadAndParse(ctx context.Context) (*T, error) { return cfg, nil } -func (m *Manager[T]) reader() client.Reader { +func (m *Manager[T]) reader() ConfigMapReader { m.mu.RLock() defer m.mu.RUnlock() if m.apiReader != nil { @@ -225,30 +228,10 @@ func (m *Manager[T]) applyAndNotify(reason ReloadReason, cfg *T) { } } -func (m *Manager[T]) configMapPredicate() predicate.Predicate { - return predicate.Funcs{ - CreateFunc: func(e event.CreateEvent) bool { - if e.Object == nil { - return false - } - return namespacedName(e.Object.GetNamespace(), e.Object.GetName()) == m.opts.ConfigMapKey - }, - UpdateFunc: func(e event.UpdateEvent) bool { - if e.ObjectNew == nil { - return false - } - return namespacedName(e.ObjectNew.GetNamespace(), e.ObjectNew.GetName()) == m.opts.ConfigMapKey - }, - DeleteFunc: func(e event.DeleteEvent) bool { - if e.Object == nil { - return false - } - return namespacedName(e.Object.GetNamespace(), e.Object.GetName()) == m.opts.ConfigMapKey - }, - GenericFunc: func(event.GenericEvent) bool { return false }, +// MatchesConfigMap reports whether the object is the configured ConfigMap. +func (m *Manager[T]) MatchesConfigMap(obj metav1.Object) bool { + if obj == nil { + return false } -} - -func namespacedName(ns, name string) types.NamespacedName { - return types.NamespacedName{Namespace: ns, Name: name} + return types.NamespacedName{Namespace: obj.GetNamespace(), Name: obj.GetName()} == m.opts.ConfigMapKey } diff --git a/runtime/operatorconfig/manager_test.go b/runtime/operatorconfig/manager_test.go index 02aa30a..2d7cf91 100644 --- a/runtime/operatorconfig/manager_test.go +++ b/runtime/operatorconfig/manager_test.go @@ -5,12 +5,10 @@ import ( "testing" corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client/fake" - "sigs.k8s.io/controller-runtime/pkg/event" - "sigs.k8s.io/controller-runtime/pkg/reconcile" ) type sampleConfig struct { @@ -44,12 +42,39 @@ func parseSampleConfig(cm *corev1.ConfigMap) (*sampleConfig, error) { return &sampleConfig{Value: val}, nil } -func TestManagerLoadInitial(t *testing.T) { - scheme := runtime.NewScheme() - if err := corev1.AddToScheme(scheme); err != nil { - t.Fatalf("failed to add corev1 scheme: %v", err) +type memoryConfigMapReader struct { + items map[types.NamespacedName]*corev1.ConfigMap +} + +func newMemoryConfigMapReader(items ...*corev1.ConfigMap) *memoryConfigMapReader { + reader := &memoryConfigMapReader{items: make(map[types.NamespacedName]*corev1.ConfigMap, len(items))} + for _, item := range items { + reader.put(item) + } + return reader +} + +func (r *memoryConfigMapReader) Get(_ context.Context, key types.NamespacedName, into *corev1.ConfigMap) error { + item := r.items[key] + if item == nil { + return apierrors.NewNotFound(schema.GroupResource{Resource: "configmaps"}, key.Name) + } + item.DeepCopyInto(into) + return nil +} + +func (r *memoryConfigMapReader) delete(item *corev1.ConfigMap) { + delete(r.items, types.NamespacedName{Name: item.Name, Namespace: item.Namespace}) +} + +func (r *memoryConfigMapReader) put(item *corev1.ConfigMap) { + if item == nil { + return } + r.items[types.NamespacedName{Name: item.Name, Namespace: item.Namespace}] = item.DeepCopy() +} +func TestManagerLoadInitial(t *testing.T) { cm := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: operatorConfigName, @@ -58,7 +83,7 @@ func TestManagerLoadInitial(t *testing.T) { Data: map[string]string{"value": liveConfigValue}, } - client := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(cm).Build() + client := newMemoryConfigMapReader(cm) applied := false manager, err := NewManager[sampleConfig](Options[sampleConfig]{ @@ -102,11 +127,6 @@ func TestNewManagerReturnsValidationErrors(t *testing.T) { } func TestManagerCallbacksAndCurrentConfigReceiveClones(t *testing.T) { - scheme := runtime.NewScheme() - if err := corev1.AddToScheme(scheme); err != nil { - t.Fatalf("failed to add corev1 scheme: %v", err) - } - cm := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: operatorConfigName, @@ -115,7 +135,7 @@ func TestManagerCallbacksAndCurrentConfigReceiveClones(t *testing.T) { Data: map[string]string{"value": liveConfigValue}, } - client := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(cm).Build() + client := newMemoryConfigMapReader(cm) manager, err := NewManager[sampleConfig](Options[sampleConfig]{ Client: client, @@ -152,11 +172,6 @@ func TestManagerCallbacksAndCurrentConfigReceiveClones(t *testing.T) { } func TestManagerReconcileSkipsUnchangedConfig(t *testing.T) { - scheme := runtime.NewScheme() - if err := corev1.AddToScheme(scheme); err != nil { - t.Fatalf("failed to add corev1 scheme: %v", err) - } - cm := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: operatorConfigName, @@ -165,7 +180,7 @@ func TestManagerReconcileSkipsUnchangedConfig(t *testing.T) { Data: map[string]string{"value": liveConfigValue}, } - client := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(cm).Build() + client := newMemoryConfigMapReader(cm) applied := 0 manager, err := NewManager[sampleConfig](Options[sampleConfig]{ @@ -191,7 +206,7 @@ func TestManagerReconcileSkipsUnchangedConfig(t *testing.T) { t.Fatalf("expected first apply during LoadInitial, got %d", applied) } - if _, err := manager.Reconcile(context.Background(), reconcile.Request{ + if _, err := manager.Reconcile(context.Background(), Request{ NamespacedName: types.NamespacedName{Name: operatorConfigName, Namespace: defaultNamespace}, }); err != nil { t.Fatalf("Reconcile failed: %v", err) @@ -201,30 +216,18 @@ func TestManagerReconcileSkipsUnchangedConfig(t *testing.T) { } } -func TestConfigMapPredicateHandlesNilEvents(t *testing.T) { +func TestMatchesConfigMapHandlesNilObject(t *testing.T) { manager := &Manager[sampleConfig]{ opts: Options[sampleConfig]{ ConfigMapKey: types.NamespacedName{Name: operatorConfigName, Namespace: defaultNamespace}, }, } - pred := manager.configMapPredicate() - if pred.Create(event.CreateEvent{}) { - t.Fatalf("expected nil create event to be ignored") - } - if pred.Update(event.UpdateEvent{}) { - t.Fatalf("expected nil update event to be ignored") - } - if pred.Delete(event.DeleteEvent{}) { - t.Fatalf("expected nil delete event to be ignored") + if manager.MatchesConfigMap(nil) { + t.Fatalf("expected nil object to be ignored") } } func TestManagerReconcileNotFoundResetsToDefault(t *testing.T) { - scheme := runtime.NewScheme() - if err := corev1.AddToScheme(scheme); err != nil { - t.Fatalf("failed to add corev1 scheme: %v", err) - } - cm := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: operatorConfigName, @@ -233,7 +236,7 @@ func TestManagerReconcileNotFoundResetsToDefault(t *testing.T) { Data: map[string]string{"value": liveConfigValue}, } - client := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(cm).Build() + client := newMemoryConfigMapReader(cm) applied := 0 manager, err := NewManager[sampleConfig](Options[sampleConfig]{ @@ -255,11 +258,9 @@ func TestManagerReconcileNotFoundResetsToDefault(t *testing.T) { if err := manager.LoadInitial(context.Background()); err != nil { t.Fatalf("LoadInitial failed: %v", err) } - if err := client.Delete(context.Background(), cm); err != nil { - t.Fatalf("delete configmap: %v", err) - } + client.delete(cm) - if _, err := manager.Reconcile(context.Background(), reconcile.Request{ + if _, err := manager.Reconcile(context.Background(), Request{ NamespacedName: types.NamespacedName{Name: operatorConfigName, Namespace: defaultNamespace}, }); err != nil { t.Fatalf("Reconcile failed: %v", err) diff --git a/runtime/transport/connector/dial.go b/runtime/transport/connector/dial.go index a747dbb..3ab9401 100644 --- a/runtime/transport/connector/dial.go +++ b/runtime/transport/connector/dial.go @@ -33,6 +33,10 @@ func WaitForReady(ctx context.Context, conn *grpc.ClientConn) error { // CallWithTimeout executes fn with a derived context that carries the timeout. // The callback should honor the provided context so any background work can unwind // promptly after timeout or cancellation. +// +// If fn ignores context cancellation, the goroutine running fn remains alive +// until fn returns. Callers must ensure fn observes callCtx.Done() to avoid +// goroutine accumulation during sustained timeout or cancellation storms. func CallWithTimeout(ctx context.Context, timeout time.Duration, opName string, fn func(context.Context) error) error { callCtx, cancel := deriveCallContext(ctx, timeout) defer cancel() @@ -53,6 +57,10 @@ func CallWithTimeout(ctx context.Context, timeout time.Duration, opName string, // RecvWithTimeout executes fn with a derived context that carries the timeout. // The callback should honor the provided context so any background work can unwind // promptly after timeout or cancellation. +// +// If fn ignores context cancellation, the goroutine running fn remains alive +// until fn returns. Callers must ensure fn observes callCtx.Done() to avoid +// goroutine accumulation during sustained timeout or cancellation storms. func RecvWithTimeout[T any]( ctx context.Context, timeout time.Duration, diff --git a/runtime/transport/connector/dial_test.go b/runtime/transport/connector/dial_test.go index b5dd580..826eec8 100644 --- a/runtime/transport/connector/dial_test.go +++ b/runtime/transport/connector/dial_test.go @@ -98,17 +98,23 @@ func TestCallWithTimeoutPropagatesParentCancellation(t *testing.T) { func TestCallWithTimeoutPropagatesParentCancellationWhenCallbackIgnoresContext(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) release := make(chan struct{}) + started := make(chan struct{}) defer close(release) done := make(chan error, 1) go func() { done <- CallWithTimeout(ctx, time.Second, "call", func(context.Context) error { + close(started) <-release return nil }) }() - time.Sleep(10 * time.Millisecond) + select { + case <-started: + case <-time.After(200 * time.Millisecond): + t.Fatal("expected callback to start") + } cancel() select { diff --git a/templating/funcs.go b/templating/funcs.go index 46107db..bce4c99 100644 --- a/templating/funcs.go +++ b/templating/funcs.go @@ -21,6 +21,8 @@ var safeSprigFunctionNames = []string{ "fromJson", "toJson", "toPrettyJson", "toRawJson", "mustFromJson", "mustToJson", "mustToPrettyJson", "mustToRawJson", "ternary", "typeOf", "typeIs", "typeIsLike", "kindOf", "kindIs", "deepEqual", + // Path helpers are string-only transforms. They do not read the filesystem + // or environment, so they stay available for portable key/path shaping. "base", "dir", "clean", "ext", "isAbs", "osBase", "osClean", "osDir", "osExt", "osIsAbs", "b64enc", "b64dec", "b32enc", "b32dec", "tuple", "list", "dict", "get", "hasKey", "pluck", "keys", "pick", "omit", "values", diff --git a/templating/funcs_test.go b/templating/funcs_test.go index 6703331..69fac5e 100644 --- a/templating/funcs_test.go +++ b/templating/funcs_test.go @@ -1,6 +1,65 @@ package templating -import "testing" +import ( + "sort" + "testing" + + sprig "github.com/Masterminds/sprig/v3" +) + +var reviewedDeniedSprigFunctionNames = map[string]string{ + "ago": "time-dependent helper", + "bcrypt": "credential/hash helper not needed in templates", + "buildCustomCert": "certificate material helper", + "chunk": "not needed in supported template surface", + "deepCopy": "object graph helper not needed in templates", + "decryptAES": "cryptographic helper", + "derivePassword": "credential derivation helper", + "encryptAES": "cryptographic helper", + "env": "host environment access", + "expandenv": "host environment access", + "genCA": "certificate material helper", + "genCAWithKey": "certificate material helper", + "genPrivateKey": "key material helper", + "genSelfSignedCert": "certificate material helper", + "genSelfSignedCertWithKey": "certificate material helper", + "genSignedCert": "certificate material helper", + "genSignedCertWithKey": "certificate material helper", + "getHostByName": "network lookup helper", + "hello": "diagnostic helper", + "htpasswd": "credential/hash helper not needed in templates", + "merge": "map merge helper can hide unexpected shape changes", + "mergeOverwrite": "map merge helper can hide unexpected shape changes", + "mustChunk": "not needed in supported template surface", + "mustDeepCopy": "object graph helper not needed in templates", + "mustMerge": "map merge helper can hide unexpected shape changes", + "mustMergeOverwrite": "map merge helper can hide unexpected shape changes", + "mustRegexFind": "regex helpers are not part of supported template surface", + "mustRegexFindAll": "regex helpers are not part of supported template surface", + "mustRegexMatch": "regex helpers are not part of supported template surface", + "mustRegexReplaceAll": "regex helpers are not part of supported template surface", + "mustRegexReplaceAllLiteral": "regex helpers are not part of supported template surface", + "mustRegexSplit": "regex helpers are not part of supported template surface", + "randBytes": "random helper is not part of current random opt-in surface", + "regexFind": "regex helpers are not part of supported template surface", + "regexFindAll": "regex helpers are not part of supported template surface", + "regexMatch": "regex helpers are not part of supported template surface", + "regexQuoteMeta": "regex helpers are not part of supported template surface", + "regexReplaceAll": "regex helpers are not part of supported template surface", + "regexReplaceAllLiteral": "regex helpers are not part of supported template surface", + "regexSplit": "regex helpers are not part of supported template surface", + "repeat": "unbounded output helper", + "seq": "unbounded sequence helper", + "set": "mutating map helper", + "sha512sum": "not needed in supported template surface", + "shuffle": "randomized helper", + "swapcase": "not needed in supported template surface", + "unset": "mutating map helper", + "until": "unbounded sequence helper", + "untilStep": "unbounded sequence helper", + "urlJoin": "URL helper not needed in templates", + "urlParse": "URL helper not needed in templates", +} func TestBuildFuncMapRestrictsDangerousSprigFunctions(t *testing.T) { funcs := buildFuncMap(false, false) @@ -31,3 +90,42 @@ func TestBuildFuncMapAllowsDeterministicOptIns(t *testing.T) { t.Fatalf("expected safe sprig helper to remain available") } } + +func TestAllSprigFunctionsExplicitlyReviewed(t *testing.T) { + reviewed := map[string]string{} + for _, name := range safeSprigFunctionNames { + reviewed[name] = "safe" + } + for _, name := range randomSprigFunctionNames { + reviewed[name] = "random opt-in" + } + reviewed["now"] = "time opt-in" + for name, reason := range reviewedDeniedSprigFunctionNames { + reviewed[name] = "denied: " + reason + } + + var unreviewed []string + for name := range sprig.TxtFuncMap() { + if _, ok := reviewed[name]; !ok { + unreviewed = append(unreviewed, name) + } + } + sort.Strings(unreviewed) + for _, name := range unreviewed { + t.Errorf( + "unreviewed Sprig function %q: add to safeSprigFunctionNames, "+ + "randomSprigFunctionNames, or reviewedDeniedSprigFunctionNames", + name, + ) + } +} + +func TestSafeSprigOSPathFunctionsAreStringOnlyAndAllowed(t *testing.T) { + funcs := buildFuncMap(false, false) + + for _, name := range []string{"osBase", "osClean", "osDir", "osExt", "osIsAbs"} { + if _, ok := funcs[name]; !ok { + t.Fatalf("expected reviewed string-only path helper %s to remain available", name) + } + } +}