From ccd0f3e2b8be3fde47c4805749c9ad88bc812397 Mon Sep 17 00:00:00 2001 From: Roman Berezkin <93445878+Glitchy-Sheep@users.noreply.github.com> Date: Thu, 29 Oct 2026 15:57:39 +0300 Subject: [PATCH 1/2] [shell-operator] fix: restore ARM images and preserve hook logs (#956) Signed-off-by: Roman Berezkin --- .dockerignore | 3 +- .github/workflows/images.yaml | 78 ++++++++++++ .github/workflows/lint.yaml | 2 +- Dockerfile | 17 +-- go.mod | 6 +- go.sum | 4 + pkg/executor/executor.go | 155 ++++++++++++------------ pkg/executor/proxy_logger_test.go | 191 ++++++++++++++++++++++++++++++ scripts/ci/verify-image.sh | 79 ++++++++++++ 9 files changed, 448 insertions(+), 87 deletions(-) create mode 100644 .github/workflows/images.yaml create mode 100644 pkg/executor/proxy_logger_test.go create mode 100644 scripts/ci/verify-image.sh diff --git a/.dockerignore b/.dockerignore index 4d248e6d..ffa14334 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,6 +2,7 @@ docs examples scripts +!scripts/ci/verify-image.sh test libjq @@ -10,4 +11,4 @@ libjq Dockerfile LICENSE *.md -*.png \ No newline at end of file +*.png diff --git a/.github/workflows/images.yaml b/.github/workflows/images.yaml new file mode 100644 index 00000000..1752a8a5 --- /dev/null +++ b/.github/workflows/images.yaml @@ -0,0 +1,78 @@ +# - Detect base-image changes in Dockerfile FROM instructions. +# - Build amd64, arm64 and arm/v7 only when bases change or on manual runs. +# - Check base-image and binary architectures and run smoke tests without publishing images. +name: Base image architecture checks +on: + pull_request: + types: [opened, synchronize] + workflow_dispatch: + +permissions: + contents: read + +jobs: + changes: + name: Detect base image changes + runs-on: ubuntu-latest + outputs: + build: ${{ steps.check.outputs.build }} + steps: + - name: Check out shell-operator code + if: github.event_name == 'pull_request' + uses: actions/checkout@v7 + - name: Decide whether to build images + id: check + shell: bash + env: + EVENT_NAME: ${{ github.event_name }} + BEFORE_SHA: ${{ github.event.before || github.event.pull_request.base.sha }} + AFTER_SHA: ${{ github.event.pull_request.head.sha }} + run: | + if [ "$EVENT_NAME" = workflow_dispatch ]; then + echo "▶️ Manual run requested: building and checking all platforms." + echo "build=true" >> "$GITHUB_OUTPUT" + exit 0 + fi + + # Compare this push so unrelated commits do not repeat the full build. + git fetch --no-tags --depth=1 origin "$BEFORE_SHA" "$AFTER_SHA" + git show "$BEFORE_SHA:Dockerfile" | awk 'toupper($1) == "FROM" { $1=$1; print }' > "$RUNNER_TEMP/base-images-before" + git show "$AFTER_SHA:Dockerfile" | awk 'toupper($1) == "FROM" { $1=$1; print }' > "$RUNNER_TEMP/base-images-after" + + if cmp -s "$RUNNER_TEMP/base-images-before" "$RUNNER_TEMP/base-images-after"; then + echo "⏭️ Base images unchanged. Skipping architecture checks." + echo "build=false" >> "$GITHUB_OUTPUT" + else + echo "🔄 Base image changes detected." + echo "🚀 Full build and architecture checks requested for all platforms." + echo "build=true" >> "$GITHUB_OUTPUT" + fi + + build: + needs: changes + if: needs.changes.outputs.build == 'true' + name: Build and verify (${{ matrix.platform }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + platform: [linux/amd64, linux/arm64, linux/arm/v7] + steps: + - name: Check out shell-operator code + uses: actions/checkout@v7 + - name: Set up QEMU + uses: docker/setup-qemu-action@v4.2.0 + with: + platforms: arm64,arm + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + - name: Build and verify image + env: + PLATFORM: ${{ matrix.platform }} + run: | + echo "🏗️ Building $PLATFORM image with architecture verification enabled." + docker buildx build \ + --platform "$PLATFORM" \ + --build-arg appVersion=ci \ + --output type=cacheonly \ + . diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index cfdc9192..6be6ea4b 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -39,7 +39,7 @@ jobs: steps: - uses: actions/setup-python@v6 with: - python-version: 3.8 + python-version: "3.12" - name: Check out addon-operator code uses: actions/checkout@v7 - name: Run codespell diff --git a/Dockerfile b/Dockerfile index 84fca7d6..83ab04cc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,4 @@ -# Prebuilt jq from deckhouse base images (v1.0.40). -FROM --platform=${TARGETPLATFORM:-linux/amd64} registry.deckhouse.io/container-factory@sha256:4b36dcf53c35b50e0afbc445232713aff15f788a61b832cd720bf9e88fc9fba8 AS libjq - -# Go builder stage (builder/golang-alpine, Go 1.26.4 on alpine 3.22). -FROM --platform=${TARGETPLATFORM:-linux/amd64} registry.deckhouse.io/container-factory@sha256:193e8ed6cd7fc19015ab615ccf92d0fe02471e66e3e5abf560b3a87fb05bdb62 AS builder +FROM --platform=${TARGETPLATFORM:-linux/amd64} golang:1.26.4-alpine3.23@sha256:18b460dd17542c2ba43299a633cf6ebfc1115101509531471d7cfce1019af083 AS builder ARG appVersion=latest @@ -32,8 +28,7 @@ RUN GOOS=linux \ -o shell-operator \ ./cmd/shell-operator -# Final runtime image (builder/alpine 3.22 from deckhouse base images v1.0.40). -FROM --platform=${TARGETPLATFORM:-linux/amd64} registry.deckhouse.io/container-factory@sha256:8fa8cf713bf8cfc9038901e5b2fbc97d0403794d834dc4a619e9e81312a6feef +FROM --platform=${TARGETPLATFORM:-linux/amd64} alpine:3.23@sha256:ff7f0c727dd796aa36787bfcd9a64bf35a6ce72a73451b3359fde849b6703d63 ARG TARGETPLATFORM ARG kubectlVersion=v1.35.8 @@ -44,6 +39,7 @@ RUN apk --no-cache add \ bash \ sed \ tini \ + jq \ curl # Determine kubectl architecture and download @@ -58,9 +54,14 @@ RUN mkdir /hooks # Copy necessary files ADD frameworks/shell /frameworks/shell ADD shell_lib.sh / -COPY --from=libjq /usr/bin/jq /usr/bin/jq COPY --from=builder /app/shell-operator / +# Reject incorrect architectures before the image can be published. +RUN --mount=type=bind,source=scripts/ci/verify-image.sh,target=/tmp/verify-image.sh \ + apk add --no-cache --virtual .image-verification file && \ + sh /tmp/verify-image.sh "${TARGETPLATFORM:-linux/amd64}" && \ + apk del .image-verification + # Set working directory WORKDIR / diff --git a/go.mod b/go.mod index db391afe..10c07930 100644 --- a/go.mod +++ b/go.mod @@ -129,9 +129,9 @@ require ( golang.org/x/sys v0.48.0 // indirect golang.org/x/term v0.46.0 // indirect golang.org/x/text v0.42.0 // indirect - golang.org/x/tools v0.48.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260720211330-0afa2a65878a // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260720211330-0afa2a65878a // indirect + golang.org/x/tools v0.49.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260803160001-6ac0973c030d // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260803160001-6ac0973c030d // indirect google.golang.org/grpc v1.83.2 // indirect google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af // indirect gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect diff --git a/go.sum b/go.sum index 30b968ad..a26d4537 100644 --- a/go.sum +++ b/go.sum @@ -435,8 +435,12 @@ 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-20260720211330-0afa2a65878a h1:97PfJ4tCxY5C7NzzgGqQEMZmXbISdvSArNNEOoUGKBg= google.golang.org/genproto/googleapis/api v0.0.0-20260720211330-0afa2a65878a/go.mod h1:1brfde68Npq6+WA75c1EHWPijZEG1kMus61ygPZfn4A= +google.golang.org/genproto/googleapis/api v0.0.0-20260803160001-6ac0973c030d h1:FarXi840EJWSHYTN3ERkADbPWjl307+FGrA22KAVjjc= +google.golang.org/genproto/googleapis/api v0.0.0-20260803160001-6ac0973c030d/go.mod h1:K/+WGbmBY7aNW1HDw1fJnKYo10i0DkAX6pows00dLig= google.golang.org/genproto/googleapis/rpc v0.0.0-20260720211330-0afa2a65878a h1:qI/YMH1ep2qQtqcp00gMQyoU7mjvbhg88GJKCvfoLj0= google.golang.org/genproto/googleapis/rpc v0.0.0-20260720211330-0afa2a65878a/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260803160001-6ac0973c030d h1:IL4hdHzcUv2l/gcg98/Rj3FbtE6axwqslOW8SW0C+S0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260803160001-6ac0973c030d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/grpc v1.83.2 h1:EManeRomTObA0BU7I8vXgg/78uE5MJ9M8B39EX2WscU= google.golang.org/grpc v1.83.2/go.mod h1:YPI1hK3kDked6iHvgX3tR0y+nX/qpMFKhPgFsokw1S8= google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af h1:+5/Sw3GsDNlEmu7TfklWKPdQ0Ykja5VEmq2i817+jbI= diff --git a/pkg/executor/executor.go b/pkg/executor/executor.go index f745443a..2dcf52ae 100644 --- a/pkg/executor/executor.go +++ b/pkg/executor/executor.go @@ -4,6 +4,7 @@ import ( "bufio" "bytes" "context" + stdjson "encoding/json" "errors" "fmt" "io" @@ -252,6 +253,8 @@ func (e *Executor) RunAndLogLines(ctx context.Context, logLabels map[string]stri e.cmd.Stderr = io.MultiWriter(ple, stdErr) usage, err := e.runner.Run(ctx, e.cmd) + plo.flush() + ple.flush() if err != nil { if len(stdErr.Bytes()) > 0 { return nil, &StderrError{Message: stdErr.String()} @@ -270,105 +273,109 @@ type proxyLogger struct { proxyJsonLogKey string logger *log.Logger buf []byte + jsonBuf []byte } func (pl *proxyLogger) Write(p []byte) (int, error) { - if !pl.logProxyHookJSON { - pl.writerScanner(p) - - return len(p), nil - } - - // join all parts of json pl.buf = append(pl.buf, p...) - var line interface{} - err := json.Unmarshal(pl.buf, &line) - if err != nil { - if err.Error() == "unexpected end of JSON input" { - return len(p), nil + for len(pl.buf) > 0 { + lineSize := bytes.IndexByte(pl.buf, '\n') + 1 + if !pl.hasPendingJSON() && len(pl.buf) >= bufio.MaxScanTokenSize && (lineSize == 0 || lineSize > bufio.MaxScanTokenSize) { + lineSize = bufio.MaxScanTokenSize + } + if lineSize == 0 { + break } - pl.logger.Debug("output is not json", log.Err(err)) - pl.writerScanner(p) - - return len(p), nil + pl.logLine(pl.buf[:lineSize]) + pl.buf = pl.buf[lineSize:] } - - logMap, ok := line.(map[string]interface{}) - defer func() { - pl.buf = pl.buf[:0] - }() - - if !ok { - pl.logger.Debug("json log line not map[string]interface{}", slog.Any(pkg.LogKeyLine, line)) - - // fall back to using the logger - pl.logger.Info(string(p)) - - return len(p), nil + if len(pl.buf) == 0 { + pl.buf = nil } - pl.mergeAndLogInputLog(pl.ctx, logMap, "hook") - return len(p), nil } -func (pl *proxyLogger) writerScanner(p []byte) { - scanner := bufio.NewScanner(bytes.NewReader(p)) - scanner.Buffer(make([]byte, bufio.MaxScanTokenSize), bufio.MaxScanTokenSize) +func (pl *proxyLogger) hasPendingJSON() bool { + if len(pl.jsonBuf) > 0 { + return true + } + line := bytes.TrimLeft(pl.buf, " \t\r\n") + return bytes.HasPrefix(line, []byte("{")) || (pl.logProxyHookJSON && (bytes.HasPrefix(line, []byte("[")) || bytes.HasPrefix(line, []byte("\"")))) +} - var jsonBuf []string - // Split large entries into chunks - chunkSize := bufio.MaxScanTokenSize // 64KB - splitFunc := func(data []byte, atEOF bool) (int, []byte, error) { - if len(data) >= chunkSize { - return chunkSize, data[:chunkSize], nil - } - return bufio.ScanLines(data, atEOF) - } - scanner.Split(splitFunc) - // Scan the input and write it to the logger using the specified print function - for scanner.Scan() { - // Prevent empty logging - line := strings.TrimSpace(scanner.Text()) - if line == "" { - continue - } +// flush logs the final unterminated record after the output stream ends. +func (pl *proxyLogger) flush() { + if len(pl.buf) > 0 { + pl.logLine(pl.buf) + pl.buf = nil + } + if len(pl.jsonBuf) > 0 { + pl.logText(string(pl.jsonBuf)) + pl.jsonBuf = nil + } +} - if len(jsonBuf) > 0 || strings.HasPrefix(line, "{") { - jsonBuf = append(jsonBuf, line) - joined := strings.Join(jsonBuf, "\n") - if err := pl.tryLogJSON(joined); err == nil { - jsonBuf = nil - } - continue - } +func (pl *proxyLogger) logLine(raw []byte) { + line := strings.TrimSpace(string(raw)) + if line == "" && len(pl.jsonBuf) == 0 { + return + } - if len(line) > 10000 { - line = fmt.Sprintf("%s:truncated", line[:10000]) + // JSON records can span both physical lines and separate Write calls. + if len(pl.jsonBuf) > 0 || strings.HasPrefix(line, "{") || (pl.logProxyHookJSON && (strings.HasPrefix(line, "[") || strings.HasPrefix(line, "\""))) { + pl.jsonBuf = append(pl.jsonBuf, raw...) + err := pl.tryLogJSON(string(pl.jsonBuf)) + if errors.Is(err, io.ErrUnexpectedEOF) { + return } - - if err := pl.tryLogJSON(line); err == nil { - continue + if err != nil { + pl.logText(string(pl.jsonBuf)) } - pl.logger.Info(line) + pl.jsonBuf = nil + return + } + + if err := pl.tryLogJSON(string(raw)); err == nil { + return } - // If there was an error while scanning the input, log an error - if err := scanner.Err(); err != nil { - pl.logger.Error("reading from scanner", log.Err(err)) + pl.logText(line) +} + +func (pl *proxyLogger) logText(line string) { + line = strings.TrimSpace(line) + if len(line) > 10000 { + line = fmt.Sprintf("%s:truncated", line[:10000]) } + pl.logger.Info(line) } -// tryLogJSON tries to parse the string as JSON and log it if it succeeds +// tryLogJSON logs a complete JSON record with the hook's structured fields. func (pl *proxyLogger) tryLogJSON(s string) error { - var m map[string]interface{} - err := json.Unmarshal([]byte(s), &m) - if err == nil { - pl.mergeAndLogInputLog(pl.ctx, m, "hook") + var value interface{} + if err := json.Unmarshal([]byte(s), &value); err != nil { + // go-json reports some incomplete documents as syntax errors. + var raw stdjson.RawMessage + if decodeErr := stdjson.NewDecoder(strings.NewReader(s)).Decode(&raw); errors.Is(decodeErr, io.ErrUnexpectedEOF) { + return io.ErrUnexpectedEOF + } + return err + } + + logMap, ok := value.(map[string]interface{}) + if ok || (value == nil && !pl.logProxyHookJSON) { + pl.mergeAndLogInputLog(pl.ctx, logMap, "hook") return nil } - return fmt.Errorf("failed to parse json: %w", err) + if !pl.logProxyHookJSON { + return errors.New("json log line is not an object") + } + + pl.logger.Debug("json log line not map[string]interface{}", slog.Any(pkg.LogKeyLine, value)) + pl.logger.Info(s) + return nil } // level = level diff --git a/pkg/executor/proxy_logger_test.go b/pkg/executor/proxy_logger_test.go new file mode 100644 index 00000000..3f186a52 --- /dev/null +++ b/pkg/executor/proxy_logger_test.go @@ -0,0 +1,191 @@ +package executor + +import ( + "bytes" + "context" + "errors" + "fmt" + "strings" + "testing" + "time" + + "github.com/deckhouse/deckhouse/pkg/log" + "github.com/stretchr/testify/require" + + json "github.com/flant/shell-operator/pkg/utils/json" +) + +func TestProxyLoggerWriteBoundaries(t *testing.T) { + cases := []struct { + name string + proxy bool + input string + expected []map[string]any + }{ + { + name: "mixed JSON and text", + proxy: true, + input: "{\"level\":\"error\",\"msg\":\"quoted \\\"{value}\\\"\",\"value\":\"Привет\"}\r\n" + + "plain text\r\n" + "{\r\n\"a\":\"b\",\r\n\"c\":\"d\"\r\n}", + expected: []map[string]any{ + {"level": "error", "msg": `quoted "{value}"`, "hook_value": "Привет", "output": "stdout", "proxyJsonLog": true}, + {"level": "info", "msg": "plain text", "output": "stdout"}, + {"level": "info", "msg": "hook result", "hook_a": "b", "hook_c": "d", "output": "stdout", "proxyJsonLog": true}, + }, + }, + { + name: "plain text without JSON proxy", + input: "first line\r\n\nПривет\nlast line", + expected: []map[string]any{ + {"level": "info", "msg": "first line", "output": "stdout"}, + {"level": "info", "msg": "Привет", "output": "stdout"}, + {"level": "info", "msg": "last line", "output": "stdout"}, + }, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + t.Run("whole", func(t *testing.T) { + require.Equal(t, tc.expected, proxyLoggerRecords(t, tc.proxy, tc.input)) + }) + for split := 1; split < len(tc.input); split++ { + t.Run(fmt.Sprintf("split_%d", split), func(t *testing.T) { + require.Equal(t, tc.expected, proxyLoggerRecords(t, tc.proxy, tc.input[:split], tc.input[split:])) + }) + } + t.Run("bytewise", func(t *testing.T) { + chunks := make([]string, len(tc.input)) + for i := range chunks { + chunks[i] = tc.input[i : i+1] + } + require.Equal(t, tc.expected, proxyLoggerRecords(t, tc.proxy, chunks...)) + }) + }) + } +} + +func TestProxyLoggerFlushIncompleteJSON(t *testing.T) { + require.Equal(t, []map[string]any{ + {"level": "info", "msg": `{"unfinished":`, "output": "stdout"}, + }, proxyLoggerRecords(t, true, `{"unfin`, `ished":`)) +} + +func TestProxyLoggerMalformedJSON(t *testing.T) { + require.Equal(t, []map[string]any{ + {"level": "info", "msg": `{"value": broken}`, "output": "stdout"}, + {"level": "info", "msg": "hook result", "hook_value": "next", "output": "stdout", "proxyJsonLog": true}, + }, proxyLoggerRecords(t, true, "{\"value\": broken}\n{\"value\":\"next\"}\n")) +} + +func TestProxyLoggerMultilineArray(t *testing.T) { + input := "[\n\n\"first\",\n\"second\"\n]\n" + require.Equal(t, []map[string]any{ + {"level": "info", "msg": input, "output": "stdout"}, + }, proxyLoggerRecords(t, true, input[:4], input[4:])) +} + +func TestProxyLoggerLongRecords(t *testing.T) { + t.Run("plain text is truncated across writes", func(t *testing.T) { + line := "result " + strings.Repeat("x", 20000) + records := proxyLoggerRecords(t, false, line[:4096], line[4096:]+"\nafter\n") + require.Equal(t, []map[string]any{ + {"level": "info", "msg": line[:10000] + ":truncated", "output": "stdout"}, + {"level": "info", "msg": "after", "output": "stdout"}, + }, records) + }) + + t.Run("JSON fields are truncated across writes", func(t *testing.T) { + input := `{"payload":"` + strings.Repeat("x", 70000) + `"}` + records := proxyLoggerRecords(t, true, input[:32768], input[32768:]+"\n") + require.Equal(t, []map[string]any{ + { + "level": "info", "msg": "hook result", "output": "stdout", "proxyJsonLog": true, + "hook": map[string]any{"truncated": input[:10000] + ":truncated"}, + }, + }, records) + }) + + t.Run("non-object JSON remains raw text", func(t *testing.T) { + input := `"` + strings.Repeat("x", 70000) + "\"\n" + require.Equal(t, []map[string]any{ + {"level": "info", "msg": input, "output": "stdout"}, + }, proxyLoggerRecords(t, true, input[:32768], input[32768:])) + }) +} + +func TestRunAndLogLinesFlushesBothStreams(t *testing.T) { + for _, failed := range []bool{false, true} { + t.Run(fmt.Sprintf("failed_%t", failed), func(t *testing.T) { + logger, output := newProxyLoggerTestLogger() + runner := &fakeRunner{stdout: `{"value":"out"}`, stderr: "stderr tail"} + if failed { + runner.err = errors.New("hook failed") + } + ex := NewExecutor("", "hook", nil, nil). + WithRunner(runner). + WithLogProxyHookJSON(true). + WithLogger(logger) + + _, err := ex.RunAndLogLines(context.Background(), nil) + if failed { + var stderrErr *StderrError + require.ErrorAs(t, err, &stderrErr) + require.Equal(t, runner.stderr, stderrErr.Message) + } else { + require.NoError(t, err) + } + require.Equal(t, []map[string]any{ + {"level": "info", "msg": "hook result", "hook_value": "out", "output": "stdout", "proxyJsonLog": true}, + {"level": "info", "msg": "stderr tail", "output": "stderr"}, + }, decodeProxyLoggerRecords(t, output.String())) + }) + } +} + +func proxyLoggerRecords(t *testing.T, proxy bool, chunks ...string) []map[string]any { + t.Helper() + logger, output := newProxyLoggerTestLogger() + pl := &proxyLogger{ + ctx: context.Background(), + logProxyHookJSON: proxy, + proxyJsonLogKey: "proxyJsonLog", + logger: logger.With("output", "stdout"), + } + for _, chunk := range chunks { + n, err := pl.Write([]byte(chunk)) + require.NoError(t, err) + require.Equal(t, len(chunk), n) + } + pl.flush() + result := output.String() + pl.flush() + require.Equal(t, result, output.String(), "flushing again must not duplicate records") + return decodeProxyLoggerRecords(t, result) +} + +func newProxyLoggerTestLogger() (*log.Logger, *bytes.Buffer) { + logger := log.NewLogger(log.WithTimeFunc(func(time.Time) time.Time { + return time.Date(2006, time.January, 2, 15, 4, 5, 0, time.UTC) + })) + logger.SetLevel(log.LevelInfo) + output := new(bytes.Buffer) + logger.SetOutput(output) + return logger, output +} + +func decodeProxyLoggerRecords(t *testing.T, output string) []map[string]any { + t.Helper() + var records []map[string]any + for _, line := range strings.Split(strings.TrimSpace(output), "\n") { + if line == "" { + continue + } + var record map[string]any + require.NoError(t, json.Unmarshal([]byte(line), &record)) + require.Equal(t, "2006-01-02T15:04:05Z", record["time"]) + delete(record, "time") + records = append(records, record) + } + return records +} diff --git a/scripts/ci/verify-image.sh b/scripts/ci/verify-image.sh new file mode 100644 index 00000000..0fb27d94 --- /dev/null +++ b/scripts/ci/verify-image.sh @@ -0,0 +1,79 @@ +#!/bin/sh +set -eu + +report_architecture_error() { + cat >&2 <&2 + exit 1 + ;; +esac + +printf '🔍 %s: starting architecture consistency checks for the base image and built binaries.\n' "$platform" + +actual_arch=$(apk --print-arch) +if [ "$actual_arch" != "$apk_arch" ]; then + report_architecture_error "Alpine base image" \ + "$(describe_architecture "$apk_arch")" "$(describe_architecture "$actual_arch")" +fi + +# Emulation can run a binary for the wrong platform, so inspect its ELF header. +for binary in /shell-operator /usr/bin/jq /bin/busybox /sbin/tini /bin/kubectl; do + description=$(file -bL "$binary") + echo "$binary: $description" + if ! printf '%s\n' "$description" | grep -Eq "$elf_pattern"; then + report_architecture_error "$binary" "$expected_cpu" "$(describe_architecture "$description")" + fi +done + +/sbin/tini -- /shell-operator version +/bin/kubectl version --client + +context_file=$(mktemp) +trap 'rm -f "$context_file"' EXIT +printf '%s\n' '[{"binding":"image-verification"}]' > "$context_file" +export BINDING_CONTEXT_PATH="$context_file" +export BINDING_CONTEXT_CURRENT_INDEX=0 +bash -ec 'source /shell_lib.sh; test "$(context::get --required binding)" = image-verification' + +printf '✅ %s: Alpine and all 5 checked binaries have the expected CPU architecture.\n' "$platform" From 599bbb2248636e4ddebf74827cc2279ee7a8cb80 Mon Sep 17 00:00:00 2001 From: skudin13 Date: Fri, 30 Oct 2026 11:15:40 +0100 Subject: [PATCH 2/2] Align Go deps with upstream: controller-runtime v0.23.3, k8s.io v0.35.8 The rebase added controller-runtime v0.25.2 for certwatcher, which pulled k8s.io/* to v0.37.0. That breaks the upstream `make generate` check (it wants kubectl v1.37.0) and is newer than our clusters (1.35-1.36). Use the controller-runtime version upstream already has (v0.23.3, which includes certwatcher) and go back to upstream's k8s.io v0.35.8. go.mod is now upstream's plus the direct controller-runtime and go-logr requirements. make generate: no changes. Tests pass. Wiz on the image: 0 Critical / 0 High. --- go.mod | 54 +++++++++++------------ go.sum | 132 ++++++++++++++++++++++++++++----------------------------- 2 files changed, 91 insertions(+), 95 deletions(-) diff --git a/go.mod b/go.mod index 0a7c1005..d2fdffe7 100644 --- a/go.mod +++ b/go.mod @@ -9,14 +9,14 @@ require ( github.com/go-chi/chi/v5 v5.3.0 github.com/go-openapi/spec v0.22.1 github.com/go-openapi/strfmt v0.26.2 - github.com/go-openapi/swag v0.27.1 // indirect + github.com/go-openapi/swag v0.26.0 // indirect github.com/go-openapi/validate v0.25.1 github.com/gofrs/uuid/v5 v5.4.0 github.com/hashicorp/go-multierror v1.1.1 github.com/kennygrant/sanitize v1.2.4 github.com/onsi/gomega v1.42.0 github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.24.0 + github.com/prometheus/client_golang v1.23.2 github.com/stretchr/testify v1.11.1 go.opentelemetry.io/otel v1.45.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.45.0 @@ -25,10 +25,10 @@ require ( golang.org/x/time v0.15.0 gopkg.in/robfig/cron.v2 v2.0.0-20150107220207-be2e0b0deed5 gopkg.in/yaml.v3 v3.0.1 - k8s.io/api v0.37.0 - k8s.io/apiextensions-apiserver v0.37.0 - k8s.io/apimachinery v0.37.0 - k8s.io/client-go v0.37.0 + k8s.io/api v0.35.8 + k8s.io/apiextensions-apiserver v0.35.8 + k8s.io/apimachinery v0.35.8 + k8s.io/client-go v0.35.8 sigs.k8s.io/yaml v1.6.0 ) @@ -38,7 +38,8 @@ replace github.com/go-openapi/validate => github.com/flant/go-openapi-validate v require ( github.com/caarlos0/env/v11 v11.4.1 github.com/deckhouse/module-sdk v0.13.0 - github.com/go-openapi/swag/yamlutils v0.27.1 + github.com/go-logr/logr v1.4.4 + github.com/go-openapi/swag/yamlutils v0.26.0 github.com/goccy/go-json v0.10.6 github.com/gojuno/minimock/v3 v3.4.7 github.com/itchyny/gojq v0.12.19 @@ -47,7 +48,7 @@ require ( github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 go.uber.org/goleak v1.3.0 - sigs.k8s.io/controller-runtime v0.25.2 + sigs.k8s.io/controller-runtime v0.23.3 ) require ( @@ -65,25 +66,23 @@ require ( github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fxamacker/cbor/v2 v2.9.2 // indirect github.com/go-errors/errors v1.4.2 // indirect - github.com/go-logr/logr v1.4.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/analysis v0.24.1 // indirect github.com/go-openapi/errors v0.22.7 // indirect - github.com/go-openapi/jsonpointer v1.0.0 // indirect - github.com/go-openapi/jsonreference v1.0.0 // indirect + github.com/go-openapi/jsonpointer v0.23.1 // indirect + github.com/go-openapi/jsonreference v0.21.6 // indirect github.com/go-openapi/loads v0.23.2 // indirect github.com/go-openapi/runtime v0.29.2 // indirect - github.com/go-openapi/swag/cmdutils v0.27.1 // indirect - github.com/go-openapi/swag/conv v0.27.1 // indirect - github.com/go-openapi/swag/fileutils v0.27.1 // 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.27.1 // indirect - github.com/go-openapi/swag/loading v0.27.1 // indirect - github.com/go-openapi/swag/mangling v0.27.1 // indirect - github.com/go-openapi/swag/netutils v0.27.1 // indirect - github.com/go-openapi/swag/pools v0.27.1 // indirect - github.com/go-openapi/swag/stringutils v0.27.1 // indirect - github.com/go-openapi/swag/typeutils v0.27.1 // 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-task/slim-sprig/v3 v3.0.0 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/google/btree v1.1.3 // indirect @@ -92,6 +91,7 @@ require ( github.com/google/go-containerregistry v0.21.3 // indirect github.com/google/pprof v0.0.0-20260402051712-545e8a4df936 // indirect github.com/google/uuid v1.6.0 // indirect + github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -108,8 +108,8 @@ require ( github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.70.0 // indirect - github.com/prometheus/procfs v0.21.1 // indirect + github.com/prometheus/common v0.66.1 // indirect + github.com/prometheus/procfs v0.16.1 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/sergi/go-diff v1.3.1 // indirect github.com/tidwall/gjson v1.19.0 // indirect @@ -123,7 +123,7 @@ require ( go.opentelemetry.io/proto/otlp v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f // indirect + golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc // indirect golang.org/x/mod v0.41.0 // indirect golang.org/x/net v0.60.0 // indirect golang.org/x/oauth2 v0.36.0 // indirect @@ -140,11 +140,11 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect k8s.io/cli-runtime v0.35.8 // indirect k8s.io/klog/v2 v2.140.0 // indirect - k8s.io/kube-openapi v0.0.0-20260721132016-d427ff9ee9ad // indirect - k8s.io/utils v0.0.0-20260626114624-be93311217bd // indirect + k8s.io/kube-openapi v0.0.0-20260603220949-865597e52e25 // indirect + k8s.io/utils v0.0.0-20260507154919-ff6756f316d2 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/kustomize/api v0.20.1 // indirect sigs.k8s.io/kustomize/kyaml v0.20.1 // indirect sigs.k8s.io/randfill v1.0.0 // indirect - sigs.k8s.io/structured-merge-diff/v6 v6.4.2 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.3.2 // indirect ) diff --git a/go.sum b/go.sum index 102d625e..28c09dc5 100644 --- a/go.sum +++ b/go.sum @@ -83,13 +83,13 @@ github.com/go-openapi/errors v0.22.7/go.mod h1://QW6SD9OsWtH6gHllUCddOXDL0tk0ZGN github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v1.0.0 h1:kR9tHqY0CtZaOPVFm622dPVNhrvYpwr4uCxgL3h1H8s= -github.com/go-openapi/jsonpointer v1.0.0/go.mod h1:Z3rw7dWu1p9IgitXCFamSlA5lmDiklEB6vkaxcNZW5Y= +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.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/jsonreference v1.0.0 h1:jlmTr6torcd1YgDQvSfNmRtKzYDO4FGBkrAdlAVWnpY= -github.com/go-openapi/jsonreference v1.0.0/go.mod h1:jtwdyGbJk0Xhe5Y+rwtglQP6Sb1WZST4rT32LWB+sv0= +github.com/go-openapi/jsonreference v0.21.6 h1:NZ5nGfnaM1n4I43Xjm1e5/M2GjOwQwndQz22uhxwD+Y= +github.com/go-openapi/jsonreference v0.21.6/go.mod h1:xzbgtQ3ZbWxvET3AxdzCJlJt6vkovbf+IfSPJjD0tUY= github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= github.com/go-openapi/loads v0.19.5/go.mod h1:dswLCAdonkRufe/gSUC3gN8nTSaB9uaS2es0x5/IbjY= github.com/go-openapi/loads v0.23.2 h1:rJXAcP7g1+lWyBHC7iTY+WAF0rprtM+pm8Jxv1uQJp4= @@ -112,38 +112,36 @@ github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.7/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= github.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= -github.com/go-openapi/swag v0.27.1 h1:VotvOLWW8q/EAxB0YdsBBGC8XYyeL1YwBj2ungAGPNg= -github.com/go-openapi/swag v0.27.1/go.mod h1:GTkJPwHfhJp6MWr4/rCh64HVI3Ofu+tcsbfjfHmTxpE= -github.com/go-openapi/swag/cmdutils v0.27.1 h1:I7sYqaWVl5mq0NEmNQkAmFDyNin9ufvMX/p2zwtQaOE= -github.com/go-openapi/swag/cmdutils v0.27.1/go.mod h1:Sm1MVFMkF6guJJ+pQqHnQA3N0j9qALV3NxzDSv6bETM= -github.com/go-openapi/swag/conv v0.27.1 h1:8wi9ZG+olmY1wXphl93EWniPtbSPkXM/feH7FgjsvrU= -github.com/go-openapi/swag/conv v0.27.1/go.mod h1:QbqMivkpKhC3g1B1GGGOJ6ANewI3S62dbzYu3Duowqs= -github.com/go-openapi/swag/fileutils v0.27.1 h1:QQqBSoi5mW4XpU85nS0mLcA+zAE6vLzrb0QkmLKf9oM= -github.com/go-openapi/swag/fileutils v0.27.1/go.mod h1:VvJFZLTZS0AI854gEQz5tk7dBESdLjiNUMSZ/th2ry8= +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.27.1 h1:SVgK3i4USzCU5mibOOS/l4ea2h9UQXy7J7RNLTjuXjU= -github.com/go-openapi/swag/jsonutils v0.27.1/go.mod h1:tdlEpZqdcQ17uj6J4YdK9vd8It5qWMwjWXOs0tjpRlk= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.27.1 h1:mJu3COL9WEaZVp/Kf2PRMi7tPszPEJfSr/OO75ynCs8= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.27.1/go.mod h1:mofwUWx70wvskwESqRJ//k/9kURmCgyJl5m5Ppoh5kY= -github.com/go-openapi/swag/loading v0.27.1 h1:/DxUgDXKbBX4bcn7r9uEXfJyzN5XpiJmZplzQTjrRCY= -github.com/go-openapi/swag/loading v0.27.1/go.mod h1:jvGh3iA2+zyUUycB5fgJWzeHnhrpvGnJJM0RVE9ZShE= -github.com/go-openapi/swag/mangling v0.27.1 h1:yC9D0HyUE8gbP+BfmGx9+AA89ikwZTMjESK3OnnoaqA= -github.com/go-openapi/swag/mangling v0.27.1/go.mod h1:jtBE2+V+3pILxOR7Vgce+Cwp6A2PgZbvVqfNntbVs0w= -github.com/go-openapi/swag/netutils v0.27.1 h1:mICMFoS82F5TZ4Zy3cqmcQk+BFeCp3Uyq3Np7GI0/qU= -github.com/go-openapi/swag/netutils v0.27.1/go.mod h1:J+WYyFMLtvtCGqa6jLv+YNUmIKI3ZRQRrvfNDMoQoEQ= -github.com/go-openapi/swag/pools v0.27.1 h1:9LeadcMyb2GJCbXX5hVQDbZ2Lq9TL4dCs/nx1j5DO0E= -github.com/go-openapi/swag/pools v0.27.1/go.mod h1:kVQefhSK5RWuRe7BXsL8htgBPAMpN7HDGpGEknqugeE= -github.com/go-openapi/swag/stringutils v0.27.1 h1:ZXePZ0r2p1qSjo8tD3Un4vFj8+FqlCkczxDrJIhYUp8= -github.com/go-openapi/swag/stringutils v0.27.1/go.mod h1:lzRN95CxXmA03XcDWHLOb6nOMcxCqR5rGY0lOgsfRoM= -github.com/go-openapi/swag/typeutils v0.27.1 h1:KSTdFlfnse4r6dP9IrEnwMldjE+zs71UeEB3//PtVXc= -github.com/go-openapi/swag/typeutils v0.27.1/go.mod h1:Srm0xFNRZ1Y+vCxJclo5qzx8aj+1pAKda/YfFPrG0dQ= -github.com/go-openapi/swag/yamlutils v0.27.1 h1:ftxv6xvXb1E3zohUc+okZ9nSqNb9StQX/FXnKZ98sQA= -github.com/go-openapi/swag/yamlutils v0.27.1/go.mod h1:bnxFIB1qewGRiZHypXGZ3fNgf13/0HfRgnS/iZBDrOo= -github.com/go-openapi/testify/enable/yaml/v2 v2.6.0 h1:gGHwAJ0R/5jU8BEGDbfRNR3hL68dAVi84WuOApp29B0= -github.com/go-openapi/testify/enable/yaml/v2 v2.6.0/go.mod h1:tY+St1SGq4NFl0QIqdTY4aEdbChAHxhyB77XQi9iJCo= -github.com/go-openapi/testify/v2 v2.6.0 h1:5PKH2HE7YJ/LuRPQGvSxBRlFXNQhSetBLlGAgUEu3ug= -github.com/go-openapi/testify/v2 v2.6.0/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw= +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.5.1 h1:TMdhCaw8fUNraVSf3Omoob1dO/AzBfhtFAPW0an6sBo= +github.com/go-openapi/testify/v2 v2.5.1/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 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= @@ -201,6 +199,8 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= +github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk= github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -228,8 +228,8 @@ github.com/kennygrant/sanitize v1.2.4 h1:gN25/otpP5vAsO2djbMhF/LQX6R7+O1TB4yv8Nz github.com/kennygrant/sanitize v1.2.4/go.mod h1:LGsjYYtgxbetdg5owWB2mpgUL6e2nfw2eObZ0u0qvak= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.19.0 h1:sXLILfc9jV2QYWkzFOPWStmcUVH2RHEB1JCdY2oVvCQ= -github.com/klauspost/compress v1.19.0/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= +github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE= +github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -290,14 +290,14 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE 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/prometheus/client_golang v1.24.0 h1:5XStIklKuAtJSNpdD3s8XJj/Yv78IQmE1kbNk87JrAI= -github.com/prometheus/client_golang v1.24.0/go.mod h1:QcsNdotprC2nS4BTM2ucbcqxd2CeXTEa9jW7zHO9iDE= +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.70.0 h1:bcpru3tWPVnxGnETLgOV5jbp/JRXgYEyv65CuBLAMMI= -github.com/prometheus/common v0.70.0/go.mod h1:S/SFasQmgGiYH6C81LKCtYa8QACgthGg5zxL2udV7SY= -github.com/prometheus/procfs v0.21.1 h1:GljZCt+zSTS+NZq88cyQ1LjZ+RCHp3uVuabBWA5+OJI= -github.com/prometheus/procfs v0.21.1/go.mod h1:aB55Cww9pdSJVHk0hUf0inxWyyjPogFIjmHKYgMKmtY= +github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs= +github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -322,8 +322,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= -github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= +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.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -372,8 +372,8 @@ 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.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= -go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +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= @@ -383,8 +383,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM= -golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f/go.mod h1:J1xhfL/vlindoeF/aINzNzt2Bket5bjo9sdOYzOsU80= +golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc h1:TS73t7x3KarrNd5qAipmspBDS1rkMcgVG/fS1aRb4Rc= +golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc= golang.org/x/mod v0.41.0 h1:qJmnOUb4YB+FsEuM3HcWucdZASCPGhsX6uljO6pog0c= golang.org/x/mod v0.41.0/go.mod h1:Ek9pY8RKWXwsWvd3rQiHYtMqkjSUV+s1Rj7j4H5Ur6o= golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -437,12 +437,8 @@ gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw gomodules.xyz/jsonpatch/v2 v2.4.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-20260720211330-0afa2a65878a h1:97PfJ4tCxY5C7NzzgGqQEMZmXbISdvSArNNEOoUGKBg= -google.golang.org/genproto/googleapis/api v0.0.0-20260720211330-0afa2a65878a/go.mod h1:1brfde68Npq6+WA75c1EHWPijZEG1kMus61ygPZfn4A= google.golang.org/genproto/googleapis/api v0.0.0-20260803160001-6ac0973c030d h1:FarXi840EJWSHYTN3ERkADbPWjl307+FGrA22KAVjjc= google.golang.org/genproto/googleapis/api v0.0.0-20260803160001-6ac0973c030d/go.mod h1:K/+WGbmBY7aNW1HDw1fJnKYo10i0DkAX6pows00dLig= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260720211330-0afa2a65878a h1:qI/YMH1ep2qQtqcp00gMQyoU7mjvbhg88GJKCvfoLj0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260720211330-0afa2a65878a/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/genproto/googleapis/rpc v0.0.0-20260803160001-6ac0973c030d h1:IL4hdHzcUv2l/gcg98/Rj3FbtE6axwqslOW8SW0C+S0= google.golang.org/genproto/googleapis/rpc v0.0.0-20260803160001-6ac0973c030d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/grpc v1.83.2 h1:EManeRomTObA0BU7I8vXgg/78uE5MJ9M8B39EX2WscU= @@ -473,24 +469,24 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.37.0 h1:Z//Vj9N7RA/yS2sDmxyeo7h+RR4zbUrd2vrd3Z0TbB4= -k8s.io/api v0.37.0/go.mod h1:LKXgcJWMc+f4OLbP5SFR8rulEg07zZhpi/zMULiBImk= -k8s.io/apiextensions-apiserver v0.37.0 h1:zRMQ3+/LIE5oZ0tVvXwYHC+dIkSP5cjNWju7AZU1LOI= -k8s.io/apiextensions-apiserver v0.37.0/go.mod h1:HU0PfSBwchHL5iDau6jjt9zU6ryWkDDlaVUiq91NK80= -k8s.io/apimachinery v0.37.0 h1:Np2AbDtf8x6RDHiD8T9LbKJ9gaegeVNa8yNm5FuGKm0= -k8s.io/apimachinery v0.37.0/go.mod h1:RN3nhprFSCxOi5Selxd7oMTXOe/c+ZbcE7Im+TS2zkE= +k8s.io/api v0.35.8 h1:hxpmPYdneQPKNh0cZyB09Hwd3vgXzdcJs5R3toDXsvU= +k8s.io/api v0.35.8/go.mod h1:I5gVNknFd4hfVVcMCixrenD7V38JUY78q3jtpGyC19c= +k8s.io/apiextensions-apiserver v0.35.8 h1:2lvyZ28M01/1uOPftaPXIbsOaCslzKHTEcdCjCI0VRE= +k8s.io/apiextensions-apiserver v0.35.8/go.mod h1:/ZbM1upeajFY5yHqGMKEzWTLb5Tx3kU2SkxTCtqB4Uw= +k8s.io/apimachinery v0.35.8 h1:piOyQQgse1sGztJVfy3B8f11YpT+KwK5KkD5Jie1EK0= +k8s.io/apimachinery v0.35.8/go.mod h1:z9Vq5oR1X38pkhh0wV531iKSeqmOVjqgHdYMjvzq2+o= k8s.io/cli-runtime v0.35.8 h1:5rfvENhl4HklU8wUHVRPwUH/xHxgrK3Ly9c0dU4nDCg= k8s.io/cli-runtime v0.35.8/go.mod h1:TSinz+vrk8BTO2+Kd6BePZyW40YuK9W8m+5V4X6fTjQ= -k8s.io/client-go v0.37.0 h1:nsN31fy8wBySuZ+QRnKmrjRSQLOG2rvoGN0tKd12zhQ= -k8s.io/client-go v0.37.0/go.mod h1:FcGqw+Ll/gNQiq+nPGY1Oyt9y7SgDh1d3MW3RFDEbn0= +k8s.io/client-go v0.35.8 h1:tIW2sirCQMiGoCSvtOYqS059CDQ5n1nrDQa+PVt4nqY= +k8s.io/client-go v0.35.8/go.mod h1:fT8dATMU8FHMq4hlOudbsxihQ1LIQfDaLNDXBnIk6OQ= 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-20260721132016-d427ff9ee9ad h1:oXImqH8mQNk7PmvzKhmN3ddJoY6OnyM225MXwGHPm0A= -k8s.io/kube-openapi v0.0.0-20260721132016-d427ff9ee9ad/go.mod h1:0/mqHCVhlumdJ3BhCfnjSZQE037nAhNodh1/hK0T8/I= -k8s.io/utils v0.0.0-20260626114624-be93311217bd h1:Ea7fgQ5we8Y9T0OX5o0dAHzQOBRI07D/dEYRaB9ZZEs= -k8s.io/utils v0.0.0-20260626114624-be93311217bd/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= -sigs.k8s.io/controller-runtime v0.25.2 h1:bEkK3PVOIVK9X8QWLGVhJgmFc++47vfT6wakSzAOLsQ= -sigs.k8s.io/controller-runtime v0.25.2/go.mod h1:4QqLdT6z/L6Olj8JJCtvztid4/fnIiYsfaTFScegctc= +k8s.io/kube-openapi v0.0.0-20260603220949-865597e52e25 h1:mPMaPMpBij2V1Wv/fR+HW124vVGXXvOSS9ver/9yjWs= +k8s.io/kube-openapi v0.0.0-20260603220949-865597e52e25/go.mod h1:V/QaCUYDa+0QpcHhVVc5l99Uz56wEMEXBSj9oCDkNDY= +k8s.io/utils v0.0.0-20260507154919-ff6756f316d2 h1:wU4tMEhLGgIbLvXQb1cfN+EcM0wf7zC6CPF+C79jroc= +k8s.io/utils v0.0.0-20260507154919-ff6756f316d2/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/kustomize/api v0.20.1 h1:iWP1Ydh3/lmldBnH/S5RXgT98vWYMaTUL1ADcr+Sv7I= @@ -499,7 +495,7 @@ sigs.k8s.io/kustomize/kyaml v0.20.1 h1:PCMnA2mrVbRP3NIB6v9kYCAc38uvFLVs8j/CD567A sigs.k8s.io/kustomize/kyaml v0.20.1/go.mod h1:0EmkQHRUsJxY8Ug9Niig1pUMSCGHxQ5RklbpV/Ri6po= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= -sigs.k8s.io/structured-merge-diff/v6 v6.4.2 h1:qdOxHwrl2Kaag1aQEarlYcOA9vSyGCp3CIki3aW8c4Q= -sigs.k8s.io/structured-merge-diff/v6 v6.4.2/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/structured-merge-diff/v6 v6.3.2 h1:kwVWMx5yS1CrnFWA/2QHyRVJ8jM6dBA80uLmm0wJkk8= +sigs.k8s.io/structured-merge-diff/v6 v6.3.2/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=