From 74315cac7bd5ce219528bfea14295ddcd2d1c68e Mon Sep 17 00:00:00 2001 From: Jefferson Ramos Date: Fri, 10 Jul 2026 12:35:18 -0300 Subject: [PATCH] pkg/agenticrun: rename Proposal API to AgenticRun The upstream lightspeed-agentic-operator renamed the Proposal CRD to AgenticRun (OLS-3295). Align CVO by renaming pkg/proposal to pkg/agenticrun and updating all references: types, functions, variables, log strings, env vars, integration tests, and the lightspeed prompt ConfigMap according to OTA-2064. Updates the lightspeed-agentic-operator/api dependency to pick up the renamed types. Co-Authored-By: Claude Opus 4.6 --- ...hift_payload_cluster-version-operator.json | 2 +- go.mod | 26 +- go.sum | 52 +- ...ersion-operator_50_lightspeed-prompts.yaml | 2 +- .../analysis_schema.json | 0 pkg/{proposal => agenticrun}/controller.go | 140 +- .../controller_test.go | 278 +- pkg/cvo/availableupdates.go | 6 +- pkg/cvo/availableupdates_test.go | 4 +- pkg/cvo/cvo.go | 24 +- pkg/cvo/cvo_test.go | 4 +- pkg/cvo/status.go | 6 +- pkg/internal/schema.go | 8 +- test/cvo/{proposal.go => agenticrun.go} | 30 +- .../fxamacker/cbor/v2/.golangci.yml | 176 +- vendor/github.com/fxamacker/cbor/v2/README.md | 29 +- vendor/github.com/fxamacker/cbor/v2/cache.go | 257 +- vendor/github.com/fxamacker/cbor/v2/decode.go | 442 +- .../fxamacker/cbor/v2/decode_map_utils.go | 98 + .../github.com/fxamacker/cbor/v2/diagnose.go | 25 +- vendor/github.com/fxamacker/cbor/v2/doc.go | 20 +- vendor/github.com/fxamacker/cbor/v2/encode.go | 55 +- .../fxamacker/cbor/v2/simplevalue.go | 2 +- vendor/github.com/fxamacker/cbor/v2/stream.go | 177 +- .../fxamacker/cbor/v2/structfields.go | 65 +- vendor/github.com/fxamacker/cbor/v2/tag.go | 5 + vendor/github.com/fxamacker/cbor/v2/valid.go | 20 +- .../api/v1alpha1/agent_types.go | 2 +- .../api/v1alpha1/agenticolsconfig_types.go | 201 + ..._types.go => agenticrun_analysis_types.go} | 51 +- ...us_types.go => agenticrun_status_types.go} | 4 +- ...{proposal_types.go => agenticrun_types.go} | 174 +- ...l_types.go => agenticrunapproval_types.go} | 73 +- .../api/v1alpha1/analysisresult_types.go | 8 +- .../api/v1alpha1/approvalpolicy_types.go | 18 +- .../api/v1alpha1/escalationresult_types.go | 8 +- .../api/v1alpha1/executionresult_types.go | 8 +- .../api/v1alpha1/llmprovider_types.go | 24 +- .../api/v1alpha1/result_types.go | 14 +- .../api/v1alpha1/shared_types.go | 2 +- .../api/v1alpha1/tools_types.go | 6 +- .../api/v1alpha1/verificationresult_types.go | 8 +- .../api/v1alpha1/zz_generated.deepcopy.go | 640 +- vendor/golang.org/x/net/html/entity.go | 5 +- vendor/golang.org/x/net/html/escape.go | 140 +- vendor/golang.org/x/net/html/foreign.go | 2 +- vendor/golang.org/x/net/html/iter.go | 2 - vendor/golang.org/x/net/html/parse.go | 287 +- vendor/golang.org/x/net/html/render.go | 35 +- vendor/golang.org/x/net/html/token.go | 52 +- .../golang.org/x/net/http/httpproxy/proxy.go | 8 +- vendor/golang.org/x/net/http2/README.md | 19 + .../x/net/http2/client_conn_pool.go | 14 +- vendor/golang.org/x/net/http2/clientconn.go | 57 + vendor/golang.org/x/net/http2/config.go | 2 + vendor/golang.org/x/net/http2/frame.go | 4 +- vendor/golang.org/x/net/http2/http2.go | 18 +- vendor/golang.org/x/net/http2/server.go | 212 +- .../golang.org/x/net/http2/server_common.go | 221 + vendor/golang.org/x/net/http2/server_wrap.go | 217 + vendor/golang.org/x/net/http2/transport.go | 470 +- .../x/net/http2/transport_common.go | 447 ++ .../golang.org/x/net/http2/transport_wrap.go | 392 ++ vendor/golang.org/x/net/http2/writesched.go | 44 +- .../x/net/http2/writesched_common.go | 90 + .../net/http2/writesched_priority_rfc7540.go | 44 +- .../net/http2/writesched_priority_rfc9218.go | 2 + .../x/net/http2/writesched_random.go | 4 + .../x/net/http2/writesched_roundrobin.go | 2 + vendor/golang.org/x/net/idna/go118.go | 13 - .../x/net/idna/{idna10.0.0.go => idna.go} | 181 +- vendor/golang.org/x/net/idna/idna9.0.0.go | 717 --- vendor/golang.org/x/net/idna/pre_go118.go | 11 - vendor/golang.org/x/net/idna/punycode.go | 5 +- vendor/golang.org/x/net/idna/tables10.0.0.go | 4559 -------------- vendor/golang.org/x/net/idna/tables11.0.0.go | 4653 --------------- vendor/golang.org/x/net/idna/tables12.0.0.go | 4733 --------------- vendor/golang.org/x/net/idna/tables13.0.0.go | 4959 --------------- vendor/golang.org/x/net/idna/tables15.0.0.go | 2 +- vendor/golang.org/x/net/idna/tables17.0.0.go | 5302 +++++++++++++++++ vendor/golang.org/x/net/idna/tables9.0.0.go | 4486 -------------- vendor/golang.org/x/net/idna/trie12.0.0.go | 30 - vendor/golang.org/x/net/idna/trie13.0.0.go | 30 - .../x/net/internal/httpcommon/request.go | 8 + vendor/golang.org/x/sync/errgroup/errgroup.go | 2 +- .../golang.org/x/sys/plan9/syscall_plan9.go | 8 +- .../golang.org/x/sys/unix/affinity_linux.go | 128 +- vendor/golang.org/x/sys/unix/mkall.sh | 2 +- vendor/golang.org/x/sys/unix/mkerrors.sh | 3 + vendor/golang.org/x/sys/unix/readv_unix.go | 103 + .../golang.org/x/sys/unix/syscall_darwin.go | 89 - vendor/golang.org/x/sys/unix/syscall_linux.go | 114 +- .../x/sys/unix/syscall_linux_arm.go | 3 + .../x/sys/unix/syscall_linux_arm64.go | 3 + .../x/sys/unix/syscall_linux_loong64.go | 3 + .../x/sys/unix/syscall_linux_riscv64.go | 3 + .../golang.org/x/sys/unix/syscall_openbsd.go | 4 + vendor/golang.org/x/sys/unix/zerrors_linux.go | 61 +- .../x/sys/unix/zerrors_linux_386.go | 7 +- .../x/sys/unix/zerrors_linux_amd64.go | 7 +- .../x/sys/unix/zerrors_linux_arm.go | 7 +- .../x/sys/unix/zerrors_linux_arm64.go | 7 +- .../x/sys/unix/zerrors_linux_loong64.go | 7 +- .../x/sys/unix/zerrors_linux_mips.go | 7 +- .../x/sys/unix/zerrors_linux_mips64.go | 7 +- .../x/sys/unix/zerrors_linux_mips64le.go | 7 +- .../x/sys/unix/zerrors_linux_mipsle.go | 7 +- .../x/sys/unix/zerrors_linux_ppc.go | 7 +- .../x/sys/unix/zerrors_linux_ppc64.go | 7 +- .../x/sys/unix/zerrors_linux_ppc64le.go | 7 +- .../x/sys/unix/zerrors_linux_riscv64.go | 1114 ++-- .../x/sys/unix/zerrors_linux_s390x.go | 7 +- .../x/sys/unix/zerrors_linux_sparc64.go | 7 +- .../golang.org/x/sys/unix/zsyscall_linux.go | 12 +- .../x/sys/unix/zsyscall_openbsd_386.go | 84 + .../x/sys/unix/zsyscall_openbsd_386.s | 20 + .../x/sys/unix/zsyscall_openbsd_amd64.go | 84 + .../x/sys/unix/zsyscall_openbsd_amd64.s | 20 + .../x/sys/unix/zsyscall_openbsd_arm.go | 84 + .../x/sys/unix/zsyscall_openbsd_arm.s | 20 + .../x/sys/unix/zsyscall_openbsd_arm64.go | 84 + .../x/sys/unix/zsyscall_openbsd_arm64.s | 20 + .../x/sys/unix/zsyscall_openbsd_mips64.go | 84 + .../x/sys/unix/zsyscall_openbsd_mips64.s | 20 + .../x/sys/unix/zsyscall_openbsd_ppc64.go | 84 + .../x/sys/unix/zsyscall_openbsd_ppc64.s | 24 + .../x/sys/unix/zsyscall_openbsd_riscv64.go | 84 + .../x/sys/unix/zsyscall_openbsd_riscv64.s | 20 + .../x/sys/unix/zsysnum_linux_386.go | 4 + .../x/sys/unix/zsysnum_linux_amd64.go | 5 + .../x/sys/unix/zsysnum_linux_arm.go | 4 + .../x/sys/unix/zsysnum_linux_arm64.go | 4 + .../x/sys/unix/zsysnum_linux_loong64.go | 5 + .../x/sys/unix/zsysnum_linux_mips.go | 4 + .../x/sys/unix/zsysnum_linux_mips64.go | 4 + .../x/sys/unix/zsysnum_linux_mips64le.go | 4 + .../x/sys/unix/zsysnum_linux_mipsle.go | 4 + .../x/sys/unix/zsysnum_linux_ppc.go | 4 + .../x/sys/unix/zsysnum_linux_ppc64.go | 4 + .../x/sys/unix/zsysnum_linux_ppc64le.go | 4 + .../x/sys/unix/zsysnum_linux_riscv64.go | 4 + .../x/sys/unix/zsysnum_linux_s390x.go | 4 + .../x/sys/unix/zsysnum_linux_sparc64.go | 5 + vendor/golang.org/x/sys/unix/ztypes_linux.go | 352 +- .../golang.org/x/sys/unix/ztypes_linux_386.go | 12 + .../x/sys/unix/ztypes_linux_amd64.go | 12 + .../golang.org/x/sys/unix/ztypes_linux_arm.go | 12 + .../x/sys/unix/ztypes_linux_arm64.go | 12 + .../x/sys/unix/ztypes_linux_loong64.go | 12 + .../x/sys/unix/ztypes_linux_mips.go | 12 + .../x/sys/unix/ztypes_linux_mips64.go | 12 + .../x/sys/unix/ztypes_linux_mips64le.go | 12 + .../x/sys/unix/ztypes_linux_mipsle.go | 12 + .../golang.org/x/sys/unix/ztypes_linux_ppc.go | 12 + .../x/sys/unix/ztypes_linux_ppc64.go | 12 + .../x/sys/unix/ztypes_linux_ppc64le.go | 12 + .../x/sys/unix/ztypes_linux_riscv64.go | 12 + .../x/sys/unix/ztypes_linux_s390x.go | 12 + .../x/sys/unix/ztypes_linux_sparc64.go | 12 + vendor/golang.org/x/sys/windows/aliases.go | 1 + .../golang.org/x/sys/windows/dll_windows.go | 37 +- .../x/sys/windows/security_windows.go | 6 +- .../x/sys/windows/syscall_windows.go | 30 +- .../golang.org/x/sys/windows/types_windows.go | 33 +- .../x/sys/windows/zsyscall_windows.go | 71 + .../x/text/unicode/norm/forminfo.go | 9 +- vendor/golang.org/x/text/unicode/norm/iter.go | 8 +- .../x/text/unicode/norm/normalize.go | 20 +- vendor/golang.org/x/tools/go/ast/edge/edge.go | 24 +- .../go-json-experiment/json/alias.go | 967 --- .../go-json-experiment/json/arshal.go | 2 - .../go-json-experiment/json/arshal_any.go | 2 - .../go-json-experiment/json/arshal_default.go | 2 - .../go-json-experiment/json/arshal_funcs.go | 2 - .../go-json-experiment/json/arshal_inlined.go | 2 - .../go-json-experiment/json/arshal_methods.go | 2 - .../go-json-experiment/json/arshal_time.go | 2 - .../go-json-experiment/json/doc.go | 2 - .../go-json-experiment/json/errors.go | 2 - .../go-json-experiment/json/fields.go | 2 - .../go-json-experiment/json/fold.go | 2 - .../go-json-experiment/json/intern.go | 2 - .../json/internal/internal.go | 2 - .../json/internal/jsonflags/flags.go | 2 - .../json/internal/jsonopts/options.go | 2 - .../json/internal/jsonwire/decode.go | 2 - .../json/internal/jsonwire/encode.go | 2 - .../json/internal/jsonwire/wire.go | 2 - .../go-json-experiment/json/jsontext/alias.go | 536 -- .../json/jsontext/decode.go | 2 - .../go-json-experiment/json/jsontext/doc.go | 2 - .../json/jsontext/encode.go | 2 - .../json/jsontext/errors.go | 2 - .../json/jsontext/export.go | 2 - .../json/jsontext/options.go | 2 - .../go-json-experiment/json/jsontext/pools.go | 2 - .../go-json-experiment/json/jsontext/quote.go | 2 - .../go-json-experiment/json/jsontext/state.go | 2 - .../go-json-experiment/json/jsontext/token.go | 2 - .../go-json-experiment/json/jsontext/value.go | 2 - .../go-json-experiment/json/options.go | 2 - .../kube-openapi/pkg/schemaconv/openapi.go | 5 +- vendor/k8s.io/utils/ptr/OWNERS | 2 + vendor/k8s.io/utils/ptr/ptr.go | 2 +- vendor/modules.txt | 40 +- .../v6/fieldpath/element.go | 28 +- .../v6/fieldpath/pathelementmap.go | 25 +- .../v6/fieldpath/serialize-pe.go | 18 +- .../v6/fieldpath/serialize.go | 11 + .../structured-merge-diff/v6/fieldpath/set.go | 24 +- .../v6/schema/elements.go | 7 + .../structured-merge-diff/v6/schema/equals.go | 3 + .../v6/schema/schemaschema.go | 3 + .../structured-merge-diff/v6/typed/remove.go | 20 +- .../v6/value/allocator.go | 80 +- .../v6/value/jsontagutil.go | 7 +- .../v6/value/reflectcache.go | 77 +- .../structured-merge-diff/v6/value/value.go | 5 + 218 files changed, 11735 insertions(+), 29174 deletions(-) rename pkg/{proposal => agenticrun}/analysis_schema.json (100%) rename pkg/{proposal => agenticrun}/controller.go (69%) rename pkg/{proposal => agenticrun}/controller_test.go (84%) rename test/cvo/{proposal.go => agenticrun.go} (76%) create mode 100644 vendor/github.com/fxamacker/cbor/v2/decode_map_utils.go create mode 100644 vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/agenticolsconfig_types.go rename vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/{proposal_analysis_types.go => agenticrun_analysis_types.go} (88%) rename vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/{proposal_status_types.go => agenticrun_status_types.go} (99%) rename vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/{proposal_types.go => agenticrun_types.go} (76%) rename vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/{proposalapproval_types.go => agenticrunapproval_types.go} (79%) create mode 100644 vendor/golang.org/x/net/http2/README.md create mode 100644 vendor/golang.org/x/net/http2/clientconn.go create mode 100644 vendor/golang.org/x/net/http2/server_common.go create mode 100644 vendor/golang.org/x/net/http2/server_wrap.go create mode 100644 vendor/golang.org/x/net/http2/transport_common.go create mode 100644 vendor/golang.org/x/net/http2/transport_wrap.go create mode 100644 vendor/golang.org/x/net/http2/writesched_common.go delete mode 100644 vendor/golang.org/x/net/idna/go118.go rename vendor/golang.org/x/net/idna/{idna10.0.0.go => idna.go} (81%) delete mode 100644 vendor/golang.org/x/net/idna/idna9.0.0.go delete mode 100644 vendor/golang.org/x/net/idna/pre_go118.go delete mode 100644 vendor/golang.org/x/net/idna/tables10.0.0.go delete mode 100644 vendor/golang.org/x/net/idna/tables11.0.0.go delete mode 100644 vendor/golang.org/x/net/idna/tables12.0.0.go delete mode 100644 vendor/golang.org/x/net/idna/tables13.0.0.go create mode 100644 vendor/golang.org/x/net/idna/tables17.0.0.go delete mode 100644 vendor/golang.org/x/net/idna/tables9.0.0.go delete mode 100644 vendor/golang.org/x/net/idna/trie12.0.0.go delete mode 100644 vendor/golang.org/x/net/idna/trie13.0.0.go create mode 100644 vendor/golang.org/x/sys/unix/readv_unix.go delete mode 100644 vendor/k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json/alias.go delete mode 100644 vendor/k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json/jsontext/alias.go diff --git a/.openshift-tests-extension/openshift_payload_cluster-version-operator.json b/.openshift-tests-extension/openshift_payload_cluster-version-operator.json index e603224f29..18970ce69f 100644 --- a/.openshift-tests-extension/openshift_payload_cluster-version-operator.json +++ b/.openshift-tests-extension/openshift_payload_cluster-version-operator.json @@ -97,7 +97,7 @@ "environmentSelector": {} }, { - "name": "[Jira:\"Cluster Version Operator\"] cluster-version-operator should create proposals", + "name": "[Jira:\"Cluster Version Operator\"] cluster-version-operator should create agentic runs", "labels": { "Lifecycle:informing": {}, "OTA-1966": {}, diff --git a/go.mod b/go.mod index dbde6bb5f7..e2fee0c8af 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/openshift/client-go v0.0.0-20260416131737-a19e91702ab5 github.com/openshift/controller-runtime-common v0.0.0-20260428152732-64ee174f5e2e github.com/openshift/library-go v0.0.0-20260413093329-d2db42c961e1 - github.com/openshift/lightspeed-agentic-operator/api v0.0.0-20260521135452-44bd61b9b92a + github.com/openshift/lightspeed-agentic-operator/api v0.0.0-20260710132625-b6a3700bb634 github.com/operator-framework/api v0.17.1 github.com/operator-framework/operator-lifecycle-manager v0.22.0 github.com/pkg/errors v0.9.1 @@ -25,8 +25,8 @@ require ( github.com/prometheus/client_model v0.6.2 github.com/prometheus/common v0.66.1 github.com/spf13/cobra v1.10.0 - golang.org/x/crypto v0.48.0 - golang.org/x/net v0.50.0 + golang.org/x/crypto v0.53.0 + golang.org/x/net v0.56.0 golang.org/x/time v0.13.0 k8s.io/api v0.35.3 k8s.io/apiextensions-apiserver v0.35.3 @@ -36,7 +36,7 @@ require ( k8s.io/component-base v0.35.3 k8s.io/klog/v2 v2.140.0 k8s.io/kube-aggregator v0.35.1 - k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 + k8s.io/utils v0.0.0-20260626114624-be93311217bd sigs.k8s.io/controller-runtime v0.23.3 sigs.k8s.io/kustomize/kyaml v0.21.1 sigs.k8s.io/yaml v1.6.0 @@ -50,7 +50,7 @@ require ( 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/fxamacker/cbor/v2 v2.9.0 // indirect + github.com/fxamacker/cbor/v2 v2.9.2 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-openapi/jsonpointer v0.22.1 // indirect github.com/go-openapi/jsonreference v0.21.2 // indirect @@ -91,26 +91,26 @@ require ( github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/otel v1.36.0 // indirect go.opentelemetry.io/otel/trace v1.36.0 // indirect - go.yaml.in/yaml/v2 v2.4.3 // 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-20240719175910-8a7402abbf56 // indirect golang.org/x/oauth2 v0.31.0 // indirect - golang.org/x/sync v0.19.0 // indirect - golang.org/x/sys v0.41.0 // indirect - golang.org/x/term v0.40.0 // indirect - golang.org/x/text v0.34.0 // indirect - golang.org/x/tools v0.42.0 // indirect + golang.org/x/sync v0.21.0 // indirect + golang.org/x/sys v0.46.0 // indirect + golang.org/x/term v0.44.0 // indirect + golang.org/x/text v0.39.0 // indirect + golang.org/x/tools v0.47.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // indirect google.golang.org/protobuf v1.36.10 // indirect gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - k8s.io/kube-openapi v0.0.0-20260519202549-bbf5c5577288 // indirect + k8s.io/kube-openapi v0.0.0-20260706235625-cdb1db5517a0 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/kube-storage-version-migrator v0.0.6-0.20230721195810-5c8923c5ff96 // indirect sigs.k8s.io/randfill v1.0.0 // indirect - sigs.k8s.io/structured-merge-diff/v6 v6.3.2 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.4.1 // indirect ) replace github.com/onsi/ginkgo/v2 => github.com/openshift/onsi-ginkgo/v2 v2.6.1-0.20241205171354-8006f302fd12 diff --git a/go.sum b/go.sum index 879bb32bcf..a9fb41f94e 100644 --- a/go.sum +++ b/go.sum @@ -23,8 +23,8 @@ github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjT github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= 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.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= -github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= +github.com/fxamacker/cbor/v2 v2.9.2 h1:X4Ksno9+x3cz0TZv69ec1hxP/+tymuR8PXQJyDwfh78= +github.com/fxamacker/cbor/v2 v2.9.2/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= @@ -126,8 +126,8 @@ github.com/openshift/controller-runtime-common v0.0.0-20260428152732-64ee174f5e2 github.com/openshift/controller-runtime-common v0.0.0-20260428152732-64ee174f5e2e/go.mod h1:XGabTMnNbz0M5Oa7IbscZp/jmcc7aHobvOCUWwkzKvM= github.com/openshift/library-go v0.0.0-20260413093329-d2db42c961e1 h1:NdVGxmPGwWoMlhSmTxMMgp2SszLtAH3nJ6AMGBpXclY= github.com/openshift/library-go v0.0.0-20260413093329-d2db42c961e1/go.mod h1:3bi4pLpYRdVd1aEhsHfRTJkwxwPLfRZ+ZePn3RmJd2k= -github.com/openshift/lightspeed-agentic-operator/api v0.0.0-20260521135452-44bd61b9b92a h1:bUTmfSz1JIB8U8JiHsJ+8xWJH9jvrRBzSUWo0wbB9aU= -github.com/openshift/lightspeed-agentic-operator/api v0.0.0-20260521135452-44bd61b9b92a/go.mod h1:tZlKXEZJ4/qxPPh6mamWrkwK+EQjudHt8AypudHttVs= +github.com/openshift/lightspeed-agentic-operator/api v0.0.0-20260710132625-b6a3700bb634 h1:pDAnHXBXvfrsjn3iIk//AfsrCDRHkLusXXcc9tvOGyQ= +github.com/openshift/lightspeed-agentic-operator/api v0.0.0-20260710132625-b6a3700bb634/go.mod h1:ABTWhTrDT7njlz1UEteDhWjT6rpC/gWgy1UBSgMH4RY= github.com/openshift/onsi-ginkgo/v2 v2.6.1-0.20241205171354-8006f302fd12 h1:AKx/w1qpS8We43bsRgf8Nll3CGlDHpr/WAXvuedTNZI= github.com/openshift/onsi-ginkgo/v2 v2.6.1-0.20241205171354-8006f302fd12/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/operator-framework/api v0.17.1 h1:J/6+Xj4IEV8C7hcirqUFwOiZAU3PbnJhWvB0/bB51c4= @@ -189,31 +189,31 @@ 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.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= -go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= +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.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= -golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= +golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto= +golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= -golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60= -golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM= +golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o= +golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec= golang.org/x/oauth2 v0.31.0 h1:8Fq0yVZLh4j4YA47vHKFTa9Ew5XIrCP8LC6UeNZnLxo= golang.org/x/oauth2 v0.31.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= -golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= -golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM= +golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= -golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= -golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= -golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= -golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= +golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw= +golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc= +golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y= +golang.org/x/text v0.39.0 h1:UbZz4pLOvn600D6Oh6GGEI6VAmndrEBLv8/6BEXzyus= +golang.org/x/text v0.39.0/go.mod h1:3UwRclnC2g0TU9x8PZiyfOajCd1zaUNHF9cvqcQZ+ZM= golang.org/x/time v0.13.0 h1:eUlYslOIt32DgYD6utsuUeHs4d7AsEYLuIAdg7FlYgI= golang.org/x/time v0.13.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= -golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= -golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= +golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q= +golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950= @@ -251,10 +251,10 @@ 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-aggregator v0.35.1 h1:LN+btMJ3yp7biqVgT/0LF6SKIKLyfPU0R+JJ1mycs2I= k8s.io/kube-aggregator v0.35.1/go.mod h1:HQSjPQfOFRzcv7biQ7jV3cEfKHG+bczpLCfh4QfvxZU= -k8s.io/kube-openapi v0.0.0-20260519202549-bbf5c5577288 h1:A7Lby6ekC6nv+6oO38huCMFBRP0Os+tIeq1GkwxOQes= -k8s.io/kube-openapi v0.0.0-20260519202549-bbf5c5577288/go.mod h1:V/QaCUYDa+0QpcHhVVc5l99Uz56wEMEXBSj9oCDkNDY= -k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU= -k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= +k8s.io/kube-openapi v0.0.0-20260706235625-cdb1db5517a0 h1:CVjOUCTXINUThEmDs25FNSna0+vnGSoTleN+wiJu6hE= +k8s.io/kube-openapi v0.0.0-20260706235625-cdb1db5517a0/go.mod h1:rcZ+P5cEvHQB+m154WBOatIGBgOEPjzmLkXjkHfg3ms= +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.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= @@ -265,7 +265,7 @@ sigs.k8s.io/kustomize/kyaml v0.21.1 h1:IVlbmhC076nf6foyL6Taw4BkrLuEsXUXNpsE+ScX7 sigs.k8s.io/kustomize/kyaml v0.21.1/go.mod h1:hmxADesM3yUN2vbA5z1/YTBnzLJ1dajdqpQonwBL1FQ= 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.3.2 h1:kwVWMx5yS1CrnFWA/2QHyRVJ8jM6dBA80uLmm0wJkk8= -sigs.k8s.io/structured-merge-diff/v6 v6.3.2/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/structured-merge-diff/v6 v6.4.1 h1:AkER7js0XVWi/F/V2Iwl5N7O/B9VP2JyrOMmHPdco+g= +sigs.k8s.io/structured-merge-diff/v6 v6.4.1/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= diff --git a/install/0000_00_cluster-version-operator_50_lightspeed-prompts.yaml b/install/0000_00_cluster-version-operator_50_lightspeed-prompts.yaml index f23a534f6e..594271190f 100644 --- a/install/0000_00_cluster-version-operator_50_lightspeed-prompts.yaml +++ b/install/0000_00_cluster-version-operator_50_lightspeed-prompts.yaml @@ -10,7 +10,7 @@ metadata: data: prompt: | You are an OpenShift upgrade advisor. Analyze the cluster readiness - data in the proposal request and produce an upgrade risk assessment. + data in the agentic run request and produce an upgrade risk assessment. The request contains a "Cluster Readiness Data" section with a JSON block. This was collected by the Cluster Version Operator — do not diff --git a/pkg/proposal/analysis_schema.json b/pkg/agenticrun/analysis_schema.json similarity index 100% rename from pkg/proposal/analysis_schema.json rename to pkg/agenticrun/analysis_schema.json diff --git a/pkg/proposal/controller.go b/pkg/agenticrun/controller.go similarity index 69% rename from pkg/proposal/controller.go rename to pkg/agenticrun/controller.go index faaa9d7aa9..6cbdecea40 100644 --- a/pkg/proposal/controller.go +++ b/pkg/agenticrun/controller.go @@ -1,4 +1,4 @@ -package proposal +package agenticrun import ( "context" @@ -24,7 +24,7 @@ import ( "k8s.io/klog/v2" configv1 "github.com/openshift/api/config/v1" - proposalv1alpha1 "github.com/openshift/lightspeed-agentic-operator/api/v1alpha1" + agenticrunv1alpha1 "github.com/openshift/lightspeed-agentic-operator/api/v1alpha1" i "github.com/openshift/cluster-version-operator/pkg/internal" "github.com/openshift/cluster-version-operator/pkg/readiness" @@ -53,7 +53,7 @@ type Controller struct { config Config } -const controllerName = "proposal-lifecycle-controller" +const controllerName = "agenticrun-lifecycle-controller" type updatesGetterFunc func() ([]configv1.Release, []configv1.ConditionalUpdate, error) @@ -63,12 +63,12 @@ type getCurrentVersionFunc func() string type configMapGetterFunc func(ctx context.Context, namespace, name string, opts metav1.GetOptions) (*corev1.ConfigMap, error) -// NewController returns Controller to manage Proposals. -// It monitors available and conditional updates, and creates a LightspeedProposal for every target version of them. -// It expires (and replace) any previous LightspeedProposals owned by the CVO after 24h. -// It deletes any CVO-owned LightspeedProposals (without replacement) that are associated with target releases +// NewController returns Controller to manage AgenticRuns. +// It monitors available and conditional updates, and creates an AgenticRun for every target version of them. +// It expires (and replaces) any previous AgenticRuns owned by the CVO after 24h. +// It deletes any CVO-owned AgenticRuns (without replacement) that are associated with target releases // that are no longer supported next-hop options (e.g. because a channel change or cluster update), but preserves -// LightspeedProposals associated with versions in the ClusterVersion status.history (history already has its own +// AgenticRuns associated with versions in the ClusterVersion status.history (history already has its own // garbage-collection). func NewController( updatesGetterFunc updatesGetterFunc, @@ -93,7 +93,7 @@ func NewController( } } -// Config holds configuration for proposal creation. +// Config holds configuration for agentic run creation. type Config struct { Namespace string PromptConfigMap string // ConfigMap name containing the system prompt @@ -103,7 +103,7 @@ type Config struct { // DefaultConfig returns the default configuration, checking env vars for overrides. func DefaultConfig() Config { return Config{ - Namespace: envOrDefault("LIGHTSPEED_PROPOSAL_NAMESPACE", "openshift-lightspeed"), + Namespace: envOrDefault("LIGHTSPEED_AGENTIC_RUN_NAMESPACE", "openshift-lightspeed"), PromptConfigMap: envOrDefault("LIGHTSPEED_PROMPT_CONFIGMAP", "cluster-update-advisory-prompt"), SkillsImage: envOrDefault("LIGHTSPEED_SKILLS_IMAGE", "quay.io/openshift/ci:ocp_5.0_agentic-skills"), } @@ -147,17 +147,17 @@ func (c *Controller) Sync(ctx context.Context, key string) error { currentVersion := c.getCurrentVersionFunc() - // Don't create proposals while CVO is reconciling — readiness data would + // Don't create agentic runs while CVO is reconciling — readiness data would // reflect the transient reconciliation state, not actual cluster health. for _, cond := range cv.Status.Conditions { if cond.Type == "Progressing" && cond.Status == configv1.ConditionTrue { - klog.V(i.Normal).Infof("Skipping proposal sync: cluster is progressing (%s)", cond.Message) + klog.V(i.Normal).Infof("Skipping agentic run sync: cluster is progressing (%s)", cond.Message) return nil } } var errs []error - if err := deleteProposals(ctx, c.client, updates, conditionalUpdates, cv.Status.History, currentVersion); err != nil { + if err := deleteAgenticRuns(ctx, c.client, updates, conditionalUpdates, cv.Status.History, currentVersion); err != nil { errs = append(errs, err) } @@ -181,67 +181,67 @@ func (c *Controller) Sync(ctx context.Context, key string) error { return kutilerrors.NewAggregate(errs) } - proposals, err := getProposals(ctx, c.dynamicClient, updates, conditionalUpdates, c.config.Namespace, currentVersion, cv.Spec.Channel, prompt, c.config.SkillsImage) + agenticRuns, err := getAgenticRuns(ctx, c.dynamicClient, updates, conditionalUpdates, c.config.Namespace, currentVersion, cv.Spec.Channel, prompt, c.config.SkillsImage) if err != nil { - klog.V(i.Normal).Infof("Getting proposals hit an error: %v", err) + klog.V(i.Normal).Infof("Getting agentic runs hit an error: %v", err) return kutilerrors.NewAggregate(append(errs, err)) } - for _, proposal := range proposals { - existing := &proposalv1alpha1.Proposal{} - err := c.client.Get(ctx, ctrlruntimeclient.ObjectKey{Name: proposal.Name, Namespace: proposal.Namespace}, existing) + for _, agenticRun := range agenticRuns { + existing := &agenticrunv1alpha1.AgenticRun{} + err := c.client.Get(ctx, ctrlruntimeclient.ObjectKey{Name: agenticRun.Name, Namespace: agenticRun.Namespace}, existing) if err != nil { if !kerrors.IsNotFound(err) { - klog.V(i.Normal).Infof("Failed to get proposal %s/%s: %v", proposal.Namespace, proposal.Name, err) + klog.V(i.Normal).Infof("Failed to get agentic run %s/%s: %v", agenticRun.Namespace, agenticRun.Name, err) errs = append(errs, err) continue } } else { if !ownedByCVO(existing) { - klog.V(i.Normal).Infof("Ignored proposal %s/%s not owned by CVO", proposal.Namespace, proposal.Name) + klog.V(i.Normal).Infof("Ignored agentic run %s/%s not owned by CVO", agenticRun.Namespace, agenticRun.Name) continue } if expired(existing) { - if err := deleteProposal(ctx, c.client, existing, "expired"); err != nil { + if err := deleteAgenticRun(ctx, c.client, existing, "expired"); err != nil { errs = append(errs, err) continue } } else { - klog.V(i.Debug).Infof("The existing proposal %s/%s is not expired", proposal.Namespace, proposal.Name) + klog.V(i.Debug).Infof("The existing agentic run %s/%s is not expired", agenticRun.Namespace, agenticRun.Name) continue } } - if err := c.client.Create(ctx, proposal); err != nil { + if err := c.client.Create(ctx, agenticRun); err != nil { if !kerrors.IsAlreadyExists(err) { - klog.V(i.Normal).Infof("Failed to create proposal %s/%s: %v", proposal.Namespace, proposal.Name, err) + klog.V(i.Normal).Infof("Failed to create agentic run %s/%s: %v", agenticRun.Namespace, agenticRun.Name, err) errs = append(errs, err) } else { - klog.V(i.Debug).Infof("The proposal %s/%s existed already", proposal.Namespace, proposal.Name) + klog.V(i.Debug).Infof("The agentic run %s/%s existed already", agenticRun.Namespace, agenticRun.Name) } } else { - klog.V(i.Debug).Infof("Created proposal %s/%s", proposal.Namespace, proposal.Name) + klog.V(i.Debug).Infof("Created agentic run %s/%s", agenticRun.Namespace, agenticRun.Name) } } return kutilerrors.NewAggregate(errs) } -func ownedByCVO(p *proposalv1alpha1.Proposal) bool { +func ownedByCVO(p *agenticrunv1alpha1.AgenticRun) bool { if p == nil { return false } return p.Labels[labelKeySource] == labelValueSource } -func expired(p *proposalv1alpha1.Proposal) bool { +func expired(p *agenticrunv1alpha1.AgenticRun) bool { if p == nil { return false } - return time.Now().After(p.CreationTimestamp.Add(proposalExpiration)) + return time.Now().After(p.CreationTimestamp.Add(agenticRunExpiration)) } -func deleteProposals(ctx context.Context, client ctrlruntimeclient.Client, availableUpdates []configv1.Release, conditionalUpdates []configv1.ConditionalUpdate, history []configv1.UpdateHistory, currentVersion string) error { +func deleteAgenticRuns(ctx context.Context, client ctrlruntimeclient.Client, availableUpdates []configv1.Release, conditionalUpdates []configv1.ConditionalUpdate, history []configv1.UpdateHistory, currentVersion string) error { targets := sets.New[string]() for _, update := range availableUpdates { targets.Insert(labelValueFromVersion(update.Version)) @@ -254,27 +254,27 @@ func deleteProposals(ctx context.Context, client ctrlruntimeclient.Client, avail associatedWithHistory.Insert(labelValueFromVersion(h.Version)) } - list := &proposalv1alpha1.ProposalList{} - if err := client.List(ctx, list, ctrlruntimeclient.MatchingLabels(CVOProposalLabels)); err != nil { - return fmt.Errorf("failed to list proposals: %w", err) + list := &agenticrunv1alpha1.AgenticRunList{} + if err := client.List(ctx, list, ctrlruntimeclient.MatchingLabels(CVOAgenticRunLabels)); err != nil { + return fmt.Errorf("failed to list agentic runs: %w", err) } var errs []error - for _, proposal := range list.Items { - if !ownedByCVO(&proposal) { - klog.V(i.Debug).Infof("Keeping proposal %s/%s not owned by CVO", proposal.Namespace, proposal.Name) + for _, agenticRun := range list.Items { + if !ownedByCVO(&agenticRun) { + klog.V(i.Debug).Infof("Keeping agentic run %s/%s not owned by CVO", agenticRun.Namespace, agenticRun.Name) continue } - cv, cvOk := proposal.Labels[labelKeyCurrentVersion] - tv, tvOk := proposal.Labels[labelKeyTargetVersion] + cv, cvOk := agenticRun.Labels[labelKeyCurrentVersion] + tv, tvOk := agenticRun.Labels[labelKeyTargetVersion] if cvOk && tvOk && cv == currentVersion && targets.Has(tv) { - klog.V(i.Debug).Infof("Keeping relevant proposal %s/%s from %s to %s", proposal.Namespace, proposal.Name, cv, tv) + klog.V(i.Debug).Infof("Keeping relevant agentic run %s/%s from %s to %s", agenticRun.Namespace, agenticRun.Name, cv, tv) continue } if tvOk && associatedWithHistory.Has(tv) { - klog.V(i.Debug).Infof("Keeping proposal %s/%s for a version %s associated with history", proposal.Namespace, proposal.Name, tv) + klog.V(i.Debug).Infof("Keeping agentic run %s/%s for a version %s associated with history", agenticRun.Namespace, agenticRun.Name, tv) continue } - err := deleteProposal(ctx, client, &proposal, "irrelevant") + err := deleteAgenticRun(ctx, client, &agenticRun, "irrelevant") if err != nil { errs = append(errs, err) } @@ -283,27 +283,27 @@ func deleteProposals(ctx context.Context, client ctrlruntimeclient.Client, avail return kutilerrors.NewAggregate(errs) } -func deleteProposal(ctx context.Context, client ctrlruntimeclient.Client, proposal *proposalv1alpha1.Proposal, adjective string) error { - if proposal == nil { +func deleteAgenticRun(ctx context.Context, client ctrlruntimeclient.Client, agenticRun *agenticrunv1alpha1.AgenticRun, adjective string) error { + if agenticRun == nil { return nil } - klog.V(i.Normal).Infof("Deleting %s proposal %s/%s ...", adjective, proposal.Namespace, proposal.Name) - err := client.Delete(ctx, proposal) + klog.V(i.Normal).Infof("Deleting %s agentic run %s/%s ...", adjective, agenticRun.Namespace, agenticRun.Name) + err := client.Delete(ctx, agenticRun) if err == nil { - klog.V(i.Normal).Infof("Deleted %s proposal %s/%s", adjective, proposal.Namespace, proposal.Name) + klog.V(i.Normal).Infof("Deleted %s agentic run %s/%s", adjective, agenticRun.Namespace, agenticRun.Name) return nil } if !kerrors.IsNotFound(err) { - klog.V(i.Normal).Infof("Failed to delete %s proposal %s/%s: %v", adjective, proposal.Namespace, proposal.Name, err) + klog.V(i.Normal).Infof("Failed to delete %s agentic run %s/%s: %v", adjective, agenticRun.Namespace, agenticRun.Name, err) return err } - klog.V(i.Normal).Infof("Failed to delete not-found proposal %s/%s", proposal.Namespace, proposal.Name) + klog.V(i.Normal).Infof("Failed to delete not-found agentic run %s/%s", agenticRun.Namespace, agenticRun.Name) return nil } -func getProposals( +func getAgenticRuns( ctx context.Context, dynamicClient dynamic.Interface, availableUpdates []configv1.Release, @@ -312,39 +312,39 @@ func getProposals( currentVersion, channel, systemPrompt string, skillsImage string, -) ([]*proposalv1alpha1.Proposal, error) { +) ([]*agenticrunv1alpha1.AgenticRun, error) { // TODO: Only 2 of 9 readiness checks (api_deprecations, olm_lifecycle) use the target version. // The other 7 query cluster-wide state identical across targets. For clusters with many available // updates, split into target-independent checks (run once) and target-dependent checks (run per // target) to reduce redundant API calls. var errs []error - var proposals []*proposalv1alpha1.Proposal + var agenticRuns []*agenticrunv1alpha1.AgenticRun for _, au := range availableUpdates { targetVersion := au.Version readinessJSON := runReadinessJSON(ctx, dynamicClient, currentVersion, targetVersion) - if proposal, err := getProposal(namespace, currentVersion, targetVersion, channel, updateKindRecommended, systemPrompt, readinessJSON, availableUpdates, skillsImage); err != nil { + if agenticRun, err := getAgenticRun(namespace, currentVersion, targetVersion, channel, updateKindRecommended, systemPrompt, readinessJSON, availableUpdates, skillsImage); err != nil { errs = append(errs, err) continue } else { - proposals = append(proposals, proposal) + agenticRuns = append(agenticRuns, agenticRun) } } for _, cu := range conditionalUpdates { targetVersion := cu.Release.Version readinessJSON := runReadinessJSON(ctx, dynamicClient, currentVersion, targetVersion) - if proposal, err := getProposal(namespace, currentVersion, targetVersion, channel, updateKindConditional, systemPrompt, readinessJSON, availableUpdates, skillsImage); err != nil { + if agenticRun, err := getAgenticRun(namespace, currentVersion, targetVersion, channel, updateKindConditional, systemPrompt, readinessJSON, availableUpdates, skillsImage); err != nil { errs = append(errs, err) continue } else { - proposals = append(proposals, proposal) + agenticRuns = append(agenticRuns, agenticRun) } } - return proposals, kutilerrors.NewAggregate(errs) + return agenticRuns, kutilerrors.NewAggregate(errs) } -func getProposal(namespace, currentVersion, targetVersion, channel, updateKind, systemPrompt, readinessJSON string, availableUpdates []configv1.Release, skillsImage string) (*proposalv1alpha1.Proposal, error) { +func getAgenticRun(namespace, currentVersion, targetVersion, channel, updateKind, systemPrompt, readinessJSON string, availableUpdates []configv1.Release, skillsImage string) (*agenticrunv1alpha1.AgenticRun, error) { var errs []error for _, v := range []string{currentVersion, targetVersion} { @@ -356,10 +356,10 @@ func getProposal(namespace, currentVersion, targetVersion, channel, updateKind, return nil, kutilerrors.NewAggregate(errs) } - name := proposalName(currentVersion, targetVersion) + name := agenticRunName(currentVersion, targetVersion) updateType := classifyUpdate(currentVersion, targetVersion) request := buildRequest(systemPrompt, currentVersion, targetVersion, channel, updateType, updateKind, availableUpdates, readinessJSON) - return &proposalv1alpha1.Proposal{ + return &agenticrunv1alpha1.AgenticRun{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, @@ -370,14 +370,14 @@ func getProposal(namespace, currentVersion, targetVersion, channel, updateKind, "agentic.openshift.io/update-type": updateType, }, }, - Spec: proposalv1alpha1.ProposalSpec{ + Spec: agenticrunv1alpha1.AgenticRunSpec{ Request: request, // CVO consumes some Agent maintained by the cluster admin - Analysis: proposalv1alpha1.ProposalStep{ + Analysis: agenticrunv1alpha1.AgenticRunStep{ Agent: "smart", }, - Tools: proposalv1alpha1.ToolsSpec{ - Skills: []proposalv1alpha1.SkillsSource{ + Tools: agenticrunv1alpha1.ToolsSpec{ + Skills: []agenticrunv1alpha1.SkillsSource{ { Image: skillsImage, Paths: []string{ @@ -387,8 +387,8 @@ func getProposal(namespace, currentVersion, targetVersion, channel, updateKind, }, }, }, - AnalysisOutput: proposalv1alpha1.AnalysisOutput{ - Mode: proposalv1alpha1.AnalysisOutputModeMinimal, + AnalysisOutput: agenticrunv1alpha1.AnalysisOutput{ + Mode: agenticrunv1alpha1.AnalysisOutputModeMinimal, Schema: analysisOutputSchema(), }, }, @@ -407,8 +407,8 @@ func trim63(value string) string { return value } -// proposalName generates a deterministic proposal name from the version pair. -func proposalName(current, target string) string { +// agenticRunName generates a deterministic agentic run name from the version pair. +func agenticRunName(current, target string) string { return toDNS1035(fmt.Sprintf("ota-%s-to-%s", current, target)) } @@ -437,11 +437,11 @@ const ( updateKindRecommended = "Recommended" updateKindConditional = "Conditional" - proposalExpiration = 24 * time.Hour + agenticRunExpiration = 24 * time.Hour ) var ( - CVOProposalLabels = map[string]string{labelKeySource: labelValueSource} + CVOAgenticRunLabels = map[string]string{labelKeySource: labelValueSource} ) // classifyUpdate returns "z-stream" if major.minor match, otherwise "minor". @@ -468,7 +468,7 @@ func runReadinessJSON(ctx context.Context, dynamicClient dynamic.Interface, curr return string(data) } -// buildRequest constructs the proposal request with system prompt, metadata, and readiness data. +// buildRequest constructs the agentic run request with system prompt, metadata, and readiness data. func buildRequest(systemPrompt, current, target, channel, updateType, targetType string, updates []configv1.Release, readinessJSON string) string { diff --git a/pkg/proposal/controller_test.go b/pkg/agenticrun/controller_test.go similarity index 84% rename from pkg/proposal/controller_test.go rename to pkg/agenticrun/controller_test.go index 288b58e953..61199ec3fb 100644 --- a/pkg/proposal/controller_test.go +++ b/pkg/agenticrun/controller_test.go @@ -1,4 +1,4 @@ -package proposal +package agenticrun import ( "context" @@ -23,7 +23,7 @@ import ( dynamicfake "k8s.io/client-go/dynamic/fake" configv1 "github.com/openshift/api/config/v1" - proposalv1alpha1 "github.com/openshift/lightspeed-agentic-operator/api/v1alpha1" + agenticrunv1alpha1 "github.com/openshift/lightspeed-agentic-operator/api/v1alpha1" "github.com/openshift/cluster-version-operator/pkg/internal" "github.com/openshift/cluster-version-operator/pkg/readiness" @@ -65,12 +65,12 @@ func TestController_Sync(t *testing.T) { }, client: fake.NewClientBuilder().Build(), verifyFunc: func(client ctrlruntimeclient.Client) error { - proposals := &proposalv1alpha1.ProposalList{} - if err := client.List(context.Background(), proposals); err != nil { + agenticRuns := &agenticrunv1alpha1.AgenticRunList{} + if err := client.List(context.Background(), agenticRuns); err != nil { return err } - expect := &proposalv1alpha1.ProposalList{ - Items: []proposalv1alpha1.Proposal{ + expect := &agenticrunv1alpha1.AgenticRunList{ + Items: []agenticrunv1alpha1.AgenticRun{ { ObjectMeta: metav1.ObjectMeta{ Name: "ota-4-22-1-to-5-0-0-ec-0", @@ -82,7 +82,7 @@ func TestController_Sync(t *testing.T) { "agentic.openshift.io/update-type": "Major", }, }, - Spec: proposalv1alpha1.ProposalSpec{ + Spec: agenticrunv1alpha1.AgenticRunSpec{ Request: `prompt-abc --- @@ -97,11 +97,11 @@ Update path: Recommended ` + "```json\n" + `{}` + "\n```\n", - Analysis: proposalv1alpha1.ProposalStep{ + Analysis: agenticrunv1alpha1.AgenticRunStep{ Agent: "smart", }, - Tools: proposalv1alpha1.ToolsSpec{ - Skills: []proposalv1alpha1.SkillsSource{ + Tools: agenticrunv1alpha1.ToolsSpec{ + Skills: []agenticrunv1alpha1.SkillsSource{ { Image: "quay.io/openshift/ci:ocp_5.0_agentic-skills", Paths: []string{ @@ -111,15 +111,15 @@ Update path: Recommended }, }, }, - AnalysisOutput: proposalv1alpha1.AnalysisOutput{ - Mode: proposalv1alpha1.AnalysisOutputModeMinimal, + AnalysisOutput: agenticrunv1alpha1.AnalysisOutput{ + Mode: agenticrunv1alpha1.AnalysisOutputModeMinimal, Schema: analysisOutputSchema(), }, }, }, }} - if diff := cmp.Diff(expect, proposals, cmpopts.IgnoreFields(metav1.ObjectMeta{}, "ResourceVersion")); diff != "" { - return fmt.Errorf("unexpected ProposalList (-want, +got) = \n%v", diff) + if diff := cmp.Diff(expect, agenticRuns, cmpopts.IgnoreFields(metav1.ObjectMeta{}, "ResourceVersion")); diff != "" { + return fmt.Errorf("unexpected AgenticRunList (-want, +got) = \n%v", diff) } return nil }, @@ -182,7 +182,7 @@ func TestClassifyUpdate(t *testing.T) { } } -func TestProposalName(t *testing.T) { +func TestAgenticRunName(t *testing.T) { tests := []struct { current string target string @@ -194,9 +194,9 @@ func TestProposalName(t *testing.T) { for _, tt := range tests { t.Run(tt.current+"->"+tt.target, func(t *testing.T) { - got := proposalName(tt.current, tt.target) + got := agenticRunName(tt.current, tt.target) if got != tt.expected { - t.Errorf("proposalName(%q, %q) = %q, want %q", tt.current, tt.target, got, tt.expected) + t.Errorf("agenticRunName(%q, %q) = %q, want %q", tt.current, tt.target, got, tt.expected) } }) } @@ -273,25 +273,25 @@ func TestBuildRequest(t *testing.T) { }) } -func TestDeleteProposals(t *testing.T) { +func TestDeleteAgenticRuns(t *testing.T) { tests := []struct { - name string - availableUpdates []configv1.Release - conditionalUpdates []configv1.ConditionalUpdate - history []configv1.UpdateHistory - currentVersion string - existingProposals []proposalv1alpha1.Proposal - expectedDeleted []string - expectedKept []string + name string + availableUpdates []configv1.Release + conditionalUpdates []configv1.ConditionalUpdate + history []configv1.UpdateHistory + currentVersion string + existingAgenticRuns []agenticrunv1alpha1.AgenticRun + expectedDeleted []string + expectedKept []string }{ { - name: "keeps relevant proposals matching current version and target", + name: "keeps relevant agentic runs matching current version and target", availableUpdates: []configv1.Release{ {Version: "4.16.0"}, {Version: "4.16.1"}, }, currentVersion: "4.15.3", - existingProposals: []proposalv1alpha1.Proposal{ + existingAgenticRuns: []agenticrunv1alpha1.AgenticRun{ { ObjectMeta: metav1.ObjectMeta{ Name: "proposal-4-15-3-to-4-16-0", @@ -319,12 +319,12 @@ func TestDeleteProposals(t *testing.T) { expectedDeleted: []string{}, }, { - name: "deletes proposals with outdated current version", + name: "deletes agentic runs with outdated current version", availableUpdates: []configv1.Release{ {Version: "4.16.0"}, }, currentVersion: "4.15.3", - existingProposals: []proposalv1alpha1.Proposal{ + existingAgenticRuns: []agenticrunv1alpha1.AgenticRun{ { ObjectMeta: metav1.ObjectMeta{ Name: "proposal-4-15-2-to-4-16-0", @@ -341,12 +341,12 @@ func TestDeleteProposals(t *testing.T) { expectedDeleted: []string{"proposal-4-15-2-to-4-16-0"}, }, { - name: "deletes proposals for targets no longer in available updates", + name: "deletes agentic runs for targets no longer in available updates", availableUpdates: []configv1.Release{ {Version: "4.16.1"}, }, currentVersion: "4.15.3", - existingProposals: []proposalv1alpha1.Proposal{ + existingAgenticRuns: []agenticrunv1alpha1.AgenticRun{ { ObjectMeta: metav1.ObjectMeta{ Name: "proposal-4-15-3-to-4-16-0", @@ -363,7 +363,7 @@ func TestDeleteProposals(t *testing.T) { expectedDeleted: []string{"proposal-4-15-3-to-4-16-0"}, }, { - name: "keeps proposals associated with history", + name: "keeps agentic runs associated with history", availableUpdates: []configv1.Release{ {Version: "4.16.2"}, }, @@ -371,7 +371,7 @@ func TestDeleteProposals(t *testing.T) { {Version: "4.16.1"}, }, currentVersion: "4.16.1", - existingProposals: []proposalv1alpha1.Proposal{ + existingAgenticRuns: []agenticrunv1alpha1.AgenticRun{ { ObjectMeta: metav1.ObjectMeta{ Name: "proposal-old-to-4-16-1", @@ -399,12 +399,12 @@ func TestDeleteProposals(t *testing.T) { expectedDeleted: []string{"proposal-old-to-4-15-3"}, }, { - name: "keeps proposals not owned by CVO", + name: "keeps agentic runs not owned by CVO", availableUpdates: []configv1.Release{ {Version: "4.16.0"}, }, currentVersion: "4.15.3", - existingProposals: []proposalv1alpha1.Proposal{ + existingAgenticRuns: []agenticrunv1alpha1.AgenticRun{ { ObjectMeta: metav1.ObjectMeta{ Name: "user-created-proposal", @@ -426,7 +426,7 @@ func TestDeleteProposals(t *testing.T) { {Release: configv1.Release{Version: "4.16.2"}}, }, currentVersion: "4.15.3", - existingProposals: []proposalv1alpha1.Proposal{ + existingAgenticRuns: []agenticrunv1alpha1.AgenticRun{ { ObjectMeta: metav1.ObjectMeta{ Name: "proposal-4-15-3-to-4-16-2", @@ -443,12 +443,12 @@ func TestDeleteProposals(t *testing.T) { expectedDeleted: []string{}, }, { - name: "deletes proposals with missing labels", + name: "deletes agentic runs with missing labels", availableUpdates: []configv1.Release{ {Version: "4.16.0"}, }, currentVersion: "4.15.3", - existingProposals: []proposalv1alpha1.Proposal{ + existingAgenticRuns: []agenticrunv1alpha1.AgenticRun{ { ObjectMeta: metav1.ObjectMeta{ Name: "proposal-missing-labels", @@ -467,7 +467,7 @@ func TestDeleteProposals(t *testing.T) { name: "handles empty updates and history", availableUpdates: []configv1.Release{}, currentVersion: "4.15.3", - existingProposals: []proposalv1alpha1.Proposal{ + existingAgenticRuns: []agenticrunv1alpha1.AgenticRun{ { ObjectMeta: metav1.ObjectMeta{ Name: "proposal-4-15-3-to-4-16-0", @@ -496,7 +496,7 @@ func TestDeleteProposals(t *testing.T) { {Version: "4.16.0"}, }, currentVersion: "4.16.0", - existingProposals: []proposalv1alpha1.Proposal{ + existingAgenticRuns: []agenticrunv1alpha1.AgenticRun{ { ObjectMeta: metav1.ObjectMeta{ Name: "keep-relevant", @@ -553,57 +553,57 @@ func TestDeleteProposals(t *testing.T) { // Create fake client with existing proposals clientBuilder := fake.NewClientBuilder() - for _, p := range tt.existingProposals { + for _, p := range tt.existingAgenticRuns { clientBuilder.WithObjects(&p) } client := clientBuilder.Build() - err := deleteProposals(ctx, client, tt.availableUpdates, tt.conditionalUpdates, tt.history, tt.currentVersion) + err := deleteAgenticRuns(ctx, client, tt.availableUpdates, tt.conditionalUpdates, tt.history, tt.currentVersion) if err != nil { - t.Errorf("deleteProposals() returned unexpected error: %v", err) + t.Errorf("deleteAgenticRuns() returned unexpected error: %v", err) } for _, name := range tt.expectedKept { - proposal := &proposalv1alpha1.Proposal{} - err := client.Get(ctx, ctrlruntimeclient.ObjectKey{Name: name, Namespace: "openshift-lightspeed"}, proposal) + agenticRun := &agenticrunv1alpha1.AgenticRun{} + err := client.Get(ctx, ctrlruntimeclient.ObjectKey{Name: name, Namespace: "openshift-lightspeed"}, agenticRun) if err != nil { - t.Errorf("expected proposal %s to be kept, but got error: %v", name, err) + t.Errorf("expected agentic run %s to be kept, but got error: %v", name, err) } } for _, name := range tt.expectedDeleted { - proposal := &proposalv1alpha1.Proposal{} - err := client.Get(ctx, ctrlruntimeclient.ObjectKey{Name: name, Namespace: "openshift-lightspeed"}, proposal) + agenticRun := &agenticrunv1alpha1.AgenticRun{} + err := client.Get(ctx, ctrlruntimeclient.ObjectKey{Name: name, Namespace: "openshift-lightspeed"}, agenticRun) if err == nil { - t.Errorf("expected proposal %s to be deleted, but it still exists", name) + t.Errorf("expected agentic run %s to be deleted, but it still exists", name) } } }) } } -func TestDeleteProposal(t *testing.T) { +func TestDeleteAgenticRun(t *testing.T) { tests := []struct { name string - proposal *proposalv1alpha1.Proposal + agenticRun *agenticrunv1alpha1.AgenticRun adjective string setupClient func() ctrlruntimeclient.Client expect error shouldBeDeleted bool }{ { - name: "successfully deletes existing proposal", - proposal: &proposalv1alpha1.Proposal{ + name: "successfully deletes existing agentic run", + agenticRun: &agenticrunv1alpha1.AgenticRun{ ObjectMeta: metav1.ObjectMeta{ - Name: "test-proposal", + Name: "test-agenticrun", Namespace: "openshift-lightspeed", }, }, adjective: "expired", setupClient: func() ctrlruntimeclient.Client { - return fake.NewClientBuilder().WithObjects(&proposalv1alpha1.Proposal{ + return fake.NewClientBuilder().WithObjects(&agenticrunv1alpha1.AgenticRun{ ObjectMeta: metav1.ObjectMeta{ - Name: "test-proposal", + Name: "test-agenticrun", Namespace: "openshift-lightspeed", }, }).Build() @@ -612,9 +612,9 @@ func TestDeleteProposal(t *testing.T) { }, { name: "handles not found error gracefully", - proposal: &proposalv1alpha1.Proposal{ + agenticRun: &agenticrunv1alpha1.AgenticRun{ ObjectMeta: metav1.ObjectMeta{ - Name: "nonexistent-proposal", + Name: "nonexistent-agenticrun", Namespace: "openshift-lightspeed", }, }, @@ -625,7 +625,7 @@ func TestDeleteProposal(t *testing.T) { shouldBeDeleted: true, }, { - name: "handles nil proposal", + name: "handles nil agentic run", adjective: "nil", setupClient: func() ctrlruntimeclient.Client { return fake.NewClientBuilder().Build() @@ -639,7 +639,7 @@ func TestDeleteProposal(t *testing.T) { ctx := context.Background() client := tt.setupClient() - err := deleteProposal(ctx, client, tt.proposal, tt.adjective) + err := deleteAgenticRun(ctx, client, tt.agenticRun, tt.adjective) if diff := cmp.Diff(tt.expect, err, cmp.Transformer("Error", func(e error) string { if e == nil { @@ -650,15 +650,15 @@ func TestDeleteProposal(t *testing.T) { t.Errorf("unexpected error (-want +got):\n%s", diff) } - if tt.proposal != nil { - proposal := &proposalv1alpha1.Proposal{} + if tt.agenticRun != nil { + agenticRun := &agenticrunv1alpha1.AgenticRun{} err = client.Get(ctx, ctrlruntimeclient.ObjectKey{ - Name: tt.proposal.Name, - Namespace: tt.proposal.Namespace, - }, proposal) + Name: tt.agenticRun.Name, + Namespace: tt.agenticRun.Namespace, + }, agenticRun) if tt.shouldBeDeleted { if !kerrors.IsNotFound(err) { - t.Error("expected proposal to be deleted but it still exists") + t.Error("expected agentic run to be deleted but it still exists") } } else { if err != nil { @@ -672,16 +672,16 @@ func TestDeleteProposal(t *testing.T) { func TestOwnedByCVO(t *testing.T) { tests := []struct { - name string - proposal *proposalv1alpha1.Proposal - expected bool + name string + agenticRun *agenticrunv1alpha1.AgenticRun + expected bool }{ { - name: "nil proposal", + name: "nil agentic run", }, { - name: "proposal owned by CVO", - proposal: &proposalv1alpha1.Proposal{ + name: "agentic run owned by CVO", + agenticRun: &agenticrunv1alpha1.AgenticRun{ ObjectMeta: metav1.ObjectMeta{ Labels: map[string]string{ labelKeySource: labelValueSource, @@ -691,8 +691,8 @@ func TestOwnedByCVO(t *testing.T) { expected: true, }, { - name: "proposal not owned by CVO", - proposal: &proposalv1alpha1.Proposal{ + name: "agentic run not owned by CVO", + agenticRun: &agenticrunv1alpha1.AgenticRun{ ObjectMeta: metav1.ObjectMeta{ Labels: map[string]string{ labelKeySource: "manual", @@ -701,22 +701,22 @@ func TestOwnedByCVO(t *testing.T) { }, }, { - name: "proposal with missing labels", - proposal: &proposalv1alpha1.Proposal{ + name: "agentic run with missing labels", + agenticRun: &agenticrunv1alpha1.AgenticRun{ ObjectMeta: metav1.ObjectMeta{ Labels: map[string]string{}, }, }, }, { - name: "proposal with nil labels", - proposal: &proposalv1alpha1.Proposal{}, + name: "agentic run with nil labels", + agenticRun: &agenticrunv1alpha1.AgenticRun{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result := ownedByCVO(tt.proposal) + result := ownedByCVO(tt.agenticRun) if result != tt.expected { t.Errorf("ownedByCVO() = %v, want %v", result, tt.expected) } @@ -724,7 +724,7 @@ func TestOwnedByCVO(t *testing.T) { } } -func TestGetProposals(t *testing.T) { +func TestGetAgenticRuns(t *testing.T) { tests := []struct { name string availableUpdates []configv1.Release @@ -733,7 +733,7 @@ func TestGetProposals(t *testing.T) { currentVersion string channel string systemPrompt string - expected []*proposalv1alpha1.Proposal + expected []*agenticrunv1alpha1.AgenticRun expectError error }{ { @@ -746,7 +746,7 @@ func TestGetProposals(t *testing.T) { currentVersion: "4.15.3", channel: "stable-4.16", systemPrompt: "Test prompt", - expected: []*proposalv1alpha1.Proposal{ + expected: []*agenticrunv1alpha1.AgenticRun{ { ObjectMeta: metav1.ObjectMeta{ Name: "ota-4-15-3-to-4-16-0", @@ -758,7 +758,7 @@ func TestGetProposals(t *testing.T) { "agentic.openshift.io/update-type": "Minor", }, }, - Spec: proposalv1alpha1.ProposalSpec{ + Spec: agenticrunv1alpha1.AgenticRunSpec{ Request: `Test prompt --- @@ -776,11 +776,11 @@ Other recommended versions available: ` + "```json\n" + `{}` + "\n```\n", - Analysis: proposalv1alpha1.ProposalStep{ + Analysis: agenticrunv1alpha1.AgenticRunStep{ Agent: "smart", }, - Tools: proposalv1alpha1.ToolsSpec{ - Skills: []proposalv1alpha1.SkillsSource{ + Tools: agenticrunv1alpha1.ToolsSpec{ + Skills: []agenticrunv1alpha1.SkillsSource{ { Image: "quay.io/openshift/ci:ocp_5.0_agentic-skills", Paths: []string{ @@ -790,8 +790,8 @@ Other recommended versions available: }, }, }, - AnalysisOutput: proposalv1alpha1.AnalysisOutput{ - Mode: proposalv1alpha1.AnalysisOutputModeMinimal, + AnalysisOutput: agenticrunv1alpha1.AnalysisOutput{ + Mode: agenticrunv1alpha1.AnalysisOutputModeMinimal, Schema: analysisOutputSchema(), }, }, @@ -807,7 +807,7 @@ Other recommended versions available: "agentic.openshift.io/update-type": "Minor", }, }, - Spec: proposalv1alpha1.ProposalSpec{ + Spec: agenticrunv1alpha1.AgenticRunSpec{ Request: `Test prompt --- @@ -825,11 +825,11 @@ Other recommended versions available: ` + "```json\n" + `{}` + "\n```\n", - Analysis: proposalv1alpha1.ProposalStep{ + Analysis: agenticrunv1alpha1.AgenticRunStep{ Agent: "smart", }, - Tools: proposalv1alpha1.ToolsSpec{ - Skills: []proposalv1alpha1.SkillsSource{ + Tools: agenticrunv1alpha1.ToolsSpec{ + Skills: []agenticrunv1alpha1.SkillsSource{ { Image: "quay.io/openshift/ci:ocp_5.0_agentic-skills", Paths: []string{ @@ -839,8 +839,8 @@ Other recommended versions available: }, }, }, - AnalysisOutput: proposalv1alpha1.AnalysisOutput{ - Mode: proposalv1alpha1.AnalysisOutputModeMinimal, + AnalysisOutput: agenticrunv1alpha1.AnalysisOutput{ + Mode: agenticrunv1alpha1.AnalysisOutputModeMinimal, Schema: analysisOutputSchema(), }, }, @@ -860,7 +860,7 @@ Other recommended versions available: namespace: "openshift-lightspeed", currentVersion: "4.15.3", channel: "stable-4.16", - expected: []*proposalv1alpha1.Proposal{ + expected: []*agenticrunv1alpha1.AgenticRun{ { ObjectMeta: metav1.ObjectMeta{ Name: "ota-4-15-3-to-4-16-0", @@ -872,7 +872,7 @@ Other recommended versions available: "agentic.openshift.io/update-type": "Minor", }, }, - Spec: proposalv1alpha1.ProposalSpec{ + Spec: agenticrunv1alpha1.AgenticRunSpec{ Request: `Current version: OCP 4.15.3 Target version: OCP 4.16.0 Channel: stable-4.16 @@ -883,11 +883,11 @@ Other recommended versions available: - 4.16.1 ` + "## Cluster Readiness Data\n\n```json\n{}\n```\n", - Analysis: proposalv1alpha1.ProposalStep{ + Analysis: agenticrunv1alpha1.AgenticRunStep{ Agent: "smart", }, - Tools: proposalv1alpha1.ToolsSpec{ - Skills: []proposalv1alpha1.SkillsSource{ + Tools: agenticrunv1alpha1.ToolsSpec{ + Skills: []agenticrunv1alpha1.SkillsSource{ { Image: "quay.io/openshift/ci:ocp_5.0_agentic-skills", Paths: []string{ @@ -897,8 +897,8 @@ Other recommended versions available: }, }, }, - AnalysisOutput: proposalv1alpha1.AnalysisOutput{ - Mode: proposalv1alpha1.AnalysisOutputModeMinimal, + AnalysisOutput: agenticrunv1alpha1.AnalysisOutput{ + Mode: agenticrunv1alpha1.AnalysisOutputModeMinimal, Schema: analysisOutputSchema(), }, }, @@ -914,7 +914,7 @@ Other recommended versions available: "agentic.openshift.io/update-type": "Minor", }, }, - Spec: proposalv1alpha1.ProposalSpec{ + Spec: agenticrunv1alpha1.AgenticRunSpec{ Request: `Current version: OCP 4.15.3 Target version: OCP 4.16.1 Channel: stable-4.16 @@ -925,11 +925,11 @@ Other recommended versions available: - 4.16.0 ` + "## Cluster Readiness Data\n\n```json\n{}\n```\n", - Analysis: proposalv1alpha1.ProposalStep{ + Analysis: agenticrunv1alpha1.AgenticRunStep{ Agent: "smart", }, - Tools: proposalv1alpha1.ToolsSpec{ - Skills: []proposalv1alpha1.SkillsSource{ + Tools: agenticrunv1alpha1.ToolsSpec{ + Skills: []agenticrunv1alpha1.SkillsSource{ { Image: "quay.io/openshift/ci:ocp_5.0_agentic-skills", Paths: []string{ @@ -939,8 +939,8 @@ Other recommended versions available: }, }, }, - AnalysisOutput: proposalv1alpha1.AnalysisOutput{ - Mode: proposalv1alpha1.AnalysisOutputModeMinimal, + AnalysisOutput: agenticrunv1alpha1.AnalysisOutput{ + Mode: agenticrunv1alpha1.AnalysisOutputModeMinimal, Schema: analysisOutputSchema(), }, }, @@ -956,7 +956,7 @@ Other recommended versions available: "agentic.openshift.io/update-type": "Minor", }, }, - Spec: proposalv1alpha1.ProposalSpec{ + Spec: agenticrunv1alpha1.AgenticRunSpec{ Request: `Current version: OCP 4.15.3 Target version: OCP 4.16.2 Channel: stable-4.16 @@ -972,11 +972,11 @@ Other recommended versions available: - 4.16.1 ` + "## Cluster Readiness Data\n\n```json\n{}\n```\n", - Analysis: proposalv1alpha1.ProposalStep{ + Analysis: agenticrunv1alpha1.AgenticRunStep{ Agent: "smart", }, - Tools: proposalv1alpha1.ToolsSpec{ - Skills: []proposalv1alpha1.SkillsSource{ + Tools: agenticrunv1alpha1.ToolsSpec{ + Skills: []agenticrunv1alpha1.SkillsSource{ { Image: "quay.io/openshift/ci:ocp_5.0_agentic-skills", Paths: []string{ @@ -986,8 +986,8 @@ Other recommended versions available: }, }, }, - AnalysisOutput: proposalv1alpha1.AnalysisOutput{ - Mode: proposalv1alpha1.AnalysisOutputModeMinimal, + AnalysisOutput: agenticrunv1alpha1.AnalysisOutput{ + Mode: agenticrunv1alpha1.AnalysisOutputModeMinimal, Schema: analysisOutputSchema(), }, }, @@ -1003,7 +1003,7 @@ Other recommended versions available: "agentic.openshift.io/update-type": "Minor", }, }, - Spec: proposalv1alpha1.ProposalSpec{ + Spec: agenticrunv1alpha1.AgenticRunSpec{ Request: `Current version: OCP 4.15.3 Target version: OCP 4.16.3 Channel: stable-4.16 @@ -1019,11 +1019,11 @@ Other recommended versions available: - 4.16.1 ` + "## Cluster Readiness Data\n\n```json\n{}\n```\n", - Analysis: proposalv1alpha1.ProposalStep{ + Analysis: agenticrunv1alpha1.AgenticRunStep{ Agent: "smart", }, - Tools: proposalv1alpha1.ToolsSpec{ - Skills: []proposalv1alpha1.SkillsSource{ + Tools: agenticrunv1alpha1.ToolsSpec{ + Skills: []agenticrunv1alpha1.SkillsSource{ { Image: "quay.io/openshift/ci:ocp_5.0_agentic-skills", Paths: []string{ @@ -1033,8 +1033,8 @@ Other recommended versions available: }, }, }, - AnalysisOutput: proposalv1alpha1.AnalysisOutput{ - Mode: proposalv1alpha1.AnalysisOutputModeMinimal, + AnalysisOutput: agenticrunv1alpha1.AnalysisOutput{ + Mode: agenticrunv1alpha1.AnalysisOutputModeMinimal, Schema: analysisOutputSchema(), }, }, @@ -1071,7 +1071,7 @@ Other recommended versions available: for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - proposals, err := getProposals( + agenticRuns, err := getAgenticRuns( context.Background(), nil, tt.availableUpdates, @@ -1092,8 +1092,8 @@ Other recommended versions available: t.Errorf("unexpected error (-want +got):\n%s", diff) } - if diff := cmp.Diff(tt.expected, proposals); diff != "" { - t.Errorf("unexpected proposals (-want +got):\n%s", diff) + if diff := cmp.Diff(tt.expected, agenticRuns); diff != "" { + t.Errorf("unexpected agentic runs (-want +got):\n%s", diff) } }) } @@ -1101,24 +1101,24 @@ Other recommended versions available: func Test_expired(t *testing.T) { tests := []struct { - name string - proposal *proposalv1alpha1.Proposal - expected bool + name string + agenticRun *agenticrunv1alpha1.AgenticRun + expected bool }{ { - name: "nil proposal", + name: "nil agentic run", }, { - name: "proposal not expired", - proposal: &proposalv1alpha1.Proposal{ + name: "agentic run not expired", + agenticRun: &agenticrunv1alpha1.AgenticRun{ ObjectMeta: metav1.ObjectMeta{ CreationTimestamp: metav1.Time{Time: time.Now().Add(-12 * time.Hour)}, }, }, }, { - name: "proposal expired", - proposal: &proposalv1alpha1.Proposal{ + name: "agentic run expired", + agenticRun: &agenticrunv1alpha1.AgenticRun{ ObjectMeta: metav1.ObjectMeta{ CreationTimestamp: metav1.Time{Time: time.Now().Add(-25 * time.Hour)}, }, @@ -1129,7 +1129,7 @@ func Test_expired(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result := expired(tt.proposal) + result := expired(tt.agenticRun) if result != tt.expected { t.Errorf("expired() = %v, want %v", result, tt.expected) } @@ -1162,7 +1162,7 @@ func newFakeDynamicClient(objects ...runtime.Object) *dynamicfake.FakeDynamicCli return dynamicfake.NewSimpleDynamicClientWithCustomListKinds(s, gvrs, objects...) } -func TestGetProposals_WithReadinessData(t *testing.T) { +func TestGetAgenticRuns_WithReadinessData(t *testing.T) { dc := newFakeDynamicClient( // ClusterVersion &unstructured.Unstructured{Object: map[string]interface{}{ @@ -1290,7 +1290,7 @@ func TestGetProposals_WithReadinessData(t *testing.T) { }}, ) - proposals, err := getProposals( + agenticRuns, err := getAgenticRuns( context.Background(), dc, []configv1.Release{{Version: "4.21.8"}}, @@ -1302,16 +1302,16 @@ func TestGetProposals_WithReadinessData(t *testing.T) { "quay.io/openshift/ci:ocp_5.0_agentic-skills", ) if err != nil { - t.Fatalf("getProposals returned error: %v", err) + t.Fatalf("getAgenticRuns returned error: %v", err) } - if len(proposals) != 1 { - t.Fatalf("expected 1 proposal, got %d", len(proposals)) + if len(agenticRuns) != 1 { + t.Fatalf("expected 1 agentic run, got %d", len(agenticRuns)) } - request := proposals[0].Spec.Request + request := agenticRuns[0].Spec.Request if !strings.Contains(request, "## Cluster Readiness Data") { - t.Fatal("proposal request missing readiness data section") + t.Fatal("agentic run request missing readiness data section") } // Extract JSON from the request diff --git a/pkg/cvo/availableupdates.go b/pkg/cvo/availableupdates.go index 76345f5e87..bd568daef0 100644 --- a/pkg/cvo/availableupdates.go +++ b/pkg/cvo/availableupdates.go @@ -182,9 +182,9 @@ func (optr *Operator) syncAvailableUpdates(ctx context.Context, config *configv1 // queue optr.sync() to update ClusterVersion status optr.queue.Add(queueKey) - if optr.shouldEnableProposalController() { - // queue optr.proposalController.Sync() to manage proposals - optr.proposalController.Queue().Add(optr.proposalController.QueueKey()) + if optr.shouldEnableAgenticRunController() { + // queue optr.agenticRunController.Sync() to manage agentic runs + optr.agenticRunController.Queue().Add(optr.agenticRunController.QueueKey()) } return nil } diff --git a/pkg/cvo/availableupdates_test.go b/pkg/cvo/availableupdates_test.go index 9b1fb73f2b..d34c9942e5 100644 --- a/pkg/cvo/availableupdates_test.go +++ b/pkg/cvo/availableupdates_test.go @@ -27,11 +27,11 @@ import ( configv1 "github.com/openshift/api/config/v1" "github.com/openshift/api/features" + "github.com/openshift/cluster-version-operator/pkg/agenticrun" "github.com/openshift/cluster-version-operator/pkg/clusterconditions" "github.com/openshift/cluster-version-operator/pkg/clusterconditions/always" "github.com/openshift/cluster-version-operator/pkg/clusterconditions/mock" "github.com/openshift/cluster-version-operator/pkg/featuregates" - "github.com/openshift/cluster-version-operator/pkg/proposal" "github.com/openshift/cluster-version-operator/pkg/risk" riskmock "github.com/openshift/cluster-version-operator/pkg/risk/mock" ) @@ -205,7 +205,7 @@ func newOperator(url string, cluster release, promqlMock clusterconditions.Condi availableUpdates := &availableUpdates{ Current: configv1.Release{Version: cluster.version, Image: cluster.image}, } - operator.proposalController = proposal.NewController( + operator.agenticRunController = agenticrun.NewController( func() ([]configv1.Release, []configv1.ConditionalUpdate, error) { return nil, nil, nil }, diff --git a/pkg/cvo/cvo.go b/pkg/cvo/cvo.go index 52e776626a..5af33246b0 100644 --- a/pkg/cvo/cvo.go +++ b/pkg/cvo/cvo.go @@ -46,6 +46,7 @@ import ( "github.com/openshift/cluster-version-operator/lib/resourcebuilder" "github.com/openshift/cluster-version-operator/lib/validation" + "github.com/openshift/cluster-version-operator/pkg/agenticrun" "github.com/openshift/cluster-version-operator/pkg/clusterconditions" "github.com/openshift/cluster-version-operator/pkg/clusterconditions/standard" "github.com/openshift/cluster-version-operator/pkg/customsignaturestore" @@ -57,7 +58,6 @@ import ( "github.com/openshift/cluster-version-operator/pkg/payload" "github.com/openshift/cluster-version-operator/pkg/payload/precondition" preconditioncv "github.com/openshift/cluster-version-operator/pkg/payload/precondition/clusterversion" - "github.com/openshift/cluster-version-operator/pkg/proposal" "github.com/openshift/cluster-version-operator/pkg/risk" "github.com/openshift/cluster-version-operator/pkg/risk/adminack" "github.com/openshift/cluster-version-operator/pkg/risk/aggregate" @@ -220,8 +220,8 @@ type Operator struct { // that will be aggregated into conditional update risks. risks risk.Source - // proposalController, if enabled, watches available and conditionals updates and manage proposals for them - proposalController *proposal.Controller + // agenticRunController, if enabled, watches available and conditionals updates and manages agentic runs for them + agenticRunController *agenticrun.Controller } // New returns a new cluster version operator. @@ -355,7 +355,7 @@ func New( optr.configuration = configuration.NewClusterVersionOperatorConfiguration(operatorClient, operatorInformerFactory) - optr.proposalController = proposal.NewController( + optr.agenticRunController = agenticrun.NewController( func() ([]configv1.Release, []configv1.ConditionalUpdate, error) { availableUpdates := optr.getAvailableUpdates() if availableUpdates == nil { @@ -526,7 +526,7 @@ func (optr *Operator) Run(runContext context.Context, shutdownContext context.Co defer optr.queue.ShutDown() defer optr.availableUpdatesQueue.ShutDown() defer optr.configuration.Queue().ShutDown() - defer optr.proposalController.Queue().ShutDown() + defer optr.agenticRunController.Queue().ShutDown() stopCh := runContext.Done() klog.Infof("Starting ClusterVersionOperator with minimum reconcile period %s", optr.minimumUpdateCheckInterval) @@ -582,17 +582,17 @@ func (optr *Operator) Run(runContext context.Context, shutdownContext context.Co klog.Infof("The ClusterVersionOperatorConfiguration feature gate is disabled or HyperShift is detected; the configuration sync routine will not run.") } - if optr.shouldEnableProposalController() { + if optr.shouldEnableAgenticRunController() { resultChannelCount++ go func() { defer utilruntime.HandleCrash() wait.UntilWithContext(runContext, func(runContext context.Context) { - optr.worker(runContext, optr.proposalController.Queue(), optr.proposalController.Sync) + optr.worker(runContext, optr.agenticRunController.Queue(), optr.agenticRunController.Sync) }, time.Second) - resultChannel <- asyncResult{name: "proposal controller"} + resultChannel <- asyncResult{name: "agenticrun controller"} }() } else { - klog.Infof("The proposal controller is disabled.") + klog.Infof("The agenticrun controller is disabled.") } resultChannelCount++ @@ -651,7 +651,7 @@ func (optr *Operator) Run(runContext context.Context, shutdownContext context.Co optr.queue.ShutDown() optr.availableUpdatesQueue.ShutDown() optr.configuration.Queue().ShutDown() - optr.proposalController.Queue().ShutDown() + optr.agenticRunController.Queue().ShutDown() } } @@ -1222,8 +1222,8 @@ func (optr *Operator) shouldReconcileAcceptRisks() bool { return optr.enabledCVOFeatureGates.AcceptRisks() && !optr.hypershift } -// shouldEnableProposalController returns whether the CVO should enable the proposal controller -func (optr *Operator) shouldEnableProposalController() bool { +// shouldEnableAgenticRunController returns whether the CVO should enable the agentic run controller +func (optr *Operator) shouldEnableAgenticRunController() bool { // Gated behind a feature set so featuregates.ChangeStopper restarts CVO when the return of this function flips. return optr.requiredFeatureSet == configv1.TechPreviewNoUpgrade } diff --git a/pkg/cvo/cvo_test.go b/pkg/cvo/cvo_test.go index b1587e99db..f42b90ee1d 100644 --- a/pkg/cvo/cvo_test.go +++ b/pkg/cvo/cvo_test.go @@ -40,12 +40,12 @@ import ( "github.com/openshift/library-go/pkg/verify/store/serial" "github.com/openshift/library-go/pkg/verify/store/sigstore" + "github.com/openshift/cluster-version-operator/pkg/agenticrun" "github.com/openshift/cluster-version-operator/pkg/clusterconditions" "github.com/openshift/cluster-version-operator/pkg/clusterconditions/always" "github.com/openshift/cluster-version-operator/pkg/featuregates" "github.com/openshift/cluster-version-operator/pkg/internal" "github.com/openshift/cluster-version-operator/pkg/payload" - "github.com/openshift/cluster-version-operator/pkg/proposal" mockrisk "github.com/openshift/cluster-version-operator/pkg/risk/mock" ) @@ -2754,7 +2754,7 @@ func TestOperator_availableUpdatesSync(t *testing.T) { old := optr.availableUpdates ctx := context.Background() - optr.proposalController = proposal.NewController(func() ([]configv1.Release, []configv1.ConditionalUpdate, error) { + optr.agenticRunController = agenticrun.NewController(func() ([]configv1.Release, []configv1.ConditionalUpdate, error) { return nil, nil, nil }, ctrlruntimefake.NewClientBuilder().Build(), nil, func(_ string) (*configv1.ClusterVersion, error) { return &configv1.ClusterVersion{}, nil diff --git a/pkg/cvo/status.go b/pkg/cvo/status.go index 8a5e9f8ae8..4683760772 100644 --- a/pkg/cvo/status.go +++ b/pkg/cvo/status.go @@ -179,10 +179,10 @@ func (optr *Operator) syncStatus(ctx context.Context, original, config *configv1 if klog.V(6).Enabled() { klog.Infof("Apply config: %s", cmp.Diff(original, config)) } - if optr.shouldEnableProposalController() { + if optr.shouldEnableAgenticRunController() { if original != nil && len(config.Status.History) < len(original.Status.History) { - klog.V(internal.Normal).Infof("Reconciling proposals because ClusterVersion.status.history got pruned") - optr.proposalController.Queue().Add(optr.proposalController.QueueKey()) + klog.V(internal.Normal).Infof("Reconciling agentic runs because ClusterVersion.status.history got pruned") + optr.agenticRunController.Queue().Add(optr.agenticRunController.QueueKey()) } } updated, err := applyClusterVersionStatus(ctx, optr.client.ConfigV1(), config, original) diff --git a/pkg/internal/schema.go b/pkg/internal/schema.go index 0c84fd5f0d..d648582130 100644 --- a/pkg/internal/schema.go +++ b/pkg/internal/schema.go @@ -6,7 +6,7 @@ import ( "k8s.io/client-go/kubernetes/scheme" - proposalv1alpha1 "github.com/openshift/lightspeed-agentic-operator/api/v1alpha1" + agenticrunv1alpha1 "github.com/openshift/lightspeed-agentic-operator/api/v1alpha1" ) var ( @@ -14,12 +14,12 @@ var ( addSchemesErr error ) -// AddSchemes registers the proposalv1alpha1 scheme with the global Kubernetes scheme. +// AddSchemes registers the agenticrunv1alpha1 scheme with the global Kubernetes scheme. // This function is safe to call concurrently and will only execute once. func AddSchemes() error { addSchemesOnce.Do(func() { - if err := proposalv1alpha1.AddToScheme(scheme.Scheme); err != nil { - addSchemesErr = fmt.Errorf("failed to add proposalv1alpha1 to scheme: %w", err) + if err := agenticrunv1alpha1.AddToScheme(scheme.Scheme); err != nil { + addSchemesErr = fmt.Errorf("failed to add agenticrunv1alpha1 to scheme: %w", err) } }) return addSchemesErr diff --git a/test/cvo/proposal.go b/test/cvo/agenticrun.go similarity index 76% rename from test/cvo/proposal.go rename to test/cvo/agenticrun.go index d46fc8c59f..f846b73975 100644 --- a/test/cvo/proposal.go +++ b/test/cvo/agenticrun.go @@ -22,15 +22,15 @@ import ( oteginkgo "github.com/openshift-eng/openshift-tests-extension/pkg/ginkgo" configv1 "github.com/openshift/api/config/v1" configv1client "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1" - proposalv1alpha1 "github.com/openshift/lightspeed-agentic-operator/api/v1alpha1" + agenticrunv1alpha1 "github.com/openshift/lightspeed-agentic-operator/api/v1alpha1" + "github.com/openshift/cluster-version-operator/pkg/agenticrun" "github.com/openshift/cluster-version-operator/pkg/external" - "github.com/openshift/cluster-version-operator/pkg/proposal" "github.com/openshift/cluster-version-operator/test/util" ) func init() { - err := proposalv1alpha1.AddToScheme(scheme.Scheme) + err := agenticrunv1alpha1.AddToScheme(scheme.Scheme) if err != nil { panic(err) } @@ -89,7 +89,7 @@ var _ = g.Describe(`[Jira:"Cluster Version Operator"] cluster-version-operator`, }) g.It("should install light speed CRDs correctly", func() { - for _, name := range []string{"proposals.agentic.openshift.io", "agents.agentic.openshift.io", "analysisresults.agentic.openshift.io", "llmproviders.agentic.openshift.io"} { + for _, name := range []string{"agenticruns.agentic.openshift.io", "agents.agentic.openshift.io", "analysisresults.agentic.openshift.io", "llmproviders.agentic.openshift.io"} { _, err := apiExtensionsClient.ApiextensionsV1().CustomResourceDefinitions().Get(ctx, name, metav1.GetOptions{}) if util.IsTechPreviewNoUpgrade(ctx, c) { o.Expect(err).To(o.BeNil()) @@ -99,7 +99,7 @@ var _ = g.Describe(`[Jira:"Cluster Version Operator"] cluster-version-operator`, } }) - g.It("should create proposals", g.Label("OTA-1966"), g.Label("Serial"), oteginkgo.Informing(), func() { + g.It("should create agentic runs", g.Label("OTA-1966"), g.Label("Serial"), oteginkgo.Informing(), func() { o.Expect(util.SkipIfNetworkRestricted(ctx, c, util.FauxinnatiAPIURL)).To(o.BeNil()) util.SkipIfNotTechPreviewNoUpgrade(ctx, c) @@ -117,23 +117,23 @@ var _ = g.Describe(`[Jira:"Cluster Version Operator"] cluster-version-operator`, now := time.Now() g.By("Checking if the namespace exists") - _, err = kubeClient.CoreV1().Namespaces().Get(ctx, proposal.DefaultConfig().Namespace, metav1.GetOptions{}) + _, err = kubeClient.CoreV1().Namespaces().Get(ctx, agenticrun.DefaultConfig().Namespace, metav1.GetOptions{}) o.Expect(err).NotTo(o.HaveOccurred()) - g.By("Checking if the proposal are created") - var proposals proposalv1alpha1.ProposalList + g.By("Checking if the agentic runs are created") + var agenticRuns agenticrunv1alpha1.AgenticRunList o.Expect(wait.PollUntilContextTimeout(ctx, 30*time.Second, 5*time.Minute, true, func(ctx context.Context) (done bool, err error) { - proposals = proposalv1alpha1.ProposalList{} - err = rtClient.List(ctx, &proposals, ctrlruntimeclient.InNamespace(proposal.DefaultConfig().Namespace), - ctrlruntimeclient.MatchingLabels(proposal.CVOProposalLabels)) + agenticRuns = agenticrunv1alpha1.AgenticRunList{} + err = rtClient.List(ctx, &agenticRuns, ctrlruntimeclient.InNamespace(agenticrun.DefaultConfig().Namespace), + ctrlruntimeclient.MatchingLabels(agenticrun.CVOAgenticRunLabels)) o.Expect(err).NotTo(o.HaveOccurred()) - for _, proposal := range proposals.Items { - if proposal.CreationTimestamp.After(now) || - strings.Contains(proposal.Spec.Request, fmt.Sprintf("Channel: %s", channel)) { + for _, ar := range agenticRuns.Items { + if ar.CreationTimestamp.After(now) || + strings.Contains(ar.Spec.Request, fmt.Sprintf("Channel: %s", channel)) { return true, nil } } return false, nil - })).NotTo(o.HaveOccurred(), "expected proposals not found", "now", now.Format(time.RFC3339), "proposals are:\n%s", getYaml(&proposals)) + })).NotTo(o.HaveOccurred(), "expected agentic runs not found", "now", now.Format(time.RFC3339), "agentic runs are:\n%s", getYaml(&agenticRuns)) }) }) diff --git a/vendor/github.com/fxamacker/cbor/v2/.golangci.yml b/vendor/github.com/fxamacker/cbor/v2/.golangci.yml index 38cb9ae101..08081fbde5 100644 --- a/vendor/github.com/fxamacker/cbor/v2/.golangci.yml +++ b/vendor/github.com/fxamacker/cbor/v2/.golangci.yml @@ -1,104 +1,116 @@ -# Do not delete linter settings. Linters like gocritic can be enabled on the command line. - -linters-settings: - depguard: - rules: - prevent_unmaintained_packages: - list-mode: strict - files: - - $all - - "!$test" - allow: - - $gostd - - github.com/x448/float16 - deny: - - pkg: io/ioutil - desc: "replaced by io and os packages since Go 1.16: https://tip.golang.org/doc/go1.16#ioutil" - dupl: - threshold: 100 - funlen: - lines: 100 - statements: 50 - goconst: - ignore-tests: true - min-len: 2 - min-occurrences: 3 - gocritic: - enabled-tags: - - diagnostic - - experimental - - opinionated - - performance - - style - disabled-checks: - - commentedOutCode - - dupImport # https://github.com/go-critic/go-critic/issues/845 - - ifElseChain - - octalLiteral - - paramTypeCombine - - whyNoLint - gofmt: - simplify: false - goimports: - local-prefixes: github.com/fxamacker/cbor - golint: - min-confidence: 0 - govet: - check-shadowing: true - lll: - line-length: 140 - maligned: - suggest-new: true - misspell: - locale: US - staticcheck: - checks: ["all"] - +version: "2" linters: - disable-all: true + default: none enable: - asciicheck - bidichk - depguard - errcheck - - exportloopref + - forbidigo - goconst - gocritic - gocyclo - - gofmt - - goimports - goprintffuncname - gosec - - gosimple - govet - ineffassign - misspell - nilerr - revive - staticcheck - - stylecheck - - typecheck - unconvert - unused - + settings: + depguard: + rules: + prevent_unmaintained_packages: + list-mode: strict + files: + - $all + - '!$test' + allow: + - $gostd + - github.com/x448/float16 + deny: + - pkg: io/ioutil + desc: 'replaced by io and os packages since Go 1.16: https://tip.golang.org/doc/go1.16#ioutil' + dupl: + threshold: 100 + funlen: + lines: 100 + statements: 50 + goconst: + min-len: 2 + min-occurrences: 3 + gocritic: + disabled-checks: + - commentedOutCode + - dupImport + - ifElseChain + - octalLiteral + - paramTypeCombine + - whyNoLint + enabled-tags: + - diagnostic + - experimental + - opinionated + - performance + - style + govet: + enable: + - shadow + lll: + line-length: 140 + misspell: + locale: US + staticcheck: + checks: + - all + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + rules: + - path: decode.go + text: string ` overflows ` has (\d+) occurrences, make it a constant + - path: decode.go + text: string ` \(range is \[` has (\d+) occurrences, make it a constant + - path: decode.go + text: string `, ` has (\d+) occurrences, make it a constant + - path: decode.go + text: string ` overflows Go's int64` has (\d+) occurrences, make it a constant + - path: decode.go + text: string `\]\)` has (\d+) occurrences, make it a constant + - path: valid.go + text: string ` for type ` has (\d+) occurrences, make it a constant + - path: valid.go + text: 'string `cbor: ` has (\d+) occurrences, make it a constant' + - linters: + - goconst + path: (.+)_test\.go + paths: + - third_party$ + - builtin$ + - examples$ issues: - # max-issues-per-linter default is 50. Set to 0 to disable limit. max-issues-per-linter: 0 - # max-same-issues default is 3. Set to 0 to disable limit. max-same-issues: 0 - - exclude-rules: - - path: decode.go - text: "string ` overflows ` has (\\d+) occurrences, make it a constant" - - path: decode.go - text: "string ` \\(range is \\[` has (\\d+) occurrences, make it a constant" - - path: decode.go - text: "string `, ` has (\\d+) occurrences, make it a constant" - - path: decode.go - text: "string ` overflows Go's int64` has (\\d+) occurrences, make it a constant" - - path: decode.go - text: "string `\\]\\)` has (\\d+) occurrences, make it a constant" - - path: valid.go - text: "string ` for type ` has (\\d+) occurrences, make it a constant" - - path: valid.go - text: "string `cbor: ` has (\\d+) occurrences, make it a constant" +formatters: + enable: + - gofmt + - goimports + settings: + gofmt: + simplify: false + goimports: + local-prefixes: + - github.com/fxamacker/cbor + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ diff --git a/vendor/github.com/fxamacker/cbor/v2/README.md b/vendor/github.com/fxamacker/cbor/v2/README.md index d072b81c73..7aca561095 100644 --- a/vendor/github.com/fxamacker/cbor/v2/README.md +++ b/vendor/github.com/fxamacker/cbor/v2/README.md @@ -683,7 +683,7 @@ because RFC 8949 treats CBOR data item with remaining bytes as malformed. Other useful functions: - `Diagnose`, `DiagnoseFirst` produce human-readable [Extended Diagnostic Notation](https://www.rfc-editor.org/rfc/rfc8610.html#appendix-G) from CBOR data. - `UnmarshalFirst` decodes first CBOR data item and return any remaining bytes. -- `Wellformed` returns true if the CBOR data item is well-formed. +- `Wellformed` returns nil error if the CBOR data item is well-formed. Interfaces identical or comparable to Go `encoding` packages include: `Marshaler`, `Unmarshaler`, `BinaryMarshaler`, and `BinaryUnmarshaler`. @@ -702,28 +702,29 @@ Default limits may need to be increased for systems handling very large data (e. ## Status -[v2.9.0](https://github.com/fxamacker/cbor/releases/tag/v2.9.0) (Jul 13, 2025) improved interoperability/transcoding between CBOR & JSON, refactored tests, and improved docs. -- Add opt-in support for `encoding.TextMarshaler` and `encoding.TextUnmarshaler` to encode and decode from CBOR text string. -- Add opt-in support for `json.Marshaler` and `json.Unmarshaler` via user-provided transcoding function. -- Update docs for TimeMode, Tag, RawTag, and add example for Embedded JSON Tag for CBOR. +v2.9.2 (Sunday, May 3, 2026) refactors and hardens the streaming encoder by adding stricter checks for encoding to CBOR indefinite-length data. This prevents improper use of this library from producing malformed CBOR indefinite-length data that would be rejected by the decoder. -v2.9.0 passed fuzz tests and is production quality. +This release includes other bugfixes, defensive checks, and added more tests. -The minimum version of Go required to build: -- v2.8.0 and newer releases require go 1.20+. -- v2.7.1 and older releases require go 1.17+. +v2.9.2 passed fuzz tests (billions of executions) and is production quality. -For more details, see [release notes](https://github.com/fxamacker/cbor/releases). +For more details, see [v2.9.2 release notes](https://github.com/fxamacker/cbor/releases). ### Prior Releases -[v2.8.0](https://github.com/fxamacker/cbor/releases/tag/v2.8.0) (March 30, 2025) is a small release primarily to add `omitzero` option to struct field tags and fix bugs. It passed fuzz tests (billions of executions) and is production quality. +Releases and commits tend to be on Sundays because my work schedule didn't leave time to work on this during weekdays (sometimes consecutive weekends and consecutive Christmas breaks, too). Monday morning releases were to allow more fuzzing to run overnight before clicking the release button. -[v2.7.0](https://github.com/fxamacker/cbor/releases/tag/v2.7.0) (June 23, 2024) adds features and improvements that help large projects (e.g. Kubernetes) use CBOR as an alternative to JSON and Protocol Buffers. Other improvements include speedups, improved memory use, bug fixes, new serialization options, etc. It passed fuzz tests (5+ billion executions) and is production quality. +[v2.9.1](https://github.com/fxamacker/cbor/releases/tag/v2.9.1) (Monday, Mar 30, 2026) includes important bugfixes, defensive checks, improved code quality, and more tests. Although not public, the fuzzer was also improved by adding more fuzz tests. It passed fuzz tests (billions of executions) and is production quality. -[v2.6.0](https://github.com/fxamacker/cbor/releases/tag/v2.6.0) (February 2024) adds important new features, optimizations, and bug fixes. It is especially useful to systems that need to convert data between CBOR and JSON. New options and optimizations improve handling of bignum, integers, maps, and strings. +[v2.9.0](https://github.com/fxamacker/cbor/releases/tag/v2.9.0) (Sunday, Jul 13, 2025) improved interoperability/transcoding between CBOR & JSON, refactored tests, and improved docs. It passed fuzz tests (billions of executions) and is production quality. -[v2.5.0](https://github.com/fxamacker/cbor/releases/tag/v2.5.0) was released on Sunday, August 13, 2023 with new features and important bug fixes. It is fuzz tested and production quality after extended beta [v2.5.0-beta](https://github.com/fxamacker/cbor/releases/tag/v2.5.0-beta) (Dec 2022) -> [v2.5.0](https://github.com/fxamacker/cbor/releases/tag/v2.5.0) (Aug 2023). +[v2.8.0](https://github.com/fxamacker/cbor/releases/tag/v2.8.0) (Sunday, March 30, 2025) is a small release primarily to add `omitzero` option to struct field tags and fix bugs. It passed fuzz tests (billions of executions) and is production quality. + +[v2.7.0](https://github.com/fxamacker/cbor/releases/tag/v2.7.0) (Monday, June 23, 2024) adds features and improvements that help large projects (e.g. Kubernetes) use CBOR as an alternative to JSON and Protocol Buffers. Other improvements include speedups, improved memory use, bug fixes, new serialization options, etc. It passed fuzz tests (5+ billion executions) and is production quality. + +[v2.6.0](https://github.com/fxamacker/cbor/releases/tag/v2.6.0) (Sunday, Feb 11, 2024) adds important new features, optimizations, and bug fixes. It is especially useful to systems that need to convert data between CBOR and JSON. New options and optimizations improve handling of bignum, integers, maps, and strings. + +[v2.5.0](https://github.com/fxamacker/cbor/releases/tag/v2.5.0) (Sunday, August 13, 2023) adds new features and important bug fixes. It is fuzz tested and production quality after extended beta [v2.5.0-beta](https://github.com/fxamacker/cbor/releases/tag/v2.5.0-beta) (Dec 2022) -> [v2.5.0](https://github.com/fxamacker/cbor/releases/tag/v2.5.0) (Aug 2023). __IMPORTANT__: 👉 Before upgrading from v2.4 or older release, please read the notable changes highlighted in the release notes. v2.5.0 is a large release with bug fixes to error handling for extraneous data in `Unmarshal`, etc. that should be reviewed before upgrading. diff --git a/vendor/github.com/fxamacker/cbor/v2/cache.go b/vendor/github.com/fxamacker/cbor/v2/cache.go index 5051f110fb..5743f3eb25 100644 --- a/vendor/github.com/fxamacker/cbor/v2/cache.go +++ b/vendor/github.com/fxamacker/cbor/v2/cache.go @@ -92,94 +92,126 @@ func newTypeInfo(t reflect.Type) *typeInfo { } type decodingStructType struct { - fields fields - fieldIndicesByName map[string]int - err error - toArray bool + fields decodingFields + fieldIndicesByName map[string]int // Only populated if toArray is false + fieldIndicesByIntKey map[int64]int // Only populated if toArray is false + err error + toArray bool } -// The stdlib errors.Join was introduced in Go 1.20, and we still support Go 1.17, so instead, -// here's a very basic implementation of an aggregated error. -type multierror []error - -func (m multierror) Error() string { - var sb strings.Builder - for i, err := range m { - sb.WriteString(err.Error()) - if i < len(m)-1 { - sb.WriteString(", ") - } - } - return sb.String() -} - -func getDecodingStructType(t reflect.Type) *decodingStructType { +func getDecodingStructType(t reflect.Type) (*decodingStructType, error) { if v, _ := decodingStructTypeCache.Load(t); v != nil { - return v.(*decodingStructType) + structType := v.(*decodingStructType) + if structType.err != nil { + return nil, structType.err + } + return structType, nil } flds, structOptions := getFields(t) toArray := hasToArrayOption(structOptions) - var errs []error - for i := 0; i < len(flds); i++ { - if flds[i].keyAsInt { - nameAsInt, numErr := strconv.Atoi(flds[i].name) - if numErr != nil { - errs = append(errs, errors.New("cbor: failed to parse field name \""+flds[i].name+"\" to int ("+numErr.Error()+")")) - break + if toArray { + return getDecodingStructToArrayType(t, flds) + } + + fieldIndicesByName := make(map[string]int, len(flds)) + var fieldIndicesByIntKey map[int64]int + + decFlds := make(decodingFields, len(flds)) + for i, f := range flds { + // nameAsInt is set in getFields() except for fields with an unparsable tagged name. + // Atoi() is called here to catch and save parsing errors. + if f.keyAsInt && f.nameAsInt == 0 { + if _, numErr := strconv.Atoi(f.name); numErr != nil { + structType := &decodingStructType{ + err: errors.New("cbor: failed to parse field name \"" + f.name + "\" to int (" + numErr.Error() + ")"), + } + decodingStructTypeCache.Store(t, structType) + return nil, structType.err } - flds[i].nameAsInt = int64(nameAsInt) } - flds[i].typInfo = getTypeInfo(flds[i].typ) - } + if f.keyAsInt { + if fieldIndicesByIntKey == nil { + fieldIndicesByIntKey = make(map[int64]int, len(flds)) + } + // The duplication check is only a safeguard, since getFields() already deduplicates fields. + if _, ok := fieldIndicesByIntKey[f.nameAsInt]; ok { + structType := &decodingStructType{ + err: fmt.Errorf("cbor: two or more fields of %v have the same keyasint value %d", t, f.nameAsInt), + } + decodingStructTypeCache.Store(t, structType) + return nil, structType.err + } + fieldIndicesByIntKey[f.nameAsInt] = i + } else { + // The duplication check is only a safeguard, since getFields() already deduplicates fields. + if _, ok := fieldIndicesByName[f.name]; ok { + structType := &decodingStructType{ + err: fmt.Errorf("cbor: two or more fields of %v have the same name %q", t, f.name), + } + decodingStructTypeCache.Store(t, structType) + return nil, structType.err + } + fieldIndicesByName[f.name] = i + } - fieldIndicesByName := make(map[string]int, len(flds)) - for i, fld := range flds { - if _, ok := fieldIndicesByName[fld.name]; ok { - errs = append(errs, fmt.Errorf("cbor: two or more fields of %v have the same name %q", t, fld.name)) - continue + decFlds[i] = &decodingField{ + field: *f, + typInfo: getTypeInfo(f.typ), } - fieldIndicesByName[fld.name] = i } - var err error - { - var multi multierror - for _, each := range errs { - if each != nil { - multi = append(multi, each) + structType := &decodingStructType{ + fields: decFlds, + fieldIndicesByName: fieldIndicesByName, + fieldIndicesByIntKey: fieldIndicesByIntKey, + } + decodingStructTypeCache.Store(t, structType) + return structType, nil +} + +func getDecodingStructToArrayType(t reflect.Type, flds fields) (*decodingStructType, error) { + decFlds := make(decodingFields, len(flds)) + for i, f := range flds { + // nameAsInt is set in getFields() except for fields with an unparsable tagged name. + // Atoi() is called here to catch and save parsing errors. + if f.keyAsInt && f.nameAsInt == 0 { + if _, numErr := strconv.Atoi(f.name); numErr != nil { + structType := &decodingStructType{ + err: errors.New("cbor: failed to parse field name \"" + f.name + "\" to int (" + numErr.Error() + ")"), + } + decodingStructTypeCache.Store(t, structType) + return nil, structType.err } } - if len(multi) == 1 { - err = multi[0] - } else if len(multi) > 1 { - err = multi + + decFlds[i] = &decodingField{ + field: *f, + typInfo: getTypeInfo(f.typ), } } structType := &decodingStructType{ - fields: flds, - fieldIndicesByName: fieldIndicesByName, - err: err, - toArray: toArray, + fields: decFlds, + toArray: true, } decodingStructTypeCache.Store(t, structType) - return structType + return structType, nil } type encodingStructType struct { - fields fields - bytewiseFields fields - lengthFirstFields fields - omitEmptyFieldsIdx []int + fields encodingFields + bytewiseFields encodingFields // Only populated if toArray is false + lengthFirstFields encodingFields // Only populated if toArray is false + omitEmptyFieldsIdx []int // Only populated if toArray is false err error toArray bool } -func (st *encodingStructType) getFields(em *encMode) fields { +func (st *encodingStructType) getFields(em *encMode) encodingFields { switch em.sort { case SortNone, SortFastShuffle: return st.fields @@ -191,7 +223,7 @@ func (st *encodingStructType) getFields(em *encMode) fields { } type bytewiseFieldSorter struct { - fields fields + fields encodingFields } func (x *bytewiseFieldSorter) Len() int { @@ -203,11 +235,11 @@ func (x *bytewiseFieldSorter) Swap(i, j int) { } func (x *bytewiseFieldSorter) Less(i, j int) bool { - return bytes.Compare(x.fields[i].cborName, x.fields[j].cborName) <= 0 + return bytes.Compare(x.fields[i].cborName, x.fields[j].cborName) < 0 } type lengthFirstFieldSorter struct { - fields fields + fields encodingFields } func (x *lengthFirstFieldSorter) Len() int { @@ -222,13 +254,16 @@ func (x *lengthFirstFieldSorter) Less(i, j int) bool { if len(x.fields[i].cborName) != len(x.fields[j].cborName) { return len(x.fields[i].cborName) < len(x.fields[j].cborName) } - return bytes.Compare(x.fields[i].cborName, x.fields[j].cborName) <= 0 + return bytes.Compare(x.fields[i].cborName, x.fields[j].cborName) < 0 } func getEncodingStructType(t reflect.Type) (*encodingStructType, error) { if v, _ := encodingStructTypeCache.Load(t); v != nil { structType := v.(*encodingStructType) - return structType, structType.err + if structType.err != nil { + return nil, structType.err + } + return structType, nil } flds, structOptions := getFields(t) @@ -237,111 +272,119 @@ func getEncodingStructType(t reflect.Type) (*encodingStructType, error) { return getEncodingStructToArrayType(t, flds) } - var err error var hasKeyAsInt bool var hasKeyAsStr bool var omitEmptyIdx []int + + encFlds := make(encodingFields, len(flds)) + e := getEncodeBuffer() - for i := 0; i < len(flds); i++ { + defer putEncodeBuffer(e) + + for i, f := range flds { + encFlds[i] = &encodingField{field: *f} + ef := encFlds[i] + // Get field's encodeFunc - flds[i].ef, flds[i].ief, flds[i].izf = getEncodeFunc(flds[i].typ) - if flds[i].ef == nil { - err = &UnsupportedTypeError{t} - break + ef.ef, ef.ief, ef.izf = getEncodeFunc(f.typ) + if ef.ef == nil { + structType := &encodingStructType{err: &UnsupportedTypeError{t}} + encodingStructTypeCache.Store(t, structType) + return nil, structType.err } // Encode field name - if flds[i].keyAsInt { - nameAsInt, numErr := strconv.Atoi(flds[i].name) - if numErr != nil { - err = errors.New("cbor: failed to parse field name \"" + flds[i].name + "\" to int (" + numErr.Error() + ")") - break + if f.keyAsInt { + if f.nameAsInt == 0 { + // nameAsInt is set in getFields() except for fields with an unparsable tagged name. + // Atoi() is called here to catch and save parsing errors. + if _, numErr := strconv.Atoi(f.name); numErr != nil { + structType := &encodingStructType{ + err: errors.New("cbor: failed to parse field name \"" + f.name + "\" to int (" + numErr.Error() + ")"), + } + encodingStructTypeCache.Store(t, structType) + return nil, structType.err + } } - flds[i].nameAsInt = int64(nameAsInt) + nameAsInt := f.nameAsInt if nameAsInt >= 0 { - encodeHead(e, byte(cborTypePositiveInt), uint64(nameAsInt)) + encodeHead(e, byte(cborTypePositiveInt), uint64(nameAsInt)) //nolint:gosec } else { n := nameAsInt*(-1) - 1 - encodeHead(e, byte(cborTypeNegativeInt), uint64(n)) + encodeHead(e, byte(cborTypeNegativeInt), uint64(n)) //nolint:gosec } - flds[i].cborName = make([]byte, e.Len()) - copy(flds[i].cborName, e.Bytes()) + ef.cborName = make([]byte, e.Len()) + copy(ef.cborName, e.Bytes()) e.Reset() hasKeyAsInt = true } else { - encodeHead(e, byte(cborTypeTextString), uint64(len(flds[i].name))) - flds[i].cborName = make([]byte, e.Len()+len(flds[i].name)) - n := copy(flds[i].cborName, e.Bytes()) - copy(flds[i].cborName[n:], flds[i].name) + encodeHead(e, byte(cborTypeTextString), uint64(len(f.name))) + ef.cborName = make([]byte, e.Len()+len(f.name)) + n := copy(ef.cborName, e.Bytes()) + copy(ef.cborName[n:], f.name) e.Reset() // If cborName contains a text string, then cborNameByteString contains a // string that has the byte string major type but is otherwise identical to // cborName. - flds[i].cborNameByteString = make([]byte, len(flds[i].cborName)) - copy(flds[i].cborNameByteString, flds[i].cborName) + ef.cborNameByteString = make([]byte, len(ef.cborName)) + copy(ef.cborNameByteString, ef.cborName) // Reset encoded CBOR type to byte string, preserving the "additional // information" bits: - flds[i].cborNameByteString[0] = byte(cborTypeByteString) | - getAdditionalInformation(flds[i].cborNameByteString[0]) + ef.cborNameByteString[0] = byte(cborTypeByteString) | + getAdditionalInformation(ef.cborNameByteString[0]) hasKeyAsStr = true } // Check if field can be omitted when empty - if flds[i].omitEmpty { + if f.omitEmpty { omitEmptyIdx = append(omitEmptyIdx, i) } } - putEncodeBuffer(e) - - if err != nil { - structType := &encodingStructType{err: err} - encodingStructTypeCache.Store(t, structType) - return structType, structType.err - } // Sort fields by canonical order - bytewiseFields := make(fields, len(flds)) - copy(bytewiseFields, flds) + bytewiseFields := make(encodingFields, len(encFlds)) + copy(bytewiseFields, encFlds) sort.Sort(&bytewiseFieldSorter{bytewiseFields}) lengthFirstFields := bytewiseFields if hasKeyAsInt && hasKeyAsStr { - lengthFirstFields = make(fields, len(flds)) - copy(lengthFirstFields, flds) + lengthFirstFields = make(encodingFields, len(encFlds)) + copy(lengthFirstFields, encFlds) sort.Sort(&lengthFirstFieldSorter{lengthFirstFields}) } structType := &encodingStructType{ - fields: flds, + fields: encFlds, bytewiseFields: bytewiseFields, lengthFirstFields: lengthFirstFields, omitEmptyFieldsIdx: omitEmptyIdx, } encodingStructTypeCache.Store(t, structType) - return structType, structType.err + return structType, nil } func getEncodingStructToArrayType(t reflect.Type, flds fields) (*encodingStructType, error) { - for i := 0; i < len(flds); i++ { - // Get field's encodeFunc - flds[i].ef, flds[i].ief, flds[i].izf = getEncodeFunc(flds[i].typ) - if flds[i].ef == nil { + encFlds := make(encodingFields, len(flds)) + for i, f := range flds { + encFlds[i] = &encodingField{field: *f} + encFlds[i].ef, encFlds[i].ief, encFlds[i].izf = getEncodeFunc(f.typ) + if encFlds[i].ef == nil { structType := &encodingStructType{err: &UnsupportedTypeError{t}} encodingStructTypeCache.Store(t, structType) - return structType, structType.err + return nil, structType.err } } structType := &encodingStructType{ - fields: flds, + fields: encFlds, toArray: true, } encodingStructTypeCache.Store(t, structType) - return structType, structType.err + return structType, nil } func getEncodeFunc(t reflect.Type) (encodeFunc, isEmptyFunc, isZeroFunc) { diff --git a/vendor/github.com/fxamacker/cbor/v2/decode.go b/vendor/github.com/fxamacker/cbor/v2/decode.go index f0bdc3b38d..1d3e63d068 100644 --- a/vendor/github.com/fxamacker/cbor/v2/decode.go +++ b/vendor/github.com/fxamacker/cbor/v2/decode.go @@ -16,7 +16,6 @@ import ( "math/big" "reflect" "strconv" - "strings" "time" "unicode/utf8" @@ -326,14 +325,14 @@ func (dmkm DupMapKeyMode) valid() bool { return dmkm >= 0 && dmkm < maxDupMapKeyMode } -// IndefLengthMode specifies whether to allow indefinite length items. +// IndefLengthMode specifies whether to allow indefinite-length items. type IndefLengthMode int const ( - // IndefLengthAllowed allows indefinite length items. + // IndefLengthAllowed allows indefinite-length items. IndefLengthAllowed IndefLengthMode = iota - // IndefLengthForbidden disallows indefinite length items. + // IndefLengthForbidden disallows indefinite-length items. IndefLengthForbidden maxIndefLengthMode @@ -378,6 +377,7 @@ const ( // - int64 if value fits // - big.Int or *big.Int (see BigIntDecMode) if value < math.MinInt64 // - return UnmarshalTypeError if value > math.MaxInt64 + // // Deprecated: IntDecConvertSigned should not be used. // Please use other options, such as IntDecConvertSignedOrError, IntDecConvertSignedOrBigInt, IntDecConvertNone. IntDecConvertSigned @@ -811,7 +811,7 @@ type DecOptions struct { // Default is 128*1024=131072 and it can be set to [16, 2147483647] MaxMapPairs int - // IndefLength specifies whether to allow indefinite length CBOR items. + // IndefLength specifies whether to allow indefinite-length CBOR items. IndefLength IndefLengthMode // TagsMd specifies whether to allow CBOR tags (major type 6). @@ -1055,7 +1055,7 @@ func (opts DecOptions) decMode() (*decMode, error) { //nolint:gocritic // ignore } if !opts.ExtraReturnErrors.valid() { - return nil, errors.New("cbor: invalid ExtraReturnErrors " + strconv.Itoa(int(opts.ExtraReturnErrors))) + return nil, errors.New("cbor: invalid ExtraReturnErrors " + strconv.Itoa(int(opts.ExtraReturnErrors))) //nolint:gosec } if opts.DefaultMapType != nil && opts.DefaultMapType.Kind() != reflect.Map { @@ -1149,8 +1149,8 @@ func (opts DecOptions) decMode() (*decMode, error) { //nolint:gocritic // ignore unrecognizedTagToAny: opts.UnrecognizedTagToAny, timeTagToAny: opts.TimeTagToAny, simpleValues: simpleValues, - nanDec: opts.NaN, - infDec: opts.Inf, + nan: opts.NaN, + inf: opts.Inf, byteStringToTime: opts.ByteStringToTime, byteStringExpectedFormat: opts.ByteStringExpectedFormat, bignumTag: opts.BignumTag, @@ -1230,8 +1230,8 @@ type decMode struct { unrecognizedTagToAny UnrecognizedTagToAnyMode timeTagToAny TimeTagToAnyMode simpleValues *SimpleValueRegistry - nanDec NaNMode - infDec InfMode + nan NaNMode + inf InfMode byteStringToTime ByteStringToTimeMode byteStringExpectedFormat ByteStringExpectedFormatMode bignumTag BignumTagMode @@ -1272,8 +1272,8 @@ func (dm *decMode) DecOptions() DecOptions { UnrecognizedTagToAny: dm.unrecognizedTagToAny, TimeTagToAny: dm.timeTagToAny, SimpleValues: simpleValues, - NaN: dm.nanDec, - Inf: dm.infDec, + NaN: dm.nan, + Inf: dm.inf, ByteStringToTime: dm.byteStringToTime, ByteStringExpectedFormat: dm.byteStringExpectedFormat, BignumTag: dm.bignumTag, @@ -1583,11 +1583,11 @@ func (d *decoder) parseToValue(v reflect.Value, tInfo *typeInfo) error { //nolin _, ai, val := d.getHead() switch ai { case additionalInformationAsFloat16: - f := float64(float16.Frombits(uint16(val)).Float32()) + f := float64(float16.Frombits(uint16(val)).Float32()) //nolint:gosec return fillFloat(t, f, v) case additionalInformationAsFloat32: - f := float64(math.Float32frombits(uint32(val))) + f := float64(math.Float32frombits(uint32(val))) //nolint:gosec return fillFloat(t, f, v) case additionalInformationAsFloat64: @@ -1595,10 +1595,10 @@ func (d *decoder) parseToValue(v reflect.Value, tInfo *typeInfo) error { //nolin return fillFloat(t, f, v) default: // ai <= 24 - if d.dm.simpleValues.rejected[SimpleValue(val)] { + if d.dm.simpleValues.rejected[SimpleValue(val)] { //nolint:gosec return &UnacceptableDataItemError{ CBORType: t.String(), - Message: "simple value " + strconv.FormatInt(int64(val), 10) + " is not recognized", + Message: "simple value " + strconv.FormatInt(int64(val), 10) + " is not recognized", //nolint:gosec } } @@ -1677,20 +1677,23 @@ func (d *decoder) parseToValue(v reflect.Value, tInfo *typeInfo) error { //nolin return d.parseToValue(v, tInfo) case cborTypeArray: - if tInfo.nonPtrKind == reflect.Slice { + switch tInfo.nonPtrKind { + case reflect.Slice: return d.parseArrayToSlice(v, tInfo) - } else if tInfo.nonPtrKind == reflect.Array { + case reflect.Array: return d.parseArrayToArray(v, tInfo) - } else if tInfo.nonPtrKind == reflect.Struct { + case reflect.Struct: return d.parseArrayToStruct(v, tInfo) } + d.skip() return &UnmarshalTypeError{CBORType: t.String(), GoType: tInfo.nonPtrType.String()} case cborTypeMap: - if tInfo.nonPtrKind == reflect.Struct { + switch tInfo.nonPtrKind { + case reflect.Struct: return d.parseMapToStruct(v, tInfo) - } else if tInfo.nonPtrKind == reflect.Map { + case reflect.Map: return d.parseMapToMap(v, tInfo) } d.skip() @@ -1745,8 +1748,8 @@ func (d *decoder) parseToTime() (time.Time, bool, error) { // Read tag number _, _, tagNum := d.getHead() if tagNum != 0 && tagNum != 1 { - d.skip() // skip tag content - return time.Time{}, false, errors.New("cbor: wrong tag number for time.Time, got " + strconv.Itoa(int(tagNum)) + ", expect 0 or 1") + d.skip() // skip tag content + return time.Time{}, false, errors.New("cbor: wrong tag number for time.Time, got " + strconv.Itoa(int(tagNum)) + ", expect 0 or 1") //nolint:gosec } } } else { @@ -1815,10 +1818,10 @@ func (d *decoder) parseToTime() (time.Time, bool, error) { var f float64 switch ai { case additionalInformationAsFloat16: - f = float64(float16.Frombits(uint16(val)).Float32()) + f = float64(float16.Frombits(uint16(val)).Float32()) //nolint:gosec case additionalInformationAsFloat32: - f = float64(math.Float32frombits(uint32(val))) + f = float64(math.Float32frombits(uint32(val))) //nolint:gosec case additionalInformationAsFloat64: f = math.Float64frombits(val) @@ -1832,6 +1835,13 @@ func (d *decoder) parseToTime() (time.Time, bool, error) { return time.Time{}, true, nil } seconds, fractional := math.Modf(f) + if seconds > math.MaxInt64 || seconds < math.MinInt64 { + return time.Time{}, false, &UnmarshalTypeError{ + CBORType: t.String(), + GoType: typeTime.String(), + errorMsg: fmt.Sprintf("%v overflows Go's int64", f), + } + } return time.Unix(int64(seconds), int64(fractional*1e9)), true, nil default: @@ -2145,14 +2155,14 @@ func (d *decoder) parse(skipSelfDescribedTag bool) (any, error) { //nolint:gocyc case cborTypePrimitives: _, ai, val := d.getHead() - if ai <= 24 && d.dm.simpleValues.rejected[SimpleValue(val)] { + if ai <= 24 && d.dm.simpleValues.rejected[SimpleValue(val)] { //nolint:gosec return nil, &UnacceptableDataItemError{ CBORType: t.String(), - Message: "simple value " + strconv.FormatInt(int64(val), 10) + " is not recognized", + Message: "simple value " + strconv.FormatInt(int64(val), 10) + " is not recognized", //nolint:gosec } } if ai < 20 || ai == 24 { - return SimpleValue(val), nil + return SimpleValue(val), nil //nolint:gosec } switch ai { @@ -2165,11 +2175,11 @@ func (d *decoder) parse(skipSelfDescribedTag bool) (any, error) { //nolint:gocyc return nil, nil case additionalInformationAsFloat16: - f := float64(float16.Frombits(uint16(val)).Float32()) + f := float64(float16.Frombits(uint16(val)).Float32()) //nolint:gosec return f, nil case additionalInformationAsFloat32: - f := float64(math.Float32frombits(uint32(val))) + f := float64(math.Float32frombits(uint32(val))) //nolint:gosec return f, nil case additionalInformationAsFloat64: @@ -2202,16 +2212,16 @@ func (d *decoder) parse(skipSelfDescribedTag bool) (any, error) { //nolint:gocyc func (d *decoder) parseByteString() ([]byte, bool) { _, _, val, indefiniteLength := d.getHeadWithIndefiniteLengthFlag() if !indefiniteLength { - b := d.data[d.off : d.off+int(val)] - d.off += int(val) + b := d.data[d.off : d.off+int(val)] //nolint:gosec + d.off += int(val) //nolint:gosec return b, false } - // Process indefinite length string chunks. + // Process indefinite-length string chunks. b := []byte{} for !d.foundBreak() { _, _, val = d.getHead() - b = append(b, d.data[d.off:d.off+int(val)]...) - d.off += int(val) + b = append(b, d.data[d.off:d.off+int(val)]...) //nolint:gosec + d.off += int(val) //nolint:gosec } return b, true } @@ -2253,7 +2263,7 @@ func (d *decoder) applyByteStringTextConversion( default: // If this happens, there is a bug: the decoder has pushed an invalid // "expected later encoding" tag to the stack. - panic(fmt.Sprintf("unrecognized expected later encoding tag: %d", d.expectedLaterEncodingTags)) + panic(fmt.Sprintf("unrecognized expected later encoding tag: %d", d.expectedLaterEncodingTags[len(d.expectedLaterEncodingTags)-1])) } case reflect.Slice: @@ -2300,19 +2310,19 @@ func (d *decoder) applyByteStringTextConversion( func (d *decoder) parseTextString() ([]byte, error) { _, _, val, indefiniteLength := d.getHeadWithIndefiniteLengthFlag() if !indefiniteLength { - b := d.data[d.off : d.off+int(val)] - d.off += int(val) + b := d.data[d.off : d.off+int(val)] //nolint:gosec + d.off += int(val) //nolint:gosec if d.dm.utf8 == UTF8RejectInvalid && !utf8.Valid(b) { return nil, &SemanticError{"cbor: invalid UTF-8 string"} } return b, nil } - // Process indefinite length string chunks. + // Process indefinite-length string chunks. b := []byte{} for !d.foundBreak() { _, _, val = d.getHead() - x := d.data[d.off : d.off+int(val)] - d.off += int(val) + x := d.data[d.off : d.off+int(val)] //nolint:gosec + d.off += int(val) //nolint:gosec if d.dm.utf8 == UTF8RejectInvalid && !utf8.Valid(x) { for !d.foundBreak() { d.skip() // Skip remaining chunk on error @@ -2327,7 +2337,7 @@ func (d *decoder) parseTextString() ([]byte, error) { func (d *decoder) parseArray() ([]any, error) { _, _, val, indefiniteLength := d.getHeadWithIndefiniteLengthFlag() hasSize := !indefiniteLength - count := int(val) + count := int(val) //nolint:gosec if !hasSize { count = d.numOfItemsUntilBreak() // peek ahead to get array size to preallocate slice for better performance } @@ -2349,7 +2359,7 @@ func (d *decoder) parseArray() ([]any, error) { func (d *decoder) parseArrayToSlice(v reflect.Value, tInfo *typeInfo) error { _, _, val, indefiniteLength := d.getHeadWithIndefiniteLengthFlag() hasSize := !indefiniteLength - count := int(val) + count := int(val) //nolint:gosec if !hasSize { count = d.numOfItemsUntilBreak() // peek ahead to get array size to preallocate slice for better performance } @@ -2371,7 +2381,7 @@ func (d *decoder) parseArrayToSlice(v reflect.Value, tInfo *typeInfo) error { func (d *decoder) parseArrayToArray(v reflect.Value, tInfo *typeInfo) error { _, _, val, indefiniteLength := d.getHeadWithIndefiniteLengthFlag() hasSize := !indefiniteLength - count := int(val) + count := int(val) //nolint:gosec gi := 0 vLen := v.Len() var err error @@ -2400,7 +2410,7 @@ func (d *decoder) parseArrayToArray(v reflect.Value, tInfo *typeInfo) error { func (d *decoder) parseMap() (any, error) { _, _, val, indefiniteLength := d.getHeadWithIndefiniteLengthFlag() hasSize := !indefiniteLength - count := int(val) + count := int(val) //nolint:gosec m := make(map[any]any) var k, e any var err, lastErr error @@ -2465,7 +2475,7 @@ func (d *decoder) parseMap() (any, error) { func (d *decoder) parseMapToMap(v reflect.Value, tInfo *typeInfo) error { //nolint:gocyclo _, _, val, indefiniteLength := d.getHeadWithIndefiniteLengthFlag() hasSize := !indefiniteLength - count := int(val) + count := int(val) //nolint:gosec if v.IsNil() { mapsize := count if !hasSize { @@ -2566,9 +2576,9 @@ func (d *decoder) parseMapToMap(v reflect.Value, tInfo *typeInfo) error { //noli } func (d *decoder) parseArrayToStruct(v reflect.Value, tInfo *typeInfo) error { - structType := getDecodingStructType(tInfo.nonPtrType) - if structType.err != nil { - return structType.err + structType, structTypeErr := getDecodingStructType(tInfo.nonPtrType) + if structTypeErr != nil { + return structTypeErr } if !structType.toArray { @@ -2584,7 +2594,7 @@ func (d *decoder) parseArrayToStruct(v reflect.Value, tInfo *typeInfo) error { start := d.off _, _, val, indefiniteLength := d.getHeadWithIndefiniteLengthFlag() hasSize := !indefiniteLength - count := int(val) + count := int(val) //nolint:gosec if !hasSize { count = d.numOfItemsUntilBreak() // peek ahead to get array size } @@ -2637,11 +2647,72 @@ func (d *decoder) parseArrayToStruct(v reflect.Value, tInfo *typeInfo) error { return err } -// parseMapToStruct needs to be fast so gocyclo can be ignored for now. +// skipMapEntriesFromIndex skips remaining map entries starting from index i. +func (d *decoder) skipMapEntriesFromIndex(i, count int, hasSize bool) { + for ; (hasSize && i < count) || (!hasSize && !d.foundBreak()); i++ { + d.skip() + d.skip() + } +} + +// skipMapForDupKey skips the current map value and all remaining map entries, +// then returns a DupMapKeyError for the given key at map index i. +func (d *decoder) skipMapForDupKey(dupKey any, i, count int, hasSize bool) error { + // Skip the value of the duplicate key. + d.skip() + // Skip all remaining map entries. + d.skipMapEntriesFromIndex(i+1, count, hasSize) + return &DupMapKeyError{dupKey, i} +} + +// skipMapForUnknownField skips the current map value and all remaining map entries, +// then returns a UnknownFieldError for the given key at map index i. +func (d *decoder) skipMapForUnknownField(i, count int, hasSize bool) error { + // Skip the value of the unknown key. + d.skip() + // Skip all remaining map entries. + d.skipMapEntriesFromIndex(i+1, count, hasSize) + return &UnknownFieldError{i} +} + +// decodeToStructField decodes the next CBOR value into the struct field f in v. +// If the field cannot be resolved, the CBOR value is skipped. +func (d *decoder) decodeToStructField(v reflect.Value, f *decodingField, tInfo *typeInfo) error { + var fv reflect.Value + + if len(f.idx) == 1 { + fv = v.Field(f.idx[0]) + } else { + var err error + fv, err = getFieldValue(v, f.idx, func(v reflect.Value) (reflect.Value, error) { + // Return a new value for embedded field null pointer to point to, or return error. + if !v.CanSet() { + return reflect.Value{}, errors.New("cbor: cannot set embedded pointer to unexported struct: " + v.Type().String()) + } + v.Set(reflect.New(v.Type().Elem())) + return v, nil + }) + if !fv.IsValid() { + d.skip() + return err + } + } + + err := d.parseToValue(fv, f.typInfo) + if err != nil { + if typeError, ok := err.(*UnmarshalTypeError); ok { + typeError.StructFieldName = tInfo.nonPtrType.String() + "." + f.name + } + return err + } + + return nil +} + func (d *decoder) parseMapToStruct(v reflect.Value, tInfo *typeInfo) error { //nolint:gocyclo - structType := getDecodingStructType(tInfo.nonPtrType) - if structType.err != nil { - return structType.err + structType, structTypeErr := getDecodingStructType(tInfo.nonPtrType) + if structTypeErr != nil { + return structTypeErr } if structType.toArray { @@ -2654,14 +2725,12 @@ func (d *decoder) parseMapToStruct(v reflect.Value, tInfo *typeInfo) error { //n } } - var err, lastErr error - // Get CBOR map size _, _, val, indefiniteLength := d.getHeadWithIndefiniteLengthFlag() hasSize := !indefiniteLength - count := int(val) + count := int(val) //nolint:gosec - // Keeps track of matched struct fields + // Keep track of matched struct fields to detect duplicate map keys. var foundFldIdx []bool { const maxStackFields = 128 @@ -2675,99 +2744,80 @@ func (d *decoder) parseMapToStruct(v reflect.Value, tInfo *typeInfo) error { //n } } - // Keeps track of CBOR map keys to detect duplicate map key - keyCount := 0 - var mapKeys map[any]struct{} - - errOnUnknownField := (d.dm.extraReturnErrors & ExtraDecErrorUnknownField) > 0 + // Keep track of unmatched CBOR map keys to detect duplicate map keys. + var unmatchedMapKeys map[any]struct{} -MapEntryLoop: - for j := 0; (hasSize && j < count) || (!hasSize && !d.foundBreak()); j++ { - var f *field + var err error - // If duplicate field detection is enabled and the key at index j did not match any - // field, k will hold the map key. - var k any + caseInsensitive := d.dm.fieldNameMatching == FieldNameMatchingPreferCaseSensitive + for i := 0; (hasSize && i < count) || (!hasSize && !d.foundBreak()); i++ { t := d.nextCBORType() - if t == cborTypeTextString || (t == cborTypeByteString && d.dm.fieldNameByteString == FieldNameByteStringAllowed) { + + // Reclassify disallowed byte string keys so they fall to the default case. + // keyType is only used for branch control. + keyType := t + if t == cborTypeByteString && d.dm.fieldNameByteString != FieldNameByteStringAllowed { + keyType = 0xff + } + + switch keyType { + case cborTypeTextString, cborTypeByteString: var keyBytes []byte if t == cborTypeTextString { - keyBytes, lastErr = d.parseTextString() - if lastErr != nil { + var parseErr error + keyBytes, parseErr = d.parseTextString() + if parseErr != nil { if err == nil { - err = lastErr + err = parseErr } - d.skip() // skip value + d.skip() // Skip value continue } } else { // cborTypeByteString keyBytes, _ = d.parseByteString() } - // Check for exact match on field name. - if i, ok := structType.fieldIndicesByName[string(keyBytes)]; ok { - fld := structType.fields[i] + // Find matching struct field (exact match, then case-insensitive fallback). + if fldIdx, ok := findStructFieldByKey(structType, keyBytes, caseInsensitive); ok { + fld := structType.fields[fldIdx] - if !foundFldIdx[i] { - f = fld - foundFldIdx[i] = true - } else if d.dm.dupMapKey == DupMapKeyEnforcedAPF { - err = &DupMapKeyError{fld.name, j} - d.skip() // skip value - j++ - // skip the rest of the map - for ; (hasSize && j < count) || (!hasSize && !d.foundBreak()); j++ { - d.skip() - d.skip() + switch checkDupField(d.dm, foundFldIdx, fldIdx) { + case mapActionParseValueAndContinue: + if fieldErr := d.decodeToStructField(v, fld, tInfo); fieldErr != nil && err == nil { + err = fieldErr } - return err - } else { - // discard repeated match + continue + case mapActionSkipAllAndReturnError: + return d.skipMapForDupKey(string(keyBytes), i, count, hasSize) + case mapActionSkipValueAndContinue: d.skip() - continue MapEntryLoop + continue } } - // Find field with case-insensitive match - if f == nil && d.dm.fieldNameMatching == FieldNameMatchingPreferCaseSensitive { - keyLen := len(keyBytes) - keyString := string(keyBytes) - for i := 0; i < len(structType.fields); i++ { - fld := structType.fields[i] - if len(fld.name) == keyLen && strings.EqualFold(fld.name, keyString) { - if !foundFldIdx[i] { - f = fld - foundFldIdx[i] = true - } else if d.dm.dupMapKey == DupMapKeyEnforcedAPF { - err = &DupMapKeyError{keyString, j} - d.skip() // skip value - j++ - // skip the rest of the map - for ; (hasSize && j < count) || (!hasSize && !d.foundBreak()); j++ { - d.skip() - d.skip() - } - return err - } else { - // discard repeated match - d.skip() - continue MapEntryLoop - } - break - } - } + // No matching struct field found. + if unmatchedErr := handleUnmatchedMapKey(d, string(keyBytes), i, count, hasSize, &unmatchedMapKeys); unmatchedErr != nil { + return unmatchedErr } - if d.dm.dupMapKey == DupMapKeyEnforcedAPF && f == nil { - k = string(keyBytes) - } - } else if t <= cborTypeNegativeInt { // uint/int + case cborTypePositiveInt, cborTypeNegativeInt: var nameAsInt int64 if t == cborTypePositiveInt { _, _, val := d.getHead() - nameAsInt = int64(val) + if val > math.MaxInt64 { + if err == nil { + err = &UnmarshalTypeError{ + CBORType: t.String(), + GoType: reflect.TypeOf(int64(0)).String(), + errorMsg: strconv.FormatUint(val, 10) + " overflows Go's int64", + } + } + d.skip() // skip value + continue + } + nameAsInt = int64(val) //nolint:gosec } else { _, _, val := d.getHead() if val > math.MaxInt64 { @@ -2781,39 +2831,35 @@ MapEntryLoop: d.skip() // skip value continue } - nameAsInt = int64(-1) ^ int64(val) - } - - // Find field - for i := 0; i < len(structType.fields); i++ { - fld := structType.fields[i] - if fld.keyAsInt && fld.nameAsInt == nameAsInt { - if !foundFldIdx[i] { - f = fld - foundFldIdx[i] = true - } else if d.dm.dupMapKey == DupMapKeyEnforcedAPF { - err = &DupMapKeyError{nameAsInt, j} - d.skip() // skip value - j++ - // skip the rest of the map - for ; (hasSize && j < count) || (!hasSize && !d.foundBreak()); j++ { - d.skip() - d.skip() - } - return err - } else { - // discard repeated match - d.skip() - continue MapEntryLoop + nameAsInt = int64(-1) ^ int64(val) //nolint:gosec + } + + // Find field by integer key + if fldIdx, ok := structType.fieldIndicesByIntKey[nameAsInt]; ok { + fld := structType.fields[fldIdx] + + switch checkDupField(d.dm, foundFldIdx, fldIdx) { + case mapActionParseValueAndContinue: + if fieldErr := d.decodeToStructField(v, fld, tInfo); fieldErr != nil && err == nil { + err = fieldErr } - break + continue + case mapActionSkipAllAndReturnError: + return d.skipMapForDupKey(nameAsInt, i, count, hasSize) + case mapActionSkipValueAndContinue: + d.skip() + continue } } - if d.dm.dupMapKey == DupMapKeyEnforcedAPF && f == nil { - k = nameAsInt + // No matching struct field found. + if unmatchedErr := handleUnmatchedMapKey(d, nameAsInt, i, count, hasSize, &unmatchedMapKeys); unmatchedErr != nil { + return unmatchedErr } - } else { + + default: + // CBOR map keys that can't be matched to any struct field. + if err == nil { err = &UnmarshalTypeError{ CBORType: t.String(), @@ -2821,97 +2867,31 @@ MapEntryLoop: errorMsg: "map key is of type " + t.String() + " and cannot be used to match struct field name", } } + + var otherKey any if d.dm.dupMapKey == DupMapKeyEnforcedAPF { // parse key - k, lastErr = d.parse(true) - if lastErr != nil { + var parseErr error + otherKey, parseErr = d.parse(true) + if parseErr != nil { d.skip() // skip value continue } // Detect if CBOR map key can be used as Go map key. - if !isHashableValue(reflect.ValueOf(k)) { + if !isHashableValue(reflect.ValueOf(otherKey)) { d.skip() // skip value continue } } else { d.skip() // skip key } - } - - if f == nil { - if errOnUnknownField { - err = &UnknownFieldError{j} - d.skip() // Skip value - j++ - // skip the rest of the map - for ; (hasSize && j < count) || (!hasSize && !d.foundBreak()); j++ { - d.skip() - d.skip() - } - return err - } - - // Two map keys that match the same struct field are immediately considered - // duplicates. This check detects duplicates between two map keys that do - // not match a struct field. If unknown field errors are enabled, then this - // check is never reached. - if d.dm.dupMapKey == DupMapKeyEnforcedAPF { - if mapKeys == nil { - mapKeys = make(map[any]struct{}, 1) - } - mapKeys[k] = struct{}{} - newKeyCount := len(mapKeys) - if newKeyCount == keyCount { - err = &DupMapKeyError{k, j} - d.skip() // skip value - j++ - // skip the rest of the map - for ; (hasSize && j < count) || (!hasSize && !d.foundBreak()); j++ { - d.skip() - d.skip() - } - return err - } - keyCount = newKeyCount - } - - d.skip() // Skip value - continue - } - - // Get field value by index - var fv reflect.Value - if len(f.idx) == 1 { - fv = v.Field(f.idx[0]) - } else { - fv, lastErr = getFieldValue(v, f.idx, func(v reflect.Value) (reflect.Value, error) { - // Return a new value for embedded field null pointer to point to, or return error. - if !v.CanSet() { - return reflect.Value{}, errors.New("cbor: cannot set embedded pointer to unexported struct: " + v.Type().String()) - } - v.Set(reflect.New(v.Type().Elem())) - return v, nil - }) - if lastErr != nil && err == nil { - err = lastErr - } - if !fv.IsValid() { - d.skip() - continue - } - } - if lastErr = d.parseToValue(fv, f.typInfo); lastErr != nil { - if err == nil { - if typeError, ok := lastErr.(*UnmarshalTypeError); ok { - typeError.StructFieldName = tInfo.nonPtrType.String() + "." + f.name - err = typeError - } else { - err = lastErr - } + if unmatchedErr := handleUnmatchedMapKey(d, otherKey, i, count, hasSize, &unmatchedMapKeys); unmatchedErr != nil { + return unmatchedErr } } } + return err } @@ -2958,15 +2938,15 @@ func (d *decoder) skip() { switch t { case cborTypeByteString, cborTypeTextString: - d.off += int(val) + d.off += int(val) //nolint:gosec case cborTypeArray: - for i := 0; i < int(val); i++ { + for i := 0; i < int(val); i++ { //nolint:gosec d.skip() } case cborTypeMap: - for i := 0; i < int(val)*2; i++ { + for i := 0; i < int(val)*2; i++ { //nolint:gosec d.skip() } diff --git a/vendor/github.com/fxamacker/cbor/v2/decode_map_utils.go b/vendor/github.com/fxamacker/cbor/v2/decode_map_utils.go new file mode 100644 index 0000000000..3c8c423ad1 --- /dev/null +++ b/vendor/github.com/fxamacker/cbor/v2/decode_map_utils.go @@ -0,0 +1,98 @@ +// Copyright (c) Faye Amacker. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +package cbor + +import "strings" + +// mapAction represents the next action during decoding a CBOR map to a Go struct. +type mapAction int + +const ( + mapActionParseValueAndContinue mapAction = iota // The caller should process the map value. + mapActionSkipValueAndContinue // The caller should skip the map value. + mapActionSkipAllAndReturnError // The caller should skip the rest of the map and return an error. +) + +// checkDupField checks if a struct field at index i has already been matched and returns the next action. +// If not matched, it marks the field as matched and returns mapActionParseValueAndContinue. +// If matched and DupMapKeyEnforcedAPF is specified in the given dm, it returns mapActionSkipAllAndReturnError. +// If matched and DupMapKeyEnforcedAPF is not specified in the given dm, it returns mapActionSkipValueAndContinue. +func checkDupField(dm *decMode, foundFldIdx []bool, i int) mapAction { + if !foundFldIdx[i] { + foundFldIdx[i] = true + return mapActionParseValueAndContinue + } + if dm.dupMapKey == DupMapKeyEnforcedAPF { + return mapActionSkipAllAndReturnError + } + return mapActionSkipValueAndContinue +} + +// findStructFieldByKey finds a struct field matching keyBytes by name. +// It tries an exact match first. If no exact match is found and +// caseInsensitive is true, it falls back to a case-insensitive search. +// findStructFieldByKey returns the field index and true, or -1 and false. +func findStructFieldByKey( + structType *decodingStructType, + keyBytes []byte, + caseInsensitive bool, +) (int, bool) { + if fldIdx, ok := structType.fieldIndicesByName[string(keyBytes)]; ok { + return fldIdx, true + } + if caseInsensitive { + return findFieldCaseInsensitive(structType.fields, string(keyBytes)) + } + return -1, false +} + +// findFieldCaseInsensitive returns the index of the first field whose name +// case-insensitively matches key, or -1 and false if no field matches. +func findFieldCaseInsensitive(flds decodingFields, key string) (int, bool) { + keyLen := len(key) + for i, f := range flds { + if f.keyAsInt { + continue + } + if len(f.name) == keyLen && strings.EqualFold(f.name, key) { + return i, true + } + } + return -1, false +} + +// handleUnmatchedMapKey handles a map entry whose key does not match any struct +// field. It can return UnknownFieldError or DupMapKeyError. +// handleUnmatchedMapKey consumes the CBOR value, so the caller doesn't need to skip any values. +// If an error is returned, the caller should abort parsing the map and return the error. +// If no error is returned, the caller should continue to process the next map pair. +func handleUnmatchedMapKey( + d *decoder, + key any, + i int, + count int, + hasSize bool, + // *map[any]struct{} is used here because we use lazy initialization for uks + uks *map[any]struct{}, //nolint:gocritic +) error { + errOnUnknownField := (d.dm.extraReturnErrors & ExtraDecErrorUnknownField) > 0 + + if errOnUnknownField { + return d.skipMapForUnknownField(i, count, hasSize) + } + + if d.dm.dupMapKey == DupMapKeyEnforcedAPF { + if *uks == nil { + *uks = make(map[any]struct{}) + } + if _, dup := (*uks)[key]; dup { + return d.skipMapForDupKey(key, i, count, hasSize) + } + (*uks)[key] = struct{}{} + } + + // Skip value. + d.skip() + return nil +} diff --git a/vendor/github.com/fxamacker/cbor/v2/diagnose.go b/vendor/github.com/fxamacker/cbor/v2/diagnose.go index 44afb86608..42a67ad11f 100644 --- a/vendor/github.com/fxamacker/cbor/v2/diagnose.go +++ b/vendor/github.com/fxamacker/cbor/v2/diagnose.go @@ -51,11 +51,8 @@ const ( maxByteStringEncoding ) -func (bse ByteStringEncoding) valid() error { - if bse >= maxByteStringEncoding { - return errors.New("cbor: invalid ByteStringEncoding " + strconv.Itoa(int(bse))) - } - return nil +func (bse ByteStringEncoding) valid() bool { + return bse < maxByteStringEncoding } // DiagOptions specifies Diag options. @@ -104,8 +101,8 @@ func (opts DiagOptions) DiagMode() (DiagMode, error) { } func (opts DiagOptions) diagMode() (*diagMode, error) { - if err := opts.ByteStringEncoding.valid(); err != nil { - return nil, err + if !opts.ByteStringEncoding.valid() { + return nil, errors.New("cbor: invalid ByteStringEncoding " + strconv.Itoa(int(opts.ByteStringEncoding))) } decMode, err := DecOptions{ @@ -360,7 +357,7 @@ func (di *diagnose) item() error { //nolint:gocyclo case cborTypeArray: _, _, val := di.d.getHead() - count := int(val) + count := int(val) //nolint:gosec di.w.WriteByte('[') for i := 0; i < count; i++ { @@ -376,7 +373,7 @@ func (di *diagnose) item() error { //nolint:gocyclo case cborTypeMap: _, _, val := di.d.getHead() - count := int(val) + count := int(val) //nolint:gosec di.w.WriteByte('{') for i := 0; i < count; i++ { @@ -477,8 +474,8 @@ func (di *diagnose) item() error { //nolint:gocyclo func (di *diagnose) writeU16(val rune) { di.w.WriteString("\\u") var in [2]byte - in[0] = byte(val >> 8) - in[1] = byte(val) + in[0] = byte(val >> 8) //nolint:gosec + in[1] = byte(val) //nolint:gosec sz := hex.EncodedLen(len(in)) di.w.Grow(sz) dst := di.w.Bytes()[di.w.Len() : di.w.Len()+sz] @@ -608,7 +605,7 @@ func (di *diagnose) encodeTextString(val string, quote byte) error { c, size := utf8.DecodeRuneInString(val[i:]) switch { - case c == utf8.RuneError: + case c == utf8.RuneError && size == 1: return &SemanticError{"cbor: invalid UTF-8 string"} case c < utf16SurrSelf: @@ -631,7 +628,7 @@ func (di *diagnose) encodeFloat(ai byte, val uint64) error { f64 := float64(0) switch ai { case additionalInformationAsFloat16: - f16 := float16.Frombits(uint16(val)) + f16 := float16.Frombits(uint16(val)) //nolint:gosec switch { case f16.IsNaN(): di.w.WriteString("NaN") @@ -647,7 +644,7 @@ func (di *diagnose) encodeFloat(ai byte, val uint64) error { } case additionalInformationAsFloat32: - f32 := math.Float32frombits(uint32(val)) + f32 := math.Float32frombits(uint32(val)) //nolint:gosec switch { case f32 != f32: di.w.WriteString("NaN") diff --git a/vendor/github.com/fxamacker/cbor/v2/doc.go b/vendor/github.com/fxamacker/cbor/v2/doc.go index c758b73748..e4c1d27b8d 100644 --- a/vendor/github.com/fxamacker/cbor/v2/doc.go +++ b/vendor/github.com/fxamacker/cbor/v2/doc.go @@ -56,20 +56,30 @@ modes won't accidentally change at runtime after they're created. Modes are intended to be reused and are safe for concurrent use. -EncMode and DecMode Interfaces +EncMode, UserBufferEncMode, and DecMode Interfaces // EncMode interface uses immutable options and is safe for concurrent use. type EncMode interface { - Marshal(v interface{}) ([]byte, error) + Marshal(v any) ([]byte, error) NewEncoder(w io.Writer) *Encoder - EncOptions() EncOptions // returns copy of options + EncOptions() EncOptions + } + + // UserBufferEncMode extends EncMode with MarshalToBuffer, which supports + // user specified buffer rather than encoding into the built-in buffer pool. + type UserBufferEncMode interface { + EncMode + MarshalToBuffer(v any, buf *bytes.Buffer) error } // DecMode interface uses immutable options and is safe for concurrent use. type DecMode interface { - Unmarshal(data []byte, v interface{}) error + Unmarshal(data []byte, v any) error + UnmarshalFirst(data []byte, v any) (rest []byte, err error) + Valid(data []byte) error // Deprecated: use Wellformed instead. + Wellformed(data []byte) error NewDecoder(r io.Reader) *Decoder - DecOptions() DecOptions // returns copy of options + DecOptions() DecOptions } Using Default Encoding Mode diff --git a/vendor/github.com/fxamacker/cbor/v2/encode.go b/vendor/github.com/fxamacker/cbor/v2/encode.go index c550617c38..e65a29d8a6 100644 --- a/vendor/github.com/fxamacker/cbor/v2/encode.go +++ b/vendor/github.com/fxamacker/cbor/v2/encode.go @@ -30,7 +30,7 @@ import ( // If value implements the Marshaler interface, Marshal calls its // MarshalCBOR method. // -// If value implements encoding.BinaryMarshaler, Marhsal calls its +// If value implements encoding.BinaryMarshaler, Marshal calls its // MarshalBinary method and encode it as CBOR byte string. // // Boolean values encode as CBOR booleans (type 7). @@ -343,7 +343,7 @@ const ( // non-UTC timezone then a "localtime - UTC" numeric offset will be included as specified in RFC3339. // NOTE: User applications can avoid including the RFC3339 numeric offset by: // - providing a time.Time value set to UTC, or - // - using the TimeUnix, TimeUnixMicro, or TimeUnixDynamic option instead of TimeRFC3339. + // - using the TimeUnix, TimeUnixMicro, TimeUnixDynamic, or TimeRFC3339NanoUTC option. TimeRFC3339 // TimeRFC3339Nano causes time.Time to encode to a CBOR time (tag 0) with a text string content @@ -351,9 +351,13 @@ const ( // non-UTC timezone then a "localtime - UTC" numeric offset will be included as specified in RFC3339. // NOTE: User applications can avoid including the RFC3339 numeric offset by: // - providing a time.Time value set to UTC, or - // - using the TimeUnix, TimeUnixMicro, or TimeUnixDynamic option instead of TimeRFC3339Nano. + // - using the TimeUnix, TimeUnixMicro, TimeUnixDynamic, or TimeRFC3339NanoUTC option. TimeRFC3339Nano + // TimeRFC3339NanoUTC causes time.Time to encode to a CBOR time (tag 0) with a text string content + // representing UTC time using nanosecond precision in RFC3339 format. + TimeRFC3339NanoUTC + maxTimeMode ) @@ -436,7 +440,7 @@ const ( // FieldNameToTextString encodes struct fields to CBOR text string (major type 3). FieldNameToTextString FieldNameMode = iota - // FieldNameToTextString encodes struct fields to CBOR byte string (major type 2). + // FieldNameToByteString encodes struct fields to CBOR byte string (major type 2). FieldNameToByteString maxFieldNameMode @@ -567,7 +571,7 @@ type EncOptions struct { // RFC3339 format gets tag number 0, and numeric epoch time tag number 1. TimeTag EncTagMode - // IndefLength specifies whether to allow indefinite length CBOR items. + // IndefLength specifies whether to allow indefinite-length CBOR items. IndefLength IndefLengthMode // NilContainers specifies how to encode nil slices and maps. @@ -1132,10 +1136,11 @@ func encodeFloat(e *bytes.Buffer, em *encMode, v reflect.Value) error { if fopt == ShortestFloat16 { var f16 float16.Float16 p := float16.PrecisionFromfloat32(f32) - if p == float16.PrecisionExact { + switch p { + case float16.PrecisionExact: // Roundtrip float32->float16->float32 test isn't needed. f16 = float16.Fromfloat32(f32) - } else if p == float16.PrecisionUnknown { + case float16.PrecisionUnknown: // Try roundtrip float32->float16->float32 to determine if float32 can fit into float16. f16 = float16.Fromfloat32(f32) if f16.Float32() == f32 { @@ -1293,10 +1298,10 @@ func encodeByteString(e *bytes.Buffer, em *encMode, v reflect.Value) error { if slen == 0 { return e.WriteByte(byte(cborTypeByteString)) } - encodeHead(e, byte(cborTypeByteString), uint64(slen)) + encodeHead(e, byte(cborTypeByteString), uint64(slen)) //nolint:gosec if vk == reflect.Array { for i := 0; i < slen; i++ { - e.WriteByte(byte(v.Index(i).Uint())) + e.WriteByte(byte(v.Index(i).Uint())) //nolint:gosec } return nil } @@ -1333,7 +1338,7 @@ func (ae arrayEncodeFunc) encode(e *bytes.Buffer, em *encMode, v reflect.Value) if alen == 0 { return e.WriteByte(byte(cborTypeArray)) } - encodeHead(e, byte(cborTypeArray), uint64(alen)) + encodeHead(e, byte(cborTypeArray), uint64(alen)) //nolint:gosec for i := 0; i < alen; i++ { if err := ae.f(e, em, v.Index(i)); err != nil { return err @@ -1364,7 +1369,7 @@ func (me mapEncodeFunc) encode(e *bytes.Buffer, em *encMode, v reflect.Value) er return e.WriteByte(byte(cborTypeMap)) } - encodeHead(e, byte(cborTypeMap), uint64(mlen)) + encodeHead(e, byte(cborTypeMap), uint64(mlen)) //nolint:gosec if em.sort == SortNone || em.sort == SortFastShuffle || mlen <= 1 { return me.e(e, em, v, nil) } @@ -1427,7 +1432,7 @@ func (x *bytewiseKeyValueSorter) Swap(i, j int) { func (x *bytewiseKeyValueSorter) Less(i, j int) bool { kvi, kvj := x.kvs[i], x.kvs[j] - return bytes.Compare(x.data[kvi.offset:kvi.valueOffset], x.data[kvj.offset:kvj.valueOffset]) <= 0 + return bytes.Compare(x.data[kvi.offset:kvi.valueOffset], x.data[kvj.offset:kvj.valueOffset]) < 0 } type lengthFirstKeyValueSorter struct { @@ -1448,7 +1453,7 @@ func (x *lengthFirstKeyValueSorter) Less(i, j int) bool { if keyLengthDifference := (kvi.valueOffset - kvi.offset) - (kvj.valueOffset - kvj.offset); keyLengthDifference != 0 { return keyLengthDifference < 0 } - return bytes.Compare(x.data[kvi.offset:kvi.valueOffset], x.data[kvj.offset:kvj.valueOffset]) <= 0 + return bytes.Compare(x.data[kvi.offset:kvi.valueOffset], x.data[kvj.offset:kvj.valueOffset]) < 0 } var keyValuePool = sync.Pool{} @@ -1535,8 +1540,8 @@ func encodeStruct(e *bytes.Buffer, em *encMode, v reflect.Value) (err error) { // Head is rewritten later if actual encoded field count is different from struct field count. encodedHeadLen := encodeHead(e, byte(cborTypeMap), uint64(len(flds))) - kvbegin := e.Len() - kvcount := 0 + kvBeginOffset := e.Len() + kvCount := 0 for offset := 0; offset < len(flds); offset++ { f := flds[(start+offset)%len(flds)] @@ -1582,10 +1587,10 @@ func encodeStruct(e *bytes.Buffer, em *encMode, v reflect.Value) (err error) { return err } - kvcount++ + kvCount++ } - if len(flds) == kvcount { + if len(flds) == kvCount { // Encoded element count in head is the same as actual element count. return nil } @@ -1593,8 +1598,8 @@ func encodeStruct(e *bytes.Buffer, em *encMode, v reflect.Value) (err error) { // Overwrite the bytes that were reserved for the head before encoding the map entries. var actualHeadLen int { - headbuf := *bytes.NewBuffer(e.Bytes()[kvbegin-encodedHeadLen : kvbegin-encodedHeadLen : kvbegin]) - actualHeadLen = encodeHead(&headbuf, byte(cborTypeMap), uint64(kvcount)) + headbuf := *bytes.NewBuffer(e.Bytes()[kvBeginOffset-encodedHeadLen : kvBeginOffset-encodedHeadLen : kvBeginOffset]) + actualHeadLen = encodeHead(&headbuf, byte(cborTypeMap), uint64(kvCount)) } if actualHeadLen == encodedHeadLen { @@ -1607,8 +1612,8 @@ func encodeStruct(e *bytes.Buffer, em *encMode, v reflect.Value) (err error) { // encoded. The encoded entries are offset to the right by the number of excess reserved // bytes. Shift the entries left to remove the gap. excessReservedBytes := encodedHeadLen - actualHeadLen - dst := e.Bytes()[kvbegin-excessReservedBytes : e.Len()-excessReservedBytes] - src := e.Bytes()[kvbegin:e.Len()] + dst := e.Bytes()[kvBeginOffset-excessReservedBytes : e.Len()-excessReservedBytes] + src := e.Bytes()[kvBeginOffset:e.Len()] copy(dst, src) // After shifting, the excess bytes are at the end of the output buffer and they are @@ -1633,7 +1638,7 @@ func encodeTime(e *bytes.Buffer, em *encMode, v reflect.Value) error { } if em.timeTag == EncTagRequired { tagNumber := 1 - if em.time == TimeRFC3339 || em.time == TimeRFC3339Nano { + if em.time == TimeRFC3339 || em.time == TimeRFC3339Nano || em.time == TimeRFC3339NanoUTC { tagNumber = 0 } encodeHead(e, byte(cborTypeTag), uint64(tagNumber)) @@ -1650,7 +1655,7 @@ func encodeTime(e *bytes.Buffer, em *encMode, v reflect.Value) error { case TimeUnixDynamic: t = t.UTC().Round(time.Microsecond) - secs, nsecs := t.Unix(), uint64(t.Nanosecond()) + secs, nsecs := t.Unix(), uint64(t.Nanosecond()) //nolint:gosec if nsecs == 0 { return encodeInt(e, em, reflect.ValueOf(secs)) } @@ -1661,6 +1666,10 @@ func encodeTime(e *bytes.Buffer, em *encMode, v reflect.Value) error { s := t.Format(time.RFC3339) return encodeString(e, em, reflect.ValueOf(s)) + case TimeRFC3339NanoUTC: + s := t.UTC().Format(time.RFC3339Nano) + return encodeString(e, em, reflect.ValueOf(s)) + default: // TimeRFC3339Nano s := t.Format(time.RFC3339Nano) return encodeString(e, em, reflect.ValueOf(s)) diff --git a/vendor/github.com/fxamacker/cbor/v2/simplevalue.go b/vendor/github.com/fxamacker/cbor/v2/simplevalue.go index 30f72814f6..9912e855c2 100644 --- a/vendor/github.com/fxamacker/cbor/v2/simplevalue.go +++ b/vendor/github.com/fxamacker/cbor/v2/simplevalue.go @@ -93,6 +93,6 @@ func (sv *SimpleValue) unmarshalCBOR(data []byte) error { // It is safe to cast val to uint8 here because // - data is already verified to be well-formed CBOR simple value and // - val is <= math.MaxUint8. - *sv = SimpleValue(val) + *sv = SimpleValue(val) //nolint:gosec return nil } diff --git a/vendor/github.com/fxamacker/cbor/v2/stream.go b/vendor/github.com/fxamacker/cbor/v2/stream.go index 7ac6d7d671..282b3f7ddc 100644 --- a/vendor/github.com/fxamacker/cbor/v2/stream.go +++ b/vendor/github.com/fxamacker/cbor/v2/stream.go @@ -6,6 +6,7 @@ package cbor import ( "bytes" "errors" + "fmt" "io" "reflect" ) @@ -157,11 +158,32 @@ func (dec *Decoder) overwriteBuf(newBuf []byte) { dec.off = 0 } +// indefDataItem tracks open indefinite-length data item during encoding. +// typ is the CBOR type of the indefinite-length data item. +// count is the number of items written so far. +// For indefinite-length maps, count must be even (key/value pairs) when +// the container is closed. +type indefDataItem struct { + typ cborType + count int +} + +// IndefiniteLengthMapOddItemCountError indicates that EndIndefinite was +// called on an open indefinite-length map with an odd number of items. +type IndefiniteLengthMapOddItemCountError struct { + count int +} + +func (e *IndefiniteLengthMapOddItemCountError) Error() string { + return fmt.Sprintf("cbor: cannot end indefinite-length map with %d item(s)", e.count) +} + // Encoder writes CBOR values to io.Writer. type Encoder struct { - w io.Writer - em *encMode - indefTypes []cborType + w io.Writer + em *encMode + indefs []indefDataItem + scratch [1]byte // reused for single-byte writes (indefinite-length head and break code) } // NewEncoder returns a new encoder that writes to w using the default encoding options. @@ -171,89 +193,166 @@ func NewEncoder(w io.Writer) *Encoder { // Encode writes the CBOR encoding of v. func (enc *Encoder) Encode(v any) error { - if len(enc.indefTypes) > 0 && v != nil { - indefType := enc.indefTypes[len(enc.indefTypes)-1] - if indefType == cborTypeTextString { - k := reflect.TypeOf(v).Kind() - if k != reflect.String { - return errors.New("cbor: cannot encode item type " + k.String() + " for indefinite-length text string") - } - } else if indefType == cborTypeByteString { - t := reflect.TypeOf(v) - k := t.Kind() - if (k != reflect.Array && k != reflect.Slice) || t.Elem().Kind() != reflect.Uint8 { - return errors.New("cbor: cannot encode item type " + k.String() + " for indefinite-length byte string") - } + if len(enc.indefs) > 0 { + err := validateIndefiniteLengthChunkByType(enc.indefs[len(enc.indefs)-1].typ, v) + if err != nil { + return err } } buf := getEncodeBuffer() err := encode(buf, enc.em, reflect.ValueOf(v)) + + // Validate the encoded chunk against the indefinite-length data item using a byte-based check. + // This reliably detects chunks from cbor.Marshaler, registered tags, StringToByteString, etc., + // which may produce a chunk inconsistent with the parent's major type. + // Applies only to indefinite-length byte/text string parents (RFC 8949 Section 3.2.3). + if err == nil && len(enc.indefs) > 0 { + err = validateIndefiniteLengthChunkByData(enc.indefs[len(enc.indefs)-1].typ, buf.Bytes(), v) + } + if err == nil { _, err = enc.w.Write(buf.Bytes()) } putEncodeBuffer(buf) - return err + + if err != nil { + return err + } + + if len(enc.indefs) > 0 { + enc.indefs[len(enc.indefs)-1].count++ + } + + return nil } -// StartIndefiniteByteString starts byte string encoding of indefinite length. +// StartIndefiniteByteString starts indefinite-length byte string encoding. // Subsequent calls of (*Encoder).Encode() encodes definite length byte strings // ("chunks") as one contiguous string until EndIndefinite is called. func (enc *Encoder) StartIndefiniteByteString() error { return enc.startIndefinite(cborTypeByteString) } -// StartIndefiniteTextString starts text string encoding of indefinite length. +// StartIndefiniteTextString starts indefinite-length text string encoding. // Subsequent calls of (*Encoder).Encode() encodes definite length text strings // ("chunks") as one contiguous string until EndIndefinite is called. func (enc *Encoder) StartIndefiniteTextString() error { return enc.startIndefinite(cborTypeTextString) } -// StartIndefiniteArray starts array encoding of indefinite length. +// StartIndefiniteArray starts indefinite-length array encoding. // Subsequent calls of (*Encoder).Encode() encodes elements of the array // until EndIndefinite is called. func (enc *Encoder) StartIndefiniteArray() error { return enc.startIndefinite(cborTypeArray) } -// StartIndefiniteMap starts array encoding of indefinite length. +// StartIndefiniteMap starts indefinite-length map encoding. // Subsequent calls of (*Encoder).Encode() encodes elements of the map // until EndIndefinite is called. func (enc *Encoder) StartIndefiniteMap() error { return enc.startIndefinite(cborTypeMap) } -// EndIndefinite closes last opened indefinite length value. +// EndIndefinite closes last opened indefinite-length value. +// It returns *IndefiniteLengthMapOddItemCountError without writing the +// "break" code if the open indefinite-length map has an odd number of +// items; the encoder state is unchanged so the caller can write the +// missing value and retry. func (enc *Encoder) EndIndefinite() error { - if len(enc.indefTypes) == 0 { + if len(enc.indefs) == 0 { return errors.New("cbor: cannot encode \"break\" code outside indefinite length values") } - _, err := enc.w.Write([]byte{cborBreakFlag}) - if err == nil { - enc.indefTypes = enc.indefTypes[:len(enc.indefTypes)-1] + + // Verify that indefinite-length map has even number of elements + top := enc.indefs[len(enc.indefs)-1] + if top.typ == cborTypeMap && top.count%2 != 0 { + return &IndefiniteLengthMapOddItemCountError{count: top.count} } - return err -} -var cborIndefHeader = map[cborType][]byte{ - cborTypeByteString: {cborByteStringWithIndefiniteLengthHead}, - cborTypeTextString: {cborTextStringWithIndefiniteLengthHead}, - cborTypeArray: {cborArrayWithIndefiniteLengthHead}, - cborTypeMap: {cborMapWithIndefiniteLengthHead}, + // Write break code + enc.scratch[0] = cborBreakFlag + _, err := enc.w.Write(enc.scratch[:]) + if err != nil { + return err + } + + enc.indefs = enc.indefs[:len(enc.indefs)-1] + + // Increment parent container's item count because the child + // (indefinite-length data item) is fully written to the stream. + if len(enc.indefs) > 0 { + enc.indefs[len(enc.indefs)-1].count++ + } + + return nil } func (enc *Encoder) startIndefinite(typ cborType) error { if enc.em.indefLength == IndefLengthForbidden { return &IndefiniteLengthError{typ} } - _, err := enc.w.Write(cborIndefHeader[typ]) - if err == nil { - enc.indefTypes = append(enc.indefTypes, typ) + + // Verify that new indefinite-length data item is not a chunk in indefinite-length byte/text string. + if len(enc.indefs) > 0 { + parent := enc.indefs[len(enc.indefs)-1].typ + if parent == cborTypeByteString || parent == cborTypeTextString { + return errors.New("cbor: cannot encode indefinite-length " + typ.String() + + " as chunk of indefinite-length " + parent.String()) + } } - return err + + // Write indefinite-length head. + enc.scratch[0] = byte(typ) | additionalInformationAsIndefiniteLengthFlag + _, err := enc.w.Write(enc.scratch[:]) + if err != nil { + return err + } + + enc.indefs = append(enc.indefs, indefDataItem{typ: typ}) + return nil +} + +// validateIndefiniteLengthChunkByType rejects chunks based solely on their Go type. +func validateIndefiniteLengthChunkByType(indefiniteLengthCborType cborType, v any) error { + if indefiniteLengthCborType != cborTypeByteString && + indefiniteLengthCborType != cborTypeTextString { + return nil + } + if v == nil { + return errors.New("cbor: cannot encode nil for indefinite-length " + indefiniteLengthCborType.String()) + } + return nil +} + +// validateIndefiniteLengthChunkByData checks that chunk is a definite-length +// CBOR data item with a matching major type. +// No-op for indefinite-length array/map, where any data item is valid. +func validateIndefiniteLengthChunkByData(indefiniteLengthCborType cborType, chunk []byte, v any) error { + if indefiniteLengthCborType != cborTypeByteString && + indefiniteLengthCborType != cborTypeTextString { + return nil + } + + if len(chunk) == 0 { + return errors.New("cbor: cannot encode item type " + reflect.TypeOf(v).Kind().String() + + " for indefinite-length " + indefiniteLengthCborType.String()) + } + + t, ai := parseInitialByte(chunk[0]) + if t != indefiniteLengthCborType { + return errors.New("cbor: cannot encode item type " + reflect.TypeOf(v).Kind().String() + + " for indefinite-length " + indefiniteLengthCborType.String()) + } + + if ai == additionalInformationAsIndefiniteLengthFlag { + return errors.New("cbor: cannot encode indefinite-length " + indefiniteLengthCborType.String() + + " as chunk of indefinite-length " + indefiniteLengthCborType.String()) + } + return nil } // RawMessage is a raw encoded CBOR value. @@ -262,7 +361,9 @@ type RawMessage []byte // MarshalCBOR returns m or CBOR nil if m is nil. func (m RawMessage) MarshalCBOR() ([]byte, error) { if len(m) == 0 { - return cborNil, nil + b := make([]byte, len(cborNil)) + copy(b, cborNil) + return b, nil } return m, nil } diff --git a/vendor/github.com/fxamacker/cbor/v2/structfields.go b/vendor/github.com/fxamacker/cbor/v2/structfields.go index cf0a922cd7..b2d71f2e9a 100644 --- a/vendor/github.com/fxamacker/cbor/v2/structfields.go +++ b/vendor/github.com/fxamacker/cbor/v2/structfields.go @@ -6,27 +6,43 @@ package cbor import ( "reflect" "sort" + "strconv" "strings" ) +// field holds shared struct field metadata returned by getFields(). type field struct { - name string - nameAsInt int64 // used to decoder to match field name with CBOR int + name string + nameAsInt int64 // used to match field name with CBOR int + idx []int + typ reflect.Type // used during cache building only + keyAsInt bool // used to encode/decode field name as int + tagged bool // used to choose dominant field (at the same level tagged fields dominate untagged fields) + omitEmpty bool // used to skip empty field + omitZero bool // used to skip zero field +} + +type fields []*field + +// encodingField extends field with encoding-specific data. +type encodingField struct { + field cborName []byte - cborNameByteString []byte // major type 2 name encoding iff cborName has major type 3 - idx []int - typ reflect.Type + cborNameByteString []byte // major type 2 name encoding if cborName has major type 3 ef encodeFunc ief isEmptyFunc izf isZeroFunc - typInfo *typeInfo // used to decoder to reuse type info - tagged bool // used to choose dominant field (at the same level tagged fields dominate untagged fields) - omitEmpty bool // used to skip empty field - omitZero bool // used to skip zero field - keyAsInt bool // used to encode/decode field name as int } -type fields []*field +type encodingFields []*encodingField + +// decodingField extends field with decoding-specific data. +type decodingField struct { + field + typInfo *typeInfo // used by decoder to reuse type info +} + +type decodingFields []*decodingField // indexFieldSorter sorts fields by field idx at each level, breaking ties with idx depth. type indexFieldSorter struct { @@ -48,7 +64,7 @@ func (x *indexFieldSorter) Less(i, j int) bool { return iIdx[k] < jIdx[k] } } - return len(iIdx) <= len(jIdx) + return len(iIdx) < len(jIdx) } // nameLevelAndTagFieldSorter sorts fields by field name, idx depth, and presence of tag. @@ -69,6 +85,10 @@ func (x *nameLevelAndTagFieldSorter) Less(i, j int) bool { if fi.name != fj.name { return fi.name < fj.name } + // Fields with the same name but different keyAsInt are in separate namespaces. + if fi.keyAsInt != fj.keyAsInt { + return fi.keyAsInt + } if len(fi.idx) != len(fj.idx) { return len(fi.idx) < len(fj.idx) } @@ -117,22 +137,37 @@ func getFields(t reflect.Type) (flds fields, structOptions string) { } } + // Normalize keyasint field names to their canonical integer string form. + // This ensures that "01", "+1", and "1" are treated as the same key + // during deduplication. + for _, f := range flds { + if f.keyAsInt { + nameAsInt, err := strconv.Atoi(f.name) + if err != nil { + continue // Leave invalid names for callers to report. + } + f.nameAsInt = int64(nameAsInt) + f.name = strconv.Itoa(nameAsInt) + } + } + sort.Sort(&nameLevelAndTagFieldSorter{flds}) // Keep visible fields. j := 0 // index of next unique field for i := 0; i < len(flds); { name := flds[i].name + keyAsInt := flds[i].keyAsInt if i == len(flds)-1 || // last field - name != flds[i+1].name || // field i has unique field name + name != flds[i+1].name || flds[i+1].keyAsInt != keyAsInt || // field i has unique (name, keyAsInt) len(flds[i].idx) < len(flds[i+1].idx) || // field i is at a less nested level than field i+1 (flds[i].tagged && !flds[i+1].tagged) { // field i is tagged while field i+1 is not flds[j] = flds[i] j++ } - // Skip fields with the same field name. - for i++; i < len(flds) && name == flds[i].name; i++ { //nolint:revive + // Skip fields with the same (name, keyAsInt). + for i++; i < len(flds) && name == flds[i].name && keyAsInt == flds[i].keyAsInt; i++ { //nolint:revive } } if j != len(flds) { diff --git a/vendor/github.com/fxamacker/cbor/v2/tag.go b/vendor/github.com/fxamacker/cbor/v2/tag.go index bd8b773f54..ee46e74213 100644 --- a/vendor/github.com/fxamacker/cbor/v2/tag.go +++ b/vendor/github.com/fxamacker/cbor/v2/tag.go @@ -155,6 +155,7 @@ type TagSet interface { Add(opts TagOptions, contentType reflect.Type, num uint64, nestedNum ...uint64) error // Remove removes given tag content type from TagSet. + // Remove is a no-op if contentType is nil. Remove(contentType reflect.Type) tagProvider @@ -245,7 +246,11 @@ func (t *syncTagSet) Add(opts TagOptions, contentType reflect.Type, num uint64, } // Remove removes given tag content type from TagSet. +// Remove is a no-op if contentType is nil. func (t *syncTagSet) Remove(contentType reflect.Type) { + if contentType == nil { + return + } for contentType.Kind() == reflect.Pointer { contentType = contentType.Elem() } diff --git a/vendor/github.com/fxamacker/cbor/v2/valid.go b/vendor/github.com/fxamacker/cbor/v2/valid.go index b40793b95e..850b95019c 100644 --- a/vendor/github.com/fxamacker/cbor/v2/valid.go +++ b/vendor/github.com/fxamacker/cbor/v2/valid.go @@ -54,7 +54,7 @@ func (e *MaxMapPairsError) Error() string { return "cbor: exceeded max number of key-value pairs " + strconv.Itoa(e.maxMapPairs) + " for CBOR map" } -// IndefiniteLengthError indicates found disallowed indefinite length items. +// IndefiniteLengthError indicates found disallowed indefinite-length items. type IndefiniteLengthError struct { t cborType } @@ -113,7 +113,7 @@ func (d *decoder) wellformedInternal(depth int, checkBuiltinTags bool) (int, err } return d.wellformedIndefiniteString(t, depth, checkBuiltinTags) } - valInt := int(val) + valInt := int(val) //nolint:gosec if valInt < 0 { // Detect integer overflow return 0, errors.New("cbor: " + t.String() + " length " + strconv.FormatUint(val, 10) + " is too large, causing integer overflow") @@ -136,7 +136,7 @@ func (d *decoder) wellformedInternal(depth int, checkBuiltinTags bool) (int, err return d.wellformedIndefiniteArrayOrMap(t, depth, checkBuiltinTags) } - valInt := int(val) + valInt := int(val) //nolint:gosec if valInt < 0 { // Detect integer overflow return 0, errors.New("cbor: " + t.String() + " length " + strconv.FormatUint(val, 10) + " is too large, it would cause integer overflow") @@ -212,7 +212,7 @@ func (d *decoder) wellformedInternal(depth int, checkBuiltinTags bool) (int, err return depth, nil } -// wellformedIndefiniteString checks indefinite length byte/text string's well-formedness and returns max depth and error. +// wellformedIndefiniteString checks indefinite-length byte/text string's well-formedness and returns max depth and error. func (d *decoder) wellformedIndefiniteString(t cborType, depth int, checkBuiltinTags bool) (int, error) { var err error for { @@ -223,7 +223,7 @@ func (d *decoder) wellformedIndefiniteString(t cborType, depth int, checkBuiltin d.off++ break } - // Peek ahead to get next type and indefinite length status. + // Peek ahead to get next type and indefinite-length status. nt, ai := parseInitialByte(d.data[d.off]) if t != nt { return 0, &SyntaxError{"cbor: wrong element type " + nt.String() + " for indefinite-length " + t.String()} @@ -238,7 +238,7 @@ func (d *decoder) wellformedIndefiniteString(t cborType, depth int, checkBuiltin return depth, nil } -// wellformedIndefiniteArrayOrMap checks indefinite length array/map's well-formedness and returns max depth and error. +// wellformedIndefiniteArrayOrMap checks indefinite-length array/map's well-formedness and returns max depth and error. func (d *decoder) wellformedIndefiniteArrayOrMap(t cborType, depth int, checkBuiltinTags bool) (int, error) { var err error maxDepth := depth @@ -326,7 +326,7 @@ func (d *decoder) wellformedHead() (t cborType, ai byte, val uint64, err error) val = uint64(binary.BigEndian.Uint16(d.data[d.off : d.off+argumentSize])) d.off += argumentSize if t == cborTypePrimitives { - if err := d.acceptableFloat(float64(float16.Frombits(uint16(val)).Float32())); err != nil { + if err := d.acceptableFloat(float64(float16.Frombits(uint16(val)).Float32())); err != nil { //nolint:gosec return 0, 0, 0, err } } @@ -341,7 +341,7 @@ func (d *decoder) wellformedHead() (t cborType, ai byte, val uint64, err error) val = uint64(binary.BigEndian.Uint32(d.data[d.off : d.off+argumentSize])) d.off += argumentSize if t == cborTypePrimitives { - if err := d.acceptableFloat(float64(math.Float32frombits(uint32(val)))); err != nil { + if err := d.acceptableFloat(float64(math.Float32frombits(uint32(val)))); err != nil { //nolint:gosec return 0, 0, 0, err } } @@ -379,12 +379,12 @@ func (d *decoder) wellformedHead() (t cborType, ai byte, val uint64, err error) func (d *decoder) acceptableFloat(f float64) error { switch { - case d.dm.nanDec == NaNDecodeForbidden && math.IsNaN(f): + case d.dm.nan == NaNDecodeForbidden && math.IsNaN(f): return &UnacceptableDataItemError{ CBORType: cborTypePrimitives.String(), Message: "floating-point NaN", } - case d.dm.infDec == InfDecodeForbidden && math.IsInf(f, 0): + case d.dm.inf == InfDecodeForbidden && math.IsInf(f, 0): return &UnacceptableDataItemError{ CBORType: cborTypePrimitives.String(), Message: "floating-point infinity", diff --git a/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/agent_types.go b/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/agent_types.go index 423d167487..f893eaa9a9 100644 --- a/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/agent_types.go +++ b/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/agent_types.go @@ -93,7 +93,7 @@ type AgentSpec struct { // Agent defines a cluster-scoped agent tier (e.g., "default", "smart", "fast"). // The cluster admin creates Agent resources to configure LLM infrastructure -// and runtime settings. Proposals reference agents by name per step. +// and runtime settings. AgenticRuns reference agents by name per step. // // Agent is cluster-scoped. The metadata.name serves as the tier identifier. // The "default" agent must exist; "smart" and "fast" are optional (the diff --git a/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/agenticolsconfig_types.go b/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/agenticolsconfig_types.go new file mode 100644 index 0000000000..1ef3aacecf --- /dev/null +++ b/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/agenticolsconfig_types.go @@ -0,0 +1,201 @@ +/* +Copyright 2026. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// Condition type for AgenticOLSConfig status. +const ( + // AgenticOLSConfigConditionSuspended tracks whether the system kill + // switch is active. True means all agentic runs have been emergency-stopped + // and the system is suspended; False means the admin has deactivated + // suspension and the system is accepting new agentic runs. + AgenticOLSConfigConditionSuspended = "Suspended" +) + +// AuditOTELConfig configures OpenTelemetry tracing export. +// +// +kubebuilder:validation:MinProperties=1 +type AuditOTELConfig struct { + // endpoint is the OTLP gRPC endpoint (e.g., "jaeger-otlp-grpc.observability.svc:4317"). + // When empty, no OTEL traces are exported (no-op tracer used). + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + // +optional + Endpoint string `json:"endpoint,omitempty"` + + // tlsMode controls TLS for the OTLP connection. + // "Secure" (default) requires TLS; "Insecure" disables TLS. + // +default="Secure" + // +optional + TLSMode AuditOTELTLSMode `json:"tlsMode,omitempty"` +} + +// AuditOTELTLSMode defines TLS behavior for the OTLP exporter. +// +kubebuilder:validation:Enum=Secure;Insecure +type AuditOTELTLSMode string + +const ( + AuditOTELTLSSecure AuditOTELTLSMode = "Secure" + AuditOTELTLSInsecure AuditOTELTLSMode = "Insecure" +) + +// AuditLoggingMode defines whether structured audit logging is enabled. +// +kubebuilder:validation:Enum=Enabled;Disabled +type AuditLoggingMode string + +const ( + AuditLoggingEnabled AuditLoggingMode = "Enabled" + AuditLoggingDisabled AuditLoggingMode = "Disabled" +) + +// AuditConfig configures compliance audit logging and tracing. +// Logging and OTEL tracing are independent controls. +// +// +kubebuilder:validation:MinProperties=1 +type AuditConfig struct { + // logging enables structured JSON audit events to stdout. + // Default: Enabled (when field is empty or config CR absent). + // +default="Enabled" + // +optional + Logging AuditLoggingMode `json:"logging,omitempty"` + + // otel configures OpenTelemetry tracing export. + // When nil or endpoint empty, uses no-op tracer (no export). + // +optional + OTEL AuditOTELConfig `json:"otel,omitzero"` +} + +// LoggingEnabled returns true when audit logging should be enabled. +// Defaults to true when config is nil or Logging field is empty. +func (c *AuditConfig) LoggingEnabled() bool { + if c == nil || c.Logging == "" { + return true + } + return c.Logging == AuditLoggingEnabled +} + +// OTELEndpoint returns the OTLP endpoint or empty string if not configured. +func (c *AuditConfig) OTELEndpoint() string { + if c == nil { + return "" + } + return c.OTEL.Endpoint +} + +// OTELInsecure returns whether to use insecure (no TLS) connections. +func (c *AuditConfig) OTELInsecure() bool { + if c == nil { + return false + } + return c.OTEL.TLSMode == AuditOTELTLSInsecure +} + +// AgenticOLSConfigSpec defines the desired state of AgenticOLSConfig. +// +// +kubebuilder:validation:MinProperties=1 +type AgenticOLSConfigSpec struct { + // suspended halts all agentic operations cluster-wide when set to true. + // All non-terminal agentic runs are immediately terminated with an + // EmergencyStopped condition. Setting back to false re-enables the + // system for new agentic runs only — EmergencyStopped runs remain + // terminal and must be recreated explicitly. + // +optional + // +default=false + Suspended bool `json:"suspended,omitempty"` //nolint:kubeapilinter // kill switch is genuinely binary; bool is the right type + + // audit configures compliance audit logging and OpenTelemetry tracing. + // When absent, logging defaults to enabled with no OTEL export. + // +optional + Audit AuditConfig `json:"audit,omitzero"` +} + +// AgenticOLSConfigStatus defines the observed state of AgenticOLSConfig. +// +// +kubebuilder:validation:MinProperties=1 +type AgenticOLSConfigStatus struct { + // conditions represent the latest available observations of the + // config's state. The "Suspended" condition tracks whether the + // kill switch is active and all agentic runs have been terminated. + // +listType=map + // +listMapKey=type + // +patchStrategy=merge + // +patchMergeKey=type + // +optional + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=8 + Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:resource:scope=Cluster +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="Suspended",type=boolean,JSONPath=`.spec.suspended` +// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` +// +kubebuilder:validation:XValidation:rule="self.metadata.name == 'cluster'",message="AgenticOLSConfig must be named 'cluster' (singleton)" + +// AgenticOLSConfig is a cluster-scoped singleton that controls system-wide +// agentic behavior. The cluster admin creates a single AgenticOLSConfig +// named "cluster". When spec.suspended is true, all non-terminal agentic runs +// are terminated and no new workflow steps are started. +// +// When no AgenticOLSConfig CR exists, the system behaves as if +// suspended is false — the CR is not required for normal operation. +// +// Example: +// +// apiVersion: agentic.openshift.io/v1alpha1 +// kind: AgenticOLSConfig +// metadata: +// name: cluster +// spec: +// suspended: false +// audit: +// logging: Enabled +// otel: +// endpoint: "jaeger-otlp-grpc.observability.svc:4317" +// tlsMode: Insecure +type AgenticOLSConfig struct { + metav1.TypeMeta `json:",inline"` + + // metadata is the standard object metadata. + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // spec defines the desired system configuration. + // +required + Spec AgenticOLSConfigSpec `json:"spec,omitzero"` + + // status defines the observed state of AgenticOLSConfig. + // +optional + Status AgenticOLSConfigStatus `json:"status,omitzero"` +} + +// +kubebuilder:object:root=true + +// AgenticOLSConfigList contains a list of AgenticOLSConfig. +type AgenticOLSConfigList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []AgenticOLSConfig `json:"items"` +} + +func init() { + SchemeBuilder.Register(&AgenticOLSConfig{}, &AgenticOLSConfigList{}) +} diff --git a/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/proposal_analysis_types.go b/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/agenticrun_analysis_types.go similarity index 88% rename from vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/proposal_analysis_types.go rename to vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/agenticrun_analysis_types.go index 61e63bc5e8..7c262bb6a1 100644 --- a/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/proposal_analysis_types.go +++ b/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/agenticrun_analysis_types.go @@ -38,7 +38,7 @@ const ( ) // RiskLevel is the agent's assessment of how risky a remediation is. -// Critical-risk proposals typically require explicit human review. +// Critical-risk remediations typically require explicit human review. // // - "Low" — Minimal risk; safe to apply automatically. // - "Medium" — Moderate risk; review recommended. @@ -58,7 +58,7 @@ const ( // DiagnosisResult contains the root cause analysis from the analysis agent. // This is populated by the agent during the analysis step and stored in // the AnalysisStepStatus as part of a RemediationOption. Users see this -// in the console UI when reviewing the proposal. +// in the console UI when reviewing the agentic run. type DiagnosisResult struct { // summary is a Markdown-formatted diagnosis summary explaining the // problem, its symptoms, and the agent's findings. Maximum 8192 characters. @@ -81,18 +81,27 @@ type DiagnosisResult struct { } // ProposedAction describes a single discrete action the analysis agent -// recommends as part of its remediation plan. Actions are displayed to -// the user after analysis for review before approval. +// recommends as part of its remediation plan. Each action contains an +// exact executable bash command and is displayed to the user after +// analysis for review before approval. type ProposedAction struct { - // type is the action category (e.g., "patch", "scale", "restart", - // "create", "delete", "rollout"). Free-form string to allow agents - // to express domain-specific action types. Must be 1-256 characters. + // command is the exact executable bash command using kubectl or oc + // (e.g., "kubectl set image deployment/foo container=registry/foo:v1.3 -n production"). + // Must be a concrete command that can be copy-pasted and run. + // Maximum 4096 characters. + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=4096 + Command string `json:"command,omitempty"` + // type is the action phase category: "pre-check", "mutation", "wait", + // or "post-check". Free-form string to allow agents to express + // domain-specific action types. Must be 1-256 characters. // +required // +kubebuilder:validation:MinLength=1 // +kubebuilder:validation:MaxLength=256 Type string `json:"type,omitempty"` - // description is a Markdown-formatted explanation of what this action - // will do (e.g., "Increase memory limit from 256Mi to 512Mi"). + // description is a Markdown-formatted explanation of what this command + // does and why (e.g., "Increase memory limit from 256Mi to 512Mi"). // Maximum 4096 characters. // +required // +kubebuilder:validation:MinLength=1 @@ -110,11 +119,11 @@ const ( ReversibilityPartial Reversibility = "Partial" ) -// ProposalResult contains the remediation plan from the analysis agent. +// RemediationPlan contains the remediation plan from the analysis agent. // This is part of a RemediationOption and is presented to the user after // analysis, before approval. The risk and reversibility assessments help // users make informed approval decisions. -type ProposalResult struct { +type RemediationPlan struct { // description is a Markdown-formatted summary of the overall remediation // approach. Maximum 8192 characters. // +required @@ -129,7 +138,7 @@ type ProposalResult struct { // +kubebuilder:validation:MaxItems=50 Actions []ProposedAction `json:"actions,omitempty"` // risk is the agent's assessment of how risky the remediation is. - // Critical-risk proposals typically require explicit human review. + // Critical-risk remediations typically require explicit human review. // +required Risk RiskLevel `json:"risk,omitempty"` // reversible indicates whether the remediation can be rolled back @@ -227,7 +236,7 @@ type VerificationPlan struct { // so that users and policy can audit why the permission is needed. type RBACRule struct { // namespace is the target namespace for namespace-scoped rules. - // Must match one of the proposal's targetNamespaces. Ignored for + // Must match one of the run's targetNamespaces. Ignored for // cluster-scoped rules. Validation is deferred to the operator's // policy engine at runtime. Must be a valid RFC 1123 DNS label. // +optional @@ -284,14 +293,14 @@ type RBACRule struct { // RBACResult contains the RBAC permissions requested by the analysis agent // for the execution step. The operator creates a dedicated ServiceAccount -// per proposal and binds these permissions via Role (namespace-scoped) or +// per agentic run and binds these permissions via Role (namespace-scoped) or // ClusterRole (cluster-scoped) before launching the execution sandbox. -// All RBAC resources are cleaned up after the proposal reaches a terminal state. +// All RBAC resources are cleaned up after the run reaches a terminal state. // // +kubebuilder:validation:MinProperties=1 type RBACResult struct { // namespaceScoped are rules that will be applied via Role + RoleBinding - // in the proposal's target namespaces. These are the most common rules. + // in the run's target namespaces. These are the most common rules. // Maximum 50 items. // +optional // +listType=atomic @@ -321,8 +330,8 @@ type RBACResult struct { // affected deployment information as components that the console plugin // renders with custom components. // -// +kubebuilder:validation:XValidation:rule="!has(self.diagnosis) || has(self.proposal)",message="proposal is required when diagnosis is present" -// +kubebuilder:validation:XValidation:rule="!has(self.proposal) || has(self.diagnosis)",message="diagnosis is required when proposal is present" +// +kubebuilder:validation:XValidation:rule="!has(self.diagnosis) || has(self.remediationPlan)",message="remediationPlan is required when diagnosis is present" +// +kubebuilder:validation:XValidation:rule="!has(self.remediationPlan) || has(self.diagnosis)",message="diagnosis is required when remediationPlan is present" type RemediationOption struct { // title is a short Markdown-formatted name for this option // (e.g., "Increase memory limit", "Restart with backoff"). @@ -342,11 +351,11 @@ type RemediationOption struct { // when mode is Minimal. // +optional Diagnosis DiagnosisResult `json:"diagnosis,omitzero"` - // proposal contains the remediation plan for this option. + // remediationPlan contains the remediation plan for this option. // Present when analysisOutput mode is Default (or omitted). Omitted // when mode is Minimal without an execution step. // +optional - Proposal ProposalResult `json:"proposal,omitzero"` + RemediationPlan RemediationPlan `json:"remediationPlan,omitzero"` // verification contains the verification plan. Omitted when // verification is skipped in the workflow. // +optional @@ -357,7 +366,7 @@ type RemediationOption struct { // +optional RBAC RBACResult `json:"rbac,omitzero"` // components contains optional adapter-defined structured data whose - // shape is determined by spec.analysisOutput.schema on the Proposal. + // shape is determined by spec.analysisOutput.schema on the AgenticRun. // The operator passes this through to the AnalysisResult CR; the // console renders it using adapter-specific UI components. // +optional diff --git a/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/proposal_status_types.go b/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/agenticrun_status_types.go similarity index 99% rename from vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/proposal_status_types.go rename to vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/agenticrun_status_types.go index 61fdb4b9e4..79ab5c5cfe 100644 --- a/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/proposal_status_types.go +++ b/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/agenticrun_status_types.go @@ -255,7 +255,7 @@ type VerificationStepStatus struct { // EscalationStepStatus is the observed state of the escalation step. // The operator injects this step when retries are exhausted; it is not -// declared in the Proposal spec. +// declared in the AgenticRun spec. // // +kubebuilder:validation:MinProperties=1 type EscalationStepStatus struct { @@ -280,7 +280,7 @@ type EscalationStepStatus struct { } // StepsStatus contains the per-step observed state for all workflow -// steps. Each step status is populated independently as the proposal +// steps. Each step status is populated independently as the run // progresses through its lifecycle. All fields are set by the operator. // // +kubebuilder:validation:MinProperties=1 diff --git a/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/proposal_types.go b/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/agenticrun_types.go similarity index 76% rename from vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/proposal_types.go rename to vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/agenticrun_types.go index 9efbdea2cc..9ba5340de9 100644 --- a/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/proposal_types.go +++ b/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/agenticrun_types.go @@ -21,27 +21,28 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// ProposalPhase summarizes the proposal's lifecycle state for display. +// AgenticRunPhase summarizes the agentic run's lifecycle state for display. // This type is used internally by the controller, CLI, and console to // derive a human-friendly phase from conditions. It is NOT stored on // the CRD status — use DerivePhase(conditions) to compute it. -type ProposalPhase string +type AgenticRunPhase string const ( - ProposalPhasePending ProposalPhase = "Pending" - ProposalPhaseAnalyzing ProposalPhase = "Analyzing" - ProposalPhaseProposed ProposalPhase = "Proposed" - ProposalPhaseExecuting ProposalPhase = "Executing" - ProposalPhaseVerifying ProposalPhase = "Verifying" - ProposalPhaseCompleted ProposalPhase = "Completed" - ProposalPhaseFailed ProposalPhase = "Failed" - ProposalPhaseDenied ProposalPhase = "Denied" - ProposalPhaseEscalating ProposalPhase = "Escalating" - ProposalPhaseEscalated ProposalPhase = "Escalated" + AgenticRunPhasePending AgenticRunPhase = "Pending" + AgenticRunPhaseAnalyzing AgenticRunPhase = "Analyzing" + AgenticRunPhaseProposed AgenticRunPhase = "Proposed" + AgenticRunPhaseExecuting AgenticRunPhase = "Executing" + AgenticRunPhaseVerifying AgenticRunPhase = "Verifying" + AgenticRunPhaseCompleted AgenticRunPhase = "Completed" + AgenticRunPhaseFailed AgenticRunPhase = "Failed" + AgenticRunPhaseDenied AgenticRunPhase = "Denied" + AgenticRunPhaseEscalating AgenticRunPhase = "Escalating" + AgenticRunPhaseEscalated AgenticRunPhase = "Escalated" + AgenticRunPhaseEmergencyStopped AgenticRunPhase = "EmergencyStopped" ) // Condition reasons used by DerivePhase for state transitions. -// SYNC: must match derivePhaseFromConditions in lightspeed-agentic-console/src/models/proposal.ts +// SYNC: must match derivePhaseFromConditions in lightspeed-agentic-console/src/models/agenticrun.ts const ( ReasonRetryingExecution = "RetryingExecution" ReasonRetriesExhausted = "RetriesExhausted" @@ -50,8 +51,8 @@ const ( // DerivePhase computes the display phase from conditions. Conditions are // the source of truth; this function maps them to a human-friendly phase // for display in CLI, console, and controller routing. -// SYNC: must match derivePhaseFromConditions in lightspeed-agentic-console/src/models/proposal.ts -func DerivePhase(conditions []metav1.Condition) ProposalPhase { +// SYNC: must match derivePhaseFromConditions in lightspeed-agentic-console/src/models/agenticrun.ts +func DerivePhase(conditions []metav1.Condition) AgenticRunPhase { get := func(condType string) *metav1.Condition { for i := range conditions { if conditions[i].Type == condType { @@ -61,61 +62,65 @@ func DerivePhase(conditions []metav1.Condition) ProposalPhase { return nil } - escalated := get(ProposalConditionEscalated) + if c := get(AgenticRunConditionEmergencyStopped); c != nil && c.Status == metav1.ConditionTrue { + return AgenticRunPhaseEmergencyStopped + } + + escalated := get(AgenticRunConditionEscalated) if escalated != nil && escalated.Status == metav1.ConditionTrue { - return ProposalPhaseEscalated + return AgenticRunPhaseEscalated } - if c := get(ProposalConditionDenied); c != nil && c.Status == metav1.ConditionTrue { - return ProposalPhaseDenied + if c := get(AgenticRunConditionDenied); c != nil && c.Status == metav1.ConditionTrue { + return AgenticRunPhaseDenied } if escalated != nil { switch escalated.Status { case metav1.ConditionUnknown: - return ProposalPhaseEscalating + return AgenticRunPhaseEscalating default: - return ProposalPhaseFailed + return AgenticRunPhaseFailed } } - if c := get(ProposalConditionVerified); c != nil { + if c := get(AgenticRunConditionVerified); c != nil { switch c.Status { case metav1.ConditionTrue: - return ProposalPhaseCompleted + return AgenticRunPhaseCompleted case metav1.ConditionUnknown: - return ProposalPhaseVerifying + return AgenticRunPhaseVerifying default: if c.Reason == ReasonRetryingExecution { - return ProposalPhaseExecuting + return AgenticRunPhaseExecuting } - return ProposalPhaseFailed + return AgenticRunPhaseFailed } } - if c := get(ProposalConditionExecuted); c != nil { + if c := get(AgenticRunConditionExecuted); c != nil { switch c.Status { case metav1.ConditionTrue: - return ProposalPhaseVerifying + return AgenticRunPhaseVerifying case metav1.ConditionUnknown: - return ProposalPhaseExecuting + return AgenticRunPhaseExecuting default: - return ProposalPhaseFailed + return AgenticRunPhaseFailed } } - if c := get(ProposalConditionAnalyzed); c != nil { + if c := get(AgenticRunConditionAnalyzed); c != nil { switch c.Status { case metav1.ConditionTrue: - return ProposalPhaseProposed + return AgenticRunPhaseProposed case metav1.ConditionUnknown: - return ProposalPhaseAnalyzing + return AgenticRunPhaseAnalyzing default: - return ProposalPhaseFailed + return AgenticRunPhaseFailed } } - return ProposalPhasePending + return AgenticRunPhasePending } // StepPhase summarizes a single step's lifecycle state for display. @@ -148,9 +153,9 @@ const ( ) // AnalysisOutputMode controls which built-in properties the analysis output -// schema includes. Use Default to get the full schema (diagnosis, proposal, +// schema includes. Use Default to get the full schema (diagnosis, remediation plan, // RBAC, verification). Use Minimal to get only the base structure (options -// array with title) — suitable for analysis-only proposals that define +// array with title) — suitable for analysis-only runs that define // their own output shape via the schema field. // // Allowed values: @@ -162,7 +167,7 @@ type AnalysisOutputMode string const ( // AnalysisOutputModeDefault uses the full analysis output schema with - // all built-in properties (diagnosis, proposal, summary, rbac, verification). + // all built-in properties (diagnosis, remediation plan, summary, rbac, verification). AnalysisOutputModeDefault AnalysisOutputMode = "Default" // AnalysisOutputModeMinimal uses a minimal analysis output schema with // only the base structure (options array with title per option). @@ -181,7 +186,7 @@ const ( type AnalysisOutput struct { // mode controls which built-in properties the analysis output schema // includes. Default includes all built-in properties (diagnosis, - // proposal, summary, rbac, verification). Minimal includes only the + // remediation plan, summary, rbac, verification). Minimal includes only the // base structure (options array with title per option). Omit or set // to "Default" for standard remediation workflows. // +optional @@ -202,8 +207,8 @@ func (a AnalysisOutput) IsZero() bool { return a.Mode == "" && a.Schema == nil } -// Condition types for Proposal. Conditions are the primary mechanism for -// observing proposal state. The operator sets these as the proposal +// Condition types for AgenticRun. Conditions are the primary mechanism for +// observing agentic run state. The operator sets these as the run // progresses through its lifecycle. Each condition has a type, status // (True/False/Unknown), reason (CamelCase token), and message. // @@ -220,32 +225,33 @@ func (a AnalysisOutput) IsZero() bool { // Escalated=True -> max retries exhausted (terminal) // Any condition=False -> step failed; check reason and message const ( - // ProposalConditionAnalyzed indicates whether analysis has completed. + // AgenticRunConditionAnalyzed indicates whether analysis has completed. // Status=True when analysis succeeds, Status=False on failure, // Status=Unknown while analysis is in progress. - ProposalConditionAnalyzed string = "Analyzed" - // ProposalConditionExecuted indicates whether execution has completed. + AgenticRunConditionAnalyzed string = "Analyzed" + // AgenticRunConditionExecuted indicates whether execution has completed. // Status=True when execution succeeds, Status=False on failure, // Status=Unknown while execution is in progress. - ProposalConditionExecuted string = "Executed" - // ProposalConditionVerified indicates whether verification has passed. + AgenticRunConditionExecuted string = "Executed" + // AgenticRunConditionVerified indicates whether verification has passed. // Status=True when verification succeeds, Status=False on failure, // Status=Unknown while verification is in progress. - ProposalConditionVerified string = "Verified" - // ProposalConditionDenied indicates the user denied a step on the - // ProposalApproval resource. Status=True when denied (terminal). - ProposalConditionDenied string = "Denied" - // ProposalConditionEscalated indicates escalation state. Status=Unknown + AgenticRunConditionVerified string = "Verified" + // AgenticRunConditionDenied indicates the user denied a step on the + // AgenticRunApproval resource. Status=True when denied (terminal). + AgenticRunConditionDenied string = "Denied" + // AgenticRunConditionEscalated indicates escalation state. Status=Unknown // while escalation is pending approval or in progress, Status=True when // escalation completes (terminal), Status=False on escalation failure. - ProposalConditionEscalated string = "Escalated" + AgenticRunConditionEscalated string = "Escalated" + AgenticRunConditionEmergencyStopped string = "EmergencyStopped" ) -// ProposalStep defines per-step configuration on a Proposal. The agent +// AgenticRunStep defines per-step configuration on an AgenticRun. The agent // field selects which cluster-scoped Agent CR handles this step. The // tools field provides per-step tools that replace the shared spec.tools. // +kubebuilder:validation:MinProperties=1 -type ProposalStep struct { +type AgenticRunStep struct { // agent is the name of the cluster-scoped Agent CR to use for this step. // Defaults to "default" when omitted. // +optional @@ -260,31 +266,31 @@ type ProposalStep struct { Tools ToolsSpec `json:"tools,omitzero"` } -func (s ProposalStep) IsZero() bool { +func (s AgenticRunStep) IsZero() bool { return s.Agent == "" && s.Tools.IsZero() } -// ProposalSpec defines the desired state of Proposal. +// AgenticRunSpec defines the desired state of AgenticRun. // -// A Proposal defines the workflow shape inline, specifying which steps +// A AgenticRun defines the workflow shape inline, specifying which steps // run and which agent handles each step. Analysis is always required. // Omit execution and/or verification to skip those steps. // // +kubebuilder:validation:XValidation:rule="has(self.analysis)",message="analysis must be provided" // +kubebuilder:validation:XValidation:rule="!has(oldSelf.targetNamespaces) || (has(self.targetNamespaces) && self.targetNamespaces == oldSelf.targetNamespaces)",message="targetNamespaces is immutable once set" // +kubebuilder:validation:XValidation:rule="!has(oldSelf.analysisOutput) || (has(self.analysisOutput) && self.analysisOutput == oldSelf.analysisOutput)",message="analysisOutput is immutable once set" -// +kubebuilder:validation:XValidation:rule="!has(self.analysisOutput) || self.analysisOutput.mode != 'Minimal' || (!has(self.execution) && !has(self.verification))",message="analysisOutput mode Minimal is only allowed for analysis-only proposals (no execution or verification steps)" +// +kubebuilder:validation:XValidation:rule="!has(self.analysisOutput) || self.analysisOutput.mode != 'Minimal' || (!has(self.execution) && !has(self.verification))",message="analysisOutput mode Minimal is only allowed for analysis-only runs (no execution or verification steps)" // +kubebuilder:validation:XValidation:rule="!has(oldSelf.tools) || (has(self.tools) && self.tools == oldSelf.tools)",message="tools is immutable once set" // +kubebuilder:validation:XValidation:rule="!has(oldSelf.analysis) || (has(self.analysis) && self.analysis == oldSelf.analysis)",message="analysis is immutable once set" // +kubebuilder:validation:XValidation:rule="!has(oldSelf.execution) || (has(self.execution) && self.execution == oldSelf.execution)",message="execution is immutable once set" // +kubebuilder:validation:XValidation:rule="!has(oldSelf.verification) || (has(self.verification) && self.verification == oldSelf.verification)",message="verification is immutable once set" -type ProposalSpec struct { +type AgenticRunSpec struct { // request is the user's original request, alert description, or a - // description of what triggered this proposal. This text is passed to + // description of what triggered this agentic run. This text is passed to // the analysis agent as the primary input. // - // Immutable: Proposals are run-to-completion (like Jobs). To change - // the request, create a new Proposal. Use spec.revisionFeedback for + // Immutable: AgenticRuns are run-to-completion (like Jobs). To change + // the request, create a new AgenticRun. Use spec.revisionFeedback for // iterative feedback on an existing analysis. // +required // +kubebuilder:validation:MinLength=1 @@ -292,10 +298,10 @@ type ProposalSpec struct { // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="request is immutable after creation" Request string `json:"request,omitempty"` - // targetNamespaces are the Kubernetes namespace(s) this proposal + // targetNamespaces are the Kubernetes namespace(s) this agentic run // operates on. Used for RBAC scoping and context to the analysis agent. // - // When omitted, the proposal is not namespace-scoped — the analysis + // When omitted, the run is not namespace-scoped — the analysis // agent determines the relevant namespaces from the request context. // Adapters (AlertManager, ACS) typically set this automatically from // the source event. @@ -340,21 +346,21 @@ type ProposalSpec struct { // // Immutable: agent and per-step tools are fixed at creation. // +required - Analysis ProposalStep `json:"analysis,omitzero"` + Analysis AgenticRunStep `json:"analysis,omitzero"` // execution defines per-step configuration for the execution step. // Omit to skip execution (advisory/assisted patterns). // // Immutable: agent and per-step tools are fixed at creation. // +optional - Execution ProposalStep `json:"execution,omitzero"` + Execution AgenticRunStep `json:"execution,omitzero"` // verification defines per-step configuration for the verification step. // Omit to skip verification. // // Immutable: agent and per-step tools are fixed at creation. // +optional - Verification ProposalStep `json:"verification,omitzero"` + Verification AgenticRunStep `json:"verification,omitzero"` // revisionFeedback is the user's free-text feedback requesting changes // to the analysis. Patching this field bumps metadata.generation, which @@ -369,15 +375,15 @@ type ProposalSpec struct { RevisionFeedback string `json:"revisionFeedback,omitempty"` } -// ProposalStatus defines the observed state of Proposal. All fields are +// AgenticRunStatus defines the observed state of AgenticRun. All fields are // set by the operator -- users should not modify status fields directly. -// The status provides complete observability into the proposal's progress, +// The status provides complete observability into the run's progress, // including per-step results, retry history, and standard Kubernetes conditions. // An empty status (`status: {}`) is the initial state before the operator's // first reconcile. // // +kubebuilder:validation:MinProperties=1 -type ProposalStatus struct { +type AgenticRunStatus struct { // conditions represent the latest available observations using the // standard Kubernetes condition pattern. Condition types include: // Analyzed, Approved, Executed, Verified, and Escalated. @@ -387,7 +393,7 @@ type ProposalStatus struct { // +patchMergeKey=type // +optional // +kubebuilder:validation:MinItems=1 - // +kubebuilder:validation:MaxItems=8 + // +kubebuilder:validation:MaxItems=9 Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"` // steps contains the per-step observed state (analysis, execution, @@ -403,17 +409,17 @@ type ProposalStatus struct { // +kubebuilder:printcolumn:name="Request",type=string,JSONPath=`.spec.request`,priority=1 // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` -// Proposal represents a unit of work managed by the agentic platform. +// AgenticRun represents a unit of work managed by the agentic platform. // It is the primary resource component teams and adapters interact with. // -// A Proposal defines the workflow shape inline: which steps run and which +// A AgenticRun defines the workflow shape inline: which steps run and which // agent handles each step. Analysis is always required. Omit execution // and/or verification to skip those steps. // // Example — analysis only (advisory): // // apiVersion: agentic.openshift.io/v1alpha1 -// kind: Proposal +// kind: AgenticRun // metadata: // name: one-off-investigation // spec: @@ -429,7 +435,7 @@ type ProposalStatus struct { // Example — full remediation (analyze → execute → verify): // // apiVersion: agentic.openshift.io/v1alpha1 -// kind: Proposal +// kind: AgenticRun // metadata: // name: fix-nginx-cve-2024-1234 // namespace: stackrox @@ -451,31 +457,31 @@ type ProposalStatus struct { // execution: {} // verification: // agent: fast -type Proposal struct { +type AgenticRun struct { metav1.TypeMeta `json:",inline"` // metadata is the standard object metadata. // +optional metav1.ObjectMeta `json:"metadata,omitempty"` - // spec defines the desired state of Proposal. + // spec defines the desired state of AgenticRun. // +required - Spec ProposalSpec `json:"spec,omitzero"` + Spec AgenticRunSpec `json:"spec,omitzero"` - // status defines the observed state of Proposal. + // status defines the observed state of AgenticRun. // +optional - Status ProposalStatus `json:"status,omitzero"` + Status AgenticRunStatus `json:"status,omitzero"` } // +kubebuilder:object:root=true -// ProposalList contains a list of Proposal. -type ProposalList struct { +// AgenticRunList contains a list of AgenticRun. +type AgenticRunList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata,omitempty"` - Items []Proposal `json:"items"` + Items []AgenticRun `json:"items"` } func init() { - SchemeBuilder.Register(&Proposal{}, &ProposalList{}) + SchemeBuilder.Register(&AgenticRun{}, &AgenticRunList{}) } diff --git a/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/proposalapproval_types.go b/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/agenticrunapproval_types.go similarity index 79% rename from vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/proposalapproval_types.go rename to vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/agenticrunapproval_types.go index ab414e81a6..d023cdfbac 100644 --- a/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/proposalapproval_types.go +++ b/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/agenticrunapproval_types.go @@ -20,6 +20,31 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +// ApproverInfo captures the authenticated identity of the user who approved +// an agentic run stage. Fields are set by the mutating admission webhook and +// are webhook-authoritative — any client-submitted values are overwritten. +// +kubebuilder:validation:MinProperties=1 +type ApproverInfo struct { + // uid is the Kubernetes user UID from the AdmissionReview userInfo. + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + UID string `json:"uid,omitempty"` + + // username is the Kubernetes username from the AdmissionReview userInfo. + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + Username string `json:"username,omitempty"` + + // approvedAt is the server-side time (RFC 3339) when the webhook processed + // the approval request. + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=64 + ApprovedAt string `json:"approvedAt,omitempty"` +} + // ApprovalDecision indicates whether a stage is approved or denied. // +kubebuilder:validation:Enum=Approved;Denied type ApprovalDecision string @@ -72,7 +97,7 @@ type ExecutionApproval struct { Option *int32 `json:"option,omitempty"` // maxAttempts is the number of execution retry attempts approved - // for this proposal. Must not exceed ApprovalPolicy.spec.maxAttempts. + // for this agentic run. Must not exceed ApprovalPolicy.spec.maxAttempts. // Defaults to 1 if unset. // +optional // +kubebuilder:validation:Minimum=1 @@ -120,7 +145,7 @@ type ApprovalStage struct { Type ApprovalStageType `json:"type,omitempty"` // decision indicates whether this stage is approved or denied. - // Denying any stage terminates the entire proposal, even if + // Denying any stage terminates the entire agentic run, even if // earlier stages were already approved. Once set to Denied, // it cannot be changed. // +optional @@ -147,7 +172,7 @@ type ApprovalStage struct { Escalation EscalationApproval `json:"escalation,omitzero"` } -// ProposalApprovalSpec defines the desired state of ProposalApproval. +// AgenticRunApprovalSpec defines the desired state of AgenticRunApproval. // // spec.stages is append-only: once a stage is added, it cannot be removed. // Decisions once set cannot be changed. maxAttempts once set cannot be reduced. @@ -156,7 +181,13 @@ type ApprovalStage struct { // +kubebuilder:validation:XValidation:rule="oldSelf.stages.all(old, !(has(old.decision) && old.decision == 'Denied') || self.stages.exists(s, s.type == old.type && has(s.decision) && s.decision == 'Denied'))",message="decisions once set cannot be changed" // +kubebuilder:validation:XValidation:rule="oldSelf.stages.all(old, old.type != 'Execution' || !has(old.execution) || !has(old.execution.maxAttempts) || old.execution.maxAttempts == 0 || self.stages.exists(s, s.type == 'Execution' && has(s.execution) && has(s.execution.maxAttempts) && s.execution.maxAttempts == old.execution.maxAttempts))",message="maxAttempts once set cannot be changed" // +kubebuilder:validation:MinProperties=1 -type ProposalApprovalSpec struct { +type AgenticRunApprovalSpec struct { + // approver captures the authenticated identity of the user who last + // modified this resource. Set by the mutating admission webhook; + // client-submitted values are overwritten. + // +optional + Approver ApproverInfo `json:"approver,omitempty,omitzero"` + // stages lists the approved (or denied) workflow steps. Each entry is // a discriminated union keyed by type. Users add stages one at a time // via patch as they approve each step. @@ -187,10 +218,10 @@ type ApprovalStageStatus struct { Name string `json:"name,omitempty"` } -// ProposalApprovalStatus defines the observed state of ProposalApproval. +// AgenticRunApprovalStatus defines the observed state of AgenticRunApproval. // // +kubebuilder:validation:MinProperties=1 -type ProposalApprovalStatus struct { +type AgenticRunApprovalStatus struct { // stages contains the per-stage approval status set by the controller. // +optional // +listType=atomic @@ -204,26 +235,30 @@ type ProposalApprovalStatus struct { // +kubebuilder:resource:scope=Namespaced // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` -// ProposalApproval tracks per-step approval state for a Proposal. The -// operator creates it when a Proposal is created. Users update it to +// AgenticRunApproval tracks per-step approval state for an AgenticRun. The +// operator creates it when an AgenticRun is created. Users update it to // approve or deny individual workflow steps. // -// ProposalApproval has a 1:1 relationship with its Proposal (same name, -// same namespace) and is owned by the Proposal via an owner reference +// AgenticRunApproval has a 1:1 relationship with its AgenticRun (same name, +// same namespace) and is owned by the AgenticRun via an owner reference // for garbage collection. // // Example: // // apiVersion: agentic.openshift.io/v1alpha1 -// kind: ProposalApproval +// kind: AgenticRunApproval // metadata: // name: fix-crash // namespace: my-namespace // ownerReferences: // - apiVersion: agentic.openshift.io/v1alpha1 -// kind: Proposal +// kind: AgenticRun // name: fix-crash // spec: +// approver: +// uid: "abc123" +// username: "admin@example.com" +// approvedAt: "2026-06-22T10:00:00Z" // stages: // - type: Analysis // analysis: {} @@ -231,7 +266,7 @@ type ProposalApprovalStatus struct { // execution: // option: 0 // agent: fast -type ProposalApproval struct { +type AgenticRunApproval struct { metav1.TypeMeta `json:",inline"` // metadata is the standard object metadata. @@ -240,22 +275,22 @@ type ProposalApproval struct { // spec defines the desired approval state. // +optional - Spec ProposalApprovalSpec `json:"spec,omitzero"` + Spec AgenticRunApprovalSpec `json:"spec,omitzero"` // status defines the observed approval state. // +optional - Status ProposalApprovalStatus `json:"status,omitzero"` + Status AgenticRunApprovalStatus `json:"status,omitzero"` } // +kubebuilder:object:root=true -// ProposalApprovalList contains a list of ProposalApproval. -type ProposalApprovalList struct { +// AgenticRunApprovalList contains a list of AgenticRunApproval. +type AgenticRunApprovalList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata,omitempty"` - Items []ProposalApproval `json:"items"` + Items []AgenticRunApproval `json:"items"` } func init() { - SchemeBuilder.Register(&ProposalApproval{}, &ProposalApprovalList{}) + SchemeBuilder.Register(&AgenticRunApproval{}, &AgenticRunApprovalList{}) } diff --git a/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/analysisresult_types.go b/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/analysisresult_types.go index 7616238c4c..1fc6d1e7fc 100644 --- a/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/analysisresult_types.go +++ b/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/analysisresult_types.go @@ -54,23 +54,23 @@ type AnalysisResultStatus struct { // AnalysisResultSpec contains the immutable identity fields for an AnalysisResult. type AnalysisResultSpec struct { - // proposalName is the name of the parent Proposal in the same namespace. + // agenticRunName is the name of the parent AgenticRun in the same namespace. // +required // +kubebuilder:validation:MinLength=1 // +kubebuilder:validation:MaxLength=253 - ProposalName string `json:"proposalName,omitempty"` + AgenticRunName string `json:"agenticRunName,omitempty"` } // +kubebuilder:object:root=true // +kubebuilder:resource:scope=Namespaced // +kubebuilder:subresource:status -// +kubebuilder:printcolumn:name="Proposal",type=string,JSONPath=`.spec.proposalName` +// +kubebuilder:printcolumn:name="AgenticRun",type=string,JSONPath=`.spec.agenticRunName` // +kubebuilder:printcolumn:name="Outcome",type=string,JSONPath=`.status.conditions[?(@.type=="Completed")].reason` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // AnalysisResult records the output of a single analysis step execution. // Created by the operator after the analysis agent completes. Owned by -// the parent Proposal for garbage collection. +// the parent AgenticRun for garbage collection. type AnalysisResult struct { metav1.TypeMeta `json:",inline"` diff --git a/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/approvalpolicy_types.go b/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/approvalpolicy_types.go index 9836715b02..9fbfdfc841 100644 --- a/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/approvalpolicy_types.go +++ b/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/approvalpolicy_types.go @@ -29,7 +29,7 @@ const ( ApprovalModeManual ApprovalMode = "Manual" ) -const DefaultMaxConcurrentProposals int32 = 5 +const DefaultMaxConcurrentRuns int32 = 5 // ApprovalPolicyStage configures the approval mode for a single workflow step. type ApprovalPolicyStage struct { @@ -39,9 +39,9 @@ type ApprovalPolicyStage struct { Name SandboxStep `json:"name,omitempty"` // approval controls whether this step auto-approves or requires - // explicit user approval on the ProposalApproval resource. + // explicit user approval on the AgenticRunApproval resource. // Allowed values: Automatic (step runs without user approval), - // Manual (step waits for explicit approval on ProposalApproval). + // Manual (step waits for explicit approval on AgenticRunApproval). // +required Approval ApprovalMode `json:"approval,omitempty"` } @@ -60,22 +60,22 @@ type ApprovalPolicySpec struct { Stages []ApprovalPolicyStage `json:"stages,omitempty"` // maxAttempts sets the maximum number of execution retry attempts - // allowed for proposals. When verification fails, the operator retries + // allowed for agentic runs. When verification fails, the operator retries // execution up to this limit before escalating. Defaults to 1 if omitted. // +optional // +kubebuilder:validation:Minimum=1 // +kubebuilder:validation:Maximum=3 MaxAttempts int32 `json:"maxAttempts,omitempty"` - // maxConcurrentProposals sets the maximum number of proposals the - // operator reconciles concurrently. Higher values allow more proposals + // maxConcurrentRuns sets the maximum number of agentic runs the + // operator reconciles concurrently. Higher values allow more agentic runs // to run in parallel but consume more cluster resources. // Defaults to 5 if omitted. // +optional // +kubebuilder:validation:Minimum=1 // +kubebuilder:validation:Maximum=20 // +default=5 - MaxConcurrentProposals int32 `json:"maxConcurrentProposals,omitempty"` + MaxConcurrentRuns int32 `json:"maxConcurrentRuns,omitempty"` } // +kubebuilder:object:root=true @@ -84,11 +84,11 @@ type ApprovalPolicySpec struct { // +kubebuilder:validation:XValidation:rule="self.metadata.name == 'cluster'",message="ApprovalPolicy must be named 'cluster' (singleton)" // ApprovalPolicy is a cluster-scoped singleton that configures default -// approval behavior for proposal workflow steps. The cluster admin creates +// approval behavior for agentic run workflow steps. The cluster admin creates // a single ApprovalPolicy named "cluster" to control which steps auto-approve. // // Steps not listed in the policy default to Manual (require explicit -// user approval on the ProposalApproval resource). +// user approval on the AgenticRunApproval resource). // // Example: // diff --git a/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/escalationresult_types.go b/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/escalationresult_types.go index c46349740e..1e1aa8d1b8 100644 --- a/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/escalationresult_types.go +++ b/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/escalationresult_types.go @@ -59,23 +59,23 @@ type EscalationResultStatus struct { // EscalationResultSpec contains the immutable identity fields for an EscalationResult. type EscalationResultSpec struct { - // proposalName is the name of the parent Proposal in the same namespace. + // agenticRunName is the name of the parent AgenticRun in the same namespace. // +required // +kubebuilder:validation:MinLength=1 // +kubebuilder:validation:MaxLength=253 - ProposalName string `json:"proposalName,omitempty"` + AgenticRunName string `json:"agenticRunName,omitempty"` } // +kubebuilder:object:root=true // +kubebuilder:resource:scope=Namespaced // +kubebuilder:subresource:status -// +kubebuilder:printcolumn:name="Proposal",type=string,JSONPath=`.spec.proposalName` +// +kubebuilder:printcolumn:name="AgenticRun",type=string,JSONPath=`.spec.agenticRunName` // +kubebuilder:printcolumn:name="Outcome",type=string,JSONPath=`.status.conditions[?(@.type=="Completed")].reason` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // EscalationResult records the output of the escalation step. Created by // the operator after the escalation agent completes. Owned by the parent -// Proposal for garbage collection. +// AgenticRun for garbage collection. type EscalationResult struct { metav1.TypeMeta `json:",inline"` diff --git a/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/executionresult_types.go b/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/executionresult_types.go index f6d6215f46..ce8dc28e86 100644 --- a/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/executionresult_types.go +++ b/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/executionresult_types.go @@ -59,11 +59,11 @@ type ExecutionResultStatus struct { // ExecutionResultSpec contains the immutable identity fields for an ExecutionResult. type ExecutionResultSpec struct { - // proposalName is the name of the parent Proposal in the same namespace. + // agenticRunName is the name of the parent AgenticRun in the same namespace. // +required // +kubebuilder:validation:MinLength=1 // +kubebuilder:validation:MaxLength=253 - ProposalName string `json:"proposalName,omitempty"` + AgenticRunName string `json:"agenticRunName,omitempty"` // retryIndex is the 0-based retry index within the current analysis. // First execution has retryIndex 0, first retry has retryIndex 1, etc. @@ -76,14 +76,14 @@ type ExecutionResultSpec struct { // +kubebuilder:object:root=true // +kubebuilder:resource:scope=Namespaced // +kubebuilder:subresource:status -// +kubebuilder:printcolumn:name="Proposal",type=string,JSONPath=`.spec.proposalName` +// +kubebuilder:printcolumn:name="AgenticRun",type=string,JSONPath=`.spec.agenticRunName` // +kubebuilder:printcolumn:name="Retry",type=integer,JSONPath=`.spec.retryIndex` // +kubebuilder:printcolumn:name="Outcome",type=string,JSONPath=`.status.conditions[?(@.type=="Completed")].reason` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // ExecutionResult records the output of a single execution step execution. // Created by the operator after the execution agent completes. Owned by -// the parent Proposal for garbage collection. +// the parent AgenticRun for garbage collection. type ExecutionResult struct { metav1.TypeMeta `json:",inline"` diff --git a/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/llmprovider_types.go b/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/llmprovider_types.go index e95ba0cf70..1324282d01 100644 --- a/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/llmprovider_types.go +++ b/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/llmprovider_types.go @@ -72,6 +72,20 @@ type AnthropicConfig struct { URL string `json:"url,omitempty"` } +// GoogleCloudVertexModelProvider selects which model provider stack talks to Vertex AI. +// +// +kubebuilder:validation:Enum=Anthropic;Google;OpenAI +type GoogleCloudVertexModelProvider string + +const ( + // GoogleCloudVertexModelProviderAnthropic uses Anthropic models on Vertex (Claude SDK). + GoogleCloudVertexModelProviderAnthropic GoogleCloudVertexModelProvider = "Anthropic" + // GoogleCloudVertexModelProviderGoogle uses Google models on Vertex (GenAI SDK). + GoogleCloudVertexModelProviderGoogle GoogleCloudVertexModelProvider = "Google" + // GoogleCloudVertexModelProviderOpenAI uses OpenAI-compatible models on Vertex (OpenAI SDK). + GoogleCloudVertexModelProviderOpenAI GoogleCloudVertexModelProvider = "OpenAI" +) + // GoogleCloudVertexConfig contains configuration for the Google Cloud Vertex AI provider. type GoogleCloudVertexConfig struct { // credentialsSecret references a Secret in the operator namespace @@ -100,6 +114,13 @@ type GoogleCloudVertexConfig struct { // +kubebuilder:validation:XValidation:rule="self.matches('^[a-z][a-z0-9-]*[a-z0-9]$')",message="region must contain only lowercase letters, digits, and hyphens, start with a letter, and not end with a hyphen" Region string `json:"region,omitempty"` + // modelProvider selects which model provider stack talks to Vertex AI (required). + // "Anthropic" uses Anthropic models on Vertex; "Google" uses Google models on Vertex; + // "OpenAI" uses OpenAI-compatible models on Vertex. + // Enum is defined on GoogleCloudVertexModelProvider. + // +required + ModelProvider GoogleCloudVertexModelProvider `json:"modelProvider,omitempty"` + // url is an optional override for the Vertex AI API endpoint. // Only needed for custom deployments or API proxies. // Must be a valid HTTP or HTTPS URL with a hostname. Paths and query @@ -288,7 +309,7 @@ type LLMProviderSpec struct { // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // LLMProvider defines an LLM provider configuration. It is the first link in -// the CRD chain (LLMProvider -> Agent -> Workflow -> Proposal) and is +// the CRD chain (LLMProvider -> Agent -> Workflow -> AgenticRun) and is // referenced by Agent resources via spec.llmProvider. // // LLMProvider is cluster-scoped — the cluster admin manages LLM infrastructure @@ -312,6 +333,7 @@ type LLMProviderSpec struct { // name: llm-credentials // projectID: my-gcp-project // region: us-central1 +// modelProvider: Anthropic type LLMProvider struct { metav1.TypeMeta `json:",inline"` diff --git a/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/result_types.go b/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/result_types.go index 3242d18990..aba9f7da49 100644 --- a/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/result_types.go +++ b/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/result_types.go @@ -30,10 +30,10 @@ const ( ) func (r *AnalysisResult) GetConditions() []metav1.Condition { return r.Status.Conditions } -func (r *AnalysisResult) SetConditions(c []metav1.Condition) { r.Status.Conditions = c } -func (r *ExecutionResult) GetConditions() []metav1.Condition { return r.Status.Conditions } -func (r *ExecutionResult) SetConditions(c []metav1.Condition) { r.Status.Conditions = c } -func (r *VerificationResult) GetConditions() []metav1.Condition { return r.Status.Conditions } -func (r *VerificationResult) SetConditions(c []metav1.Condition) { r.Status.Conditions = c } -func (r *EscalationResult) GetConditions() []metav1.Condition { return r.Status.Conditions } -func (r *EscalationResult) SetConditions(c []metav1.Condition) { r.Status.Conditions = c } +func (r *AnalysisResult) SetConditions(c []metav1.Condition) { r.Status.Conditions = c } +func (r *ExecutionResult) GetConditions() []metav1.Condition { return r.Status.Conditions } +func (r *ExecutionResult) SetConditions(c []metav1.Condition) { r.Status.Conditions = c } +func (r *VerificationResult) GetConditions() []metav1.Condition { return r.Status.Conditions } +func (r *VerificationResult) SetConditions(c []metav1.Condition) { r.Status.Conditions = c } +func (r *EscalationResult) GetConditions() []metav1.Condition { return r.Status.Conditions } +func (r *EscalationResult) SetConditions(c []metav1.Condition) { r.Status.Conditions = c } diff --git a/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/shared_types.go b/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/shared_types.go index 753e8da731..27eb633c6a 100644 --- a/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/shared_types.go +++ b/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/shared_types.go @@ -189,5 +189,5 @@ type SkillsSource struct { // +kubebuilder:validation:XValidation:rule="self.all(p, p.matches('^[a-zA-Z0-9/_.-]+$'))",message="paths may only contain alphanumeric characters, '/', '_', '.', and '-'" // +kubebuilder:validation:items:MinLength=2 // +kubebuilder:validation:items:MaxLength=512 - Paths []string `json:"paths"` + Paths []string `json:"paths,omitempty"` } diff --git a/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/tools_types.go b/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/tools_types.go index 4955d28ee6..96d9798e65 100644 --- a/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/tools_types.go +++ b/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/tools_types.go @@ -86,7 +86,7 @@ type SecretMountSpec struct { // SecretRequirement declares a Kubernetes Secret that the sandbox needs // at runtime. The Secret must exist in the operator namespace (where -// sandbox pods run), not in the Proposal's namespace. +// sandbox pods run), not in the AgenticRun's namespace. type SecretRequirement struct { // name of the Secret (must exist in the operator namespace). // Must be a valid RFC 1123 DNS subdomain. @@ -111,7 +111,7 @@ type SecretRequirement struct { // ToolsSpec defines the tools available to an agent in its sandbox pod. // This includes skills images, MCP servers, and required secrets. // -// ToolsSpec is specified on a Proposal either as a shared default +// ToolsSpec is specified on an AgenticRun either as a shared default // (spec.tools) or per-step (spec.analysis.tools, spec.execution.tools, // spec.verification.tools). Per-step tools replace the shared default // for that step. @@ -140,7 +140,7 @@ type ToolsSpec struct { // requiredSecrets declares Kubernetes Secrets that the sandbox pod // needs at runtime. The cluster admin creates the actual Secrets - // in the same namespace as the Proposal. + // in the same namespace as the AgenticRun. // +optional // +listType=map // +listMapKey=name diff --git a/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/verificationresult_types.go b/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/verificationresult_types.go index 11a3bbe8bf..15629248ef 100644 --- a/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/verificationresult_types.go +++ b/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/verificationresult_types.go @@ -60,11 +60,11 @@ type VerificationResultStatus struct { // VerificationResultSpec contains the immutable identity fields for a VerificationResult. type VerificationResultSpec struct { - // proposalName is the name of the parent Proposal in the same namespace. + // agenticRunName is the name of the parent AgenticRun in the same namespace. // +required // +kubebuilder:validation:MinLength=1 // +kubebuilder:validation:MaxLength=253 - ProposalName string `json:"proposalName,omitempty"` + AgenticRunName string `json:"agenticRunName,omitempty"` // retryIndex is the 0-based retry index within the current analysis. // +required @@ -76,14 +76,14 @@ type VerificationResultSpec struct { // +kubebuilder:object:root=true // +kubebuilder:resource:scope=Namespaced // +kubebuilder:subresource:status -// +kubebuilder:printcolumn:name="Proposal",type=string,JSONPath=`.spec.proposalName` +// +kubebuilder:printcolumn:name="AgenticRun",type=string,JSONPath=`.spec.agenticRunName` // +kubebuilder:printcolumn:name="Retry",type=integer,JSONPath=`.spec.retryIndex` // +kubebuilder:printcolumn:name="Outcome",type=string,JSONPath=`.status.conditions[?(@.type=="Completed")].reason` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // VerificationResult records the output of a single verification step // execution. Created by the operator after the verification agent -// completes. Owned by the parent Proposal for garbage collection. +// completes. Owned by the parent AgenticRun for garbage collection. type VerificationResult struct { metav1.TypeMeta `json:",inline"` diff --git a/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/zz_generated.deepcopy.go b/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/zz_generated.deepcopy.go index ebdea54d6a..13f6e6a036 100644 --- a/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/zz_generated.deepcopy.go +++ b/vendor/github.com/openshift/lightspeed-agentic-operator/api/v1alpha1/zz_generated.deepcopy.go @@ -139,6 +139,330 @@ func (in *AgentTimeouts) DeepCopy() *AgentTimeouts { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AgenticOLSConfig) DeepCopyInto(out *AgenticOLSConfig) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AgenticOLSConfig. +func (in *AgenticOLSConfig) DeepCopy() *AgenticOLSConfig { + if in == nil { + return nil + } + out := new(AgenticOLSConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *AgenticOLSConfig) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AgenticOLSConfigList) DeepCopyInto(out *AgenticOLSConfigList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]AgenticOLSConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AgenticOLSConfigList. +func (in *AgenticOLSConfigList) DeepCopy() *AgenticOLSConfigList { + if in == nil { + return nil + } + out := new(AgenticOLSConfigList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *AgenticOLSConfigList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AgenticOLSConfigSpec) DeepCopyInto(out *AgenticOLSConfigSpec) { + *out = *in + out.Audit = in.Audit +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AgenticOLSConfigSpec. +func (in *AgenticOLSConfigSpec) DeepCopy() *AgenticOLSConfigSpec { + if in == nil { + return nil + } + out := new(AgenticOLSConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AgenticOLSConfigStatus) DeepCopyInto(out *AgenticOLSConfigStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AgenticOLSConfigStatus. +func (in *AgenticOLSConfigStatus) DeepCopy() *AgenticOLSConfigStatus { + if in == nil { + return nil + } + out := new(AgenticOLSConfigStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AgenticRun) DeepCopyInto(out *AgenticRun) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AgenticRun. +func (in *AgenticRun) DeepCopy() *AgenticRun { + if in == nil { + return nil + } + out := new(AgenticRun) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *AgenticRun) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AgenticRunApproval) DeepCopyInto(out *AgenticRunApproval) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AgenticRunApproval. +func (in *AgenticRunApproval) DeepCopy() *AgenticRunApproval { + if in == nil { + return nil + } + out := new(AgenticRunApproval) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *AgenticRunApproval) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AgenticRunApprovalList) DeepCopyInto(out *AgenticRunApprovalList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]AgenticRunApproval, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AgenticRunApprovalList. +func (in *AgenticRunApprovalList) DeepCopy() *AgenticRunApprovalList { + if in == nil { + return nil + } + out := new(AgenticRunApprovalList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *AgenticRunApprovalList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AgenticRunApprovalSpec) DeepCopyInto(out *AgenticRunApprovalSpec) { + *out = *in + out.Approver = in.Approver + if in.Stages != nil { + in, out := &in.Stages, &out.Stages + *out = make([]ApprovalStage, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AgenticRunApprovalSpec. +func (in *AgenticRunApprovalSpec) DeepCopy() *AgenticRunApprovalSpec { + if in == nil { + return nil + } + out := new(AgenticRunApprovalSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AgenticRunApprovalStatus) DeepCopyInto(out *AgenticRunApprovalStatus) { + *out = *in + if in.Stages != nil { + in, out := &in.Stages, &out.Stages + *out = make([]ApprovalStageStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AgenticRunApprovalStatus. +func (in *AgenticRunApprovalStatus) DeepCopy() *AgenticRunApprovalStatus { + if in == nil { + return nil + } + out := new(AgenticRunApprovalStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AgenticRunList) DeepCopyInto(out *AgenticRunList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]AgenticRun, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AgenticRunList. +func (in *AgenticRunList) DeepCopy() *AgenticRunList { + if in == nil { + return nil + } + out := new(AgenticRunList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *AgenticRunList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AgenticRunSpec) DeepCopyInto(out *AgenticRunSpec) { + *out = *in + if in.TargetNamespaces != nil { + in, out := &in.TargetNamespaces, &out.TargetNamespaces + *out = make([]string, len(*in)) + copy(*out, *in) + } + in.AnalysisOutput.DeepCopyInto(&out.AnalysisOutput) + in.Tools.DeepCopyInto(&out.Tools) + in.Analysis.DeepCopyInto(&out.Analysis) + in.Execution.DeepCopyInto(&out.Execution) + in.Verification.DeepCopyInto(&out.Verification) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AgenticRunSpec. +func (in *AgenticRunSpec) DeepCopy() *AgenticRunSpec { + if in == nil { + return nil + } + out := new(AgenticRunSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AgenticRunStatus) DeepCopyInto(out *AgenticRunStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.Steps.DeepCopyInto(&out.Steps) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AgenticRunStatus. +func (in *AgenticRunStatus) DeepCopy() *AgenticRunStatus { + if in == nil { + return nil + } + out := new(AgenticRunStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AgenticRunStep) DeepCopyInto(out *AgenticRunStep) { + *out = *in + in.Tools.DeepCopyInto(&out.Tools) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AgenticRunStep. +func (in *AgenticRunStep) DeepCopy() *AgenticRunStep { + if in == nil { + return nil + } + out := new(AgenticRunStep) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AnalysisApproval) DeepCopyInto(out *AnalysisApproval) { *out = *in @@ -455,6 +779,52 @@ func (in *ApprovalStageStatus) DeepCopy() *ApprovalStageStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ApproverInfo) DeepCopyInto(out *ApproverInfo) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApproverInfo. +func (in *ApproverInfo) DeepCopy() *ApproverInfo { + if in == nil { + return nil + } + out := new(ApproverInfo) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AuditConfig) DeepCopyInto(out *AuditConfig) { + *out = *in + out.OTEL = in.OTEL +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AuditConfig. +func (in *AuditConfig) DeepCopy() *AuditConfig { + if in == nil { + return nil + } + out := new(AuditConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AuditOTELConfig) DeepCopyInto(out *AuditOTELConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AuditOTELConfig. +func (in *AuditOTELConfig) DeepCopy() *AuditOTELConfig { + if in == nil { + return nil + } + out := new(AuditOTELConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AzureOpenAIConfig) DeepCopyInto(out *AzureOpenAIConfig) { *out = *in @@ -994,253 +1364,6 @@ func (in *OpenAIConfig) DeepCopy() *OpenAIConfig { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Proposal) DeepCopyInto(out *Proposal) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Proposal. -func (in *Proposal) DeepCopy() *Proposal { - if in == nil { - return nil - } - out := new(Proposal) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Proposal) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ProposalApproval) DeepCopyInto(out *ProposalApproval) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProposalApproval. -func (in *ProposalApproval) DeepCopy() *ProposalApproval { - if in == nil { - return nil - } - out := new(ProposalApproval) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ProposalApproval) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ProposalApprovalList) DeepCopyInto(out *ProposalApprovalList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]ProposalApproval, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProposalApprovalList. -func (in *ProposalApprovalList) DeepCopy() *ProposalApprovalList { - if in == nil { - return nil - } - out := new(ProposalApprovalList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ProposalApprovalList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ProposalApprovalSpec) DeepCopyInto(out *ProposalApprovalSpec) { - *out = *in - if in.Stages != nil { - in, out := &in.Stages, &out.Stages - *out = make([]ApprovalStage, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProposalApprovalSpec. -func (in *ProposalApprovalSpec) DeepCopy() *ProposalApprovalSpec { - if in == nil { - return nil - } - out := new(ProposalApprovalSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ProposalApprovalStatus) DeepCopyInto(out *ProposalApprovalStatus) { - *out = *in - if in.Stages != nil { - in, out := &in.Stages, &out.Stages - *out = make([]ApprovalStageStatus, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProposalApprovalStatus. -func (in *ProposalApprovalStatus) DeepCopy() *ProposalApprovalStatus { - if in == nil { - return nil - } - out := new(ProposalApprovalStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ProposalList) DeepCopyInto(out *ProposalList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]Proposal, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProposalList. -func (in *ProposalList) DeepCopy() *ProposalList { - if in == nil { - return nil - } - out := new(ProposalList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ProposalList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ProposalResult) DeepCopyInto(out *ProposalResult) { - *out = *in - if in.Actions != nil { - in, out := &in.Actions, &out.Actions - *out = make([]ProposedAction, len(*in)) - copy(*out, *in) - } - out.RollbackPlan = in.RollbackPlan -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProposalResult. -func (in *ProposalResult) DeepCopy() *ProposalResult { - if in == nil { - return nil - } - out := new(ProposalResult) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ProposalSpec) DeepCopyInto(out *ProposalSpec) { - *out = *in - if in.TargetNamespaces != nil { - in, out := &in.TargetNamespaces, &out.TargetNamespaces - *out = make([]string, len(*in)) - copy(*out, *in) - } - in.AnalysisOutput.DeepCopyInto(&out.AnalysisOutput) - in.Tools.DeepCopyInto(&out.Tools) - in.Analysis.DeepCopyInto(&out.Analysis) - in.Execution.DeepCopyInto(&out.Execution) - in.Verification.DeepCopyInto(&out.Verification) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProposalSpec. -func (in *ProposalSpec) DeepCopy() *ProposalSpec { - if in == nil { - return nil - } - out := new(ProposalSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ProposalStatus) DeepCopyInto(out *ProposalStatus) { - *out = *in - if in.Conditions != nil { - in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - in.Steps.DeepCopyInto(&out.Steps) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProposalStatus. -func (in *ProposalStatus) DeepCopy() *ProposalStatus { - if in == nil { - return nil - } - out := new(ProposalStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ProposalStep) DeepCopyInto(out *ProposalStep) { - *out = *in - in.Tools.DeepCopyInto(&out.Tools) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProposalStep. -func (in *ProposalStep) DeepCopy() *ProposalStep { - if in == nil { - return nil - } - out := new(ProposalStep) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ProposedAction) DeepCopyInto(out *ProposedAction) { *out = *in @@ -1324,7 +1447,7 @@ func (in *RBACRule) DeepCopy() *RBACRule { func (in *RemediationOption) DeepCopyInto(out *RemediationOption) { *out = *in out.Diagnosis = in.Diagnosis - in.Proposal.DeepCopyInto(&out.Proposal) + in.RemediationPlan.DeepCopyInto(&out.RemediationPlan) in.Verification.DeepCopyInto(&out.Verification) in.RBAC.DeepCopyInto(&out.RBAC) if in.Components != nil { @@ -1344,6 +1467,27 @@ func (in *RemediationOption) DeepCopy() *RemediationOption { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RemediationPlan) DeepCopyInto(out *RemediationPlan) { + *out = *in + if in.Actions != nil { + in, out := &in.Actions, &out.Actions + *out = make([]ProposedAction, len(*in)) + copy(*out, *in) + } + out.RollbackPlan = in.RollbackPlan +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RemediationPlan. +func (in *RemediationPlan) DeepCopy() *RemediationPlan { + if in == nil { + return nil + } + out := new(RemediationPlan) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RollbackPlan) DeepCopyInto(out *RollbackPlan) { *out = *in diff --git a/vendor/golang.org/x/net/html/entity.go b/vendor/golang.org/x/net/html/entity.go index b628880a01..4e8d5d55f2 100644 --- a/vendor/golang.org/x/net/html/entity.go +++ b/vendor/golang.org/x/net/html/entity.go @@ -2156,9 +2156,8 @@ var entity = map[string]rune{ // HTML entities that are two unicode codepoints. var entity2 = map[string][2]rune{ - // TODO(nigeltao): Handle replacements that are wider than their names. - // "nLt;": {'\u226A', '\u20D2'}, - // "nGt;": {'\u226B', '\u20D2'}, + "nLt;": {'\u226A', '\u20D2'}, + "nGt;": {'\u226B', '\u20D2'}, "NotEqualTilde;": {'\u2242', '\u0338'}, "NotGreaterFullEqual;": {'\u2267', '\u0338'}, "NotGreaterGreater;": {'\u226B', '\u0338'}, diff --git a/vendor/golang.org/x/net/html/escape.go b/vendor/golang.org/x/net/html/escape.go index 12f2273706..df3edc5b12 100644 --- a/vendor/golang.org/x/net/html/escape.go +++ b/vendor/golang.org/x/net/html/escape.go @@ -6,6 +6,7 @@ package html import ( "bytes" + "slices" "strings" "unicode/utf8" ) @@ -50,25 +51,24 @@ var replacementTable = [...]rune{ // 0x0D->'\u000D' is a no-op. } -// unescapeEntity reads an entity like "<" from b[src:] and writes the -// corresponding "<" to b[dst:], returning the incremented dst and src cursors. -// Precondition: b[src] == '&' && dst <= src. -// attribute should be true if parsing an attribute value. -func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { +// unescapeEntity attempts to consume a character reference from s[src:], +// returning the rune, potential second rune, and number of bytes consumed +// (which indicates the length of the character reference). It is assumed that +// the first byte of s is '&'. attribute should be true if parsing an attribute +// value. +func unescapeEntity(s []byte, attribute bool) (rune, rune, int) { // https://html.spec.whatwg.org/multipage/syntax.html#consume-a-character-reference // i starts at 1 because we already know that s[0] == '&'. - i, s := 1, b[src:] + i := 1 if len(s) <= 1 { - b[dst] = b[src] - return dst + 1, src + 1 + return '&', 0, 1 } if s[i] == '#' { - if len(s) <= 3 { // We need to have at least "&#.". - b[dst] = b[src] - return dst + 1, src + 1 + if len(s) <= 2 { // We need to have at least "&#". + return '&', 0, 1 } i++ c := s[i] @@ -78,34 +78,43 @@ func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { i++ } + i0 := i x := '\x00' for i < len(s) { c = s[i] - i++ + var d rune + var mult rune if hex { + mult = 16 if '0' <= c && c <= '9' { - x = 16*x + rune(c) - '0' - continue + d = rune(c) - '0' } else if 'a' <= c && c <= 'f' { - x = 16*x + rune(c) - 'a' + 10 - continue + d = rune(c) - 'a' + 10 } else if 'A' <= c && c <= 'F' { - x = 16*x + rune(c) - 'A' + 10 - continue + d = rune(c) - 'A' + 10 + } else { + break + } + } else { + mult = 10 + if '0' <= c && c <= '9' { + d = rune(c) - '0' + } else { + break } - } else if '0' <= c && c <= '9' { - x = 10*x + rune(c) - '0' - continue } - if c != ';' { - i-- + if x <= 0x10FFFF { + x = mult*x + d } - break + i++ + } + + if i == i0 { // No characters matched. + return '&', 0, 1 } - if i <= 3 { // No characters matched. - b[dst] = b[src] - return dst + 1, src + 1 + if i < len(s) && s[i] == ';' { + i++ } if 0x80 <= x && x <= 0x9F { @@ -116,7 +125,7 @@ func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { x = '\uFFFD' } - return dst + utf8.EncodeRune(b[dst:], x), src + i + return x, 0, i } // Consume the maximum number of characters possible, with the @@ -141,10 +150,9 @@ func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { } else if attribute && entityName[len(entityName)-1] != ';' && len(s) > i && s[i] == '=' { // No-op. } else if x := entity[entityName]; x != 0 { - return dst + utf8.EncodeRune(b[dst:], x), src + i + return x, 0, i } else if x := entity2[entityName]; x[0] != 0 { - dst1 := dst + utf8.EncodeRune(b[dst:], x[0]) - return dst1 + utf8.EncodeRune(b[dst1:], x[1]), src + i + return x[0], x[1], i } else if !attribute { maxLen := len(entityName) - 1 if maxLen > longestEntityWithoutSemicolon { @@ -152,35 +160,67 @@ func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { } for j := maxLen; j > 1; j-- { if x := entity[entityName[:j]]; x != 0 { - return dst + utf8.EncodeRune(b[dst:], x), src + j + 1 + return x, 0, j + 1 } } } - dst1, src1 = dst+i, src+i - copy(b[dst:dst1], b[src:src1]) - return dst1, src1 + return '&', 0, 1 } -// unescape unescapes b's entities in-place, so that "a<b" becomes "a entityNameLen { + if reusingB { + out = slices.Clone(out) + reusingB = false } - return b[0:dst] + out = slices.Grow(out, replLen) + } + out = utf8.AppendRune(out, r1) + if r2 != 0 { + out = utf8.AppendRune(out, r2) } + + src += entityNameLen } - return b + + return out } // lower lower-cases the A-Z bytes in b in-place, so that "aBc" becomes "abc". diff --git a/vendor/golang.org/x/net/html/foreign.go b/vendor/golang.org/x/net/html/foreign.go index e8515d8e88..65d01d1ed9 100644 --- a/vendor/golang.org/x/net/html/foreign.go +++ b/vendor/golang.org/x/net/html/foreign.go @@ -23,7 +23,7 @@ func adjustForeignAttributes(aa []Attribute) { } switch a.Key { case "xlink:actuate", "xlink:arcrole", "xlink:href", "xlink:role", "xlink:show", - "xlink:title", "xlink:type", "xml:base", "xml:lang", "xml:space", "xmlns:xlink": + "xlink:title", "xlink:type", "xml:lang", "xml:space", "xmlns:xlink": j := strings.Index(a.Key, ":") aa[i].Namespace = a.Key[:j] aa[i].Key = a.Key[j+1:] diff --git a/vendor/golang.org/x/net/html/iter.go b/vendor/golang.org/x/net/html/iter.go index 54be8fd30f..349ef73e64 100644 --- a/vendor/golang.org/x/net/html/iter.go +++ b/vendor/golang.org/x/net/html/iter.go @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build go1.23 - package html import "iter" diff --git a/vendor/golang.org/x/net/html/parse.go b/vendor/golang.org/x/net/html/parse.go index 88fc0056a3..165b6108d4 100644 --- a/vendor/golang.org/x/net/html/parse.go +++ b/vendor/golang.org/x/net/html/parse.go @@ -5,9 +5,11 @@ package html import ( + "cmp" "errors" "fmt" "io" + "slices" "strings" a "golang.org/x/net/html/atom" @@ -61,7 +63,7 @@ func (p *parser) top() *Node { // Stop tags for use in popUntil. These come from section 12.2.4.2. var ( defaultScopeStopTags = map[string][]a.Atom{ - "": {a.Applet, a.Caption, a.Html, a.Table, a.Td, a.Th, a.Marquee, a.Object, a.Template}, + "": {a.Applet, a.Caption, a.Html, a.Table, a.Td, a.Th, a.Marquee, a.Object, a.Template, a.Select}, "math": {a.AnnotationXml, a.Mi, a.Mn, a.Mo, a.Ms, a.Mtext}, "svg": {a.Desc, a.ForeignObject, a.Title}, } @@ -76,7 +78,6 @@ const ( tableScope tableRowScope tableBodyScope - selectScope ) // popUntil pops the stack of open elements at the highest element whose tag @@ -131,10 +132,6 @@ func (p *parser) indexOfElementInScope(s scope, matchTags ...a.Atom) int { if tagAtom == a.Html || tagAtom == a.Table || tagAtom == a.Template { return -1 } - case selectScope: - if tagAtom != a.Optgroup && tagAtom != a.Option { - return -1 - } default: panic(fmt.Sprintf("html: internal error: indexOfElementInScope unknown scope: %d", s)) } @@ -328,6 +325,14 @@ func (p *parser) addText(text string) { }) } +func attrCompare(a, b Attribute) int { + return cmp.Or( + cmp.Compare(a.Namespace, b.Namespace), + cmp.Compare(a.Key, b.Key), + cmp.Compare(a.Val, b.Val), + ) +} + // addElement adds a child element based on the current token. func (p *parser) addElement() { p.addChild(&Node{ @@ -343,6 +348,10 @@ func (p *parser) addFormattingElement() { tagAtom, attr := p.tok.DataAtom, p.tok.Attr p.addElement() + // In order to optimize the search, we need the attributes to be sorted, so we + // can just use slices.Equal. + slices.SortFunc(attr, attrCompare) + // Implement the Noah's Ark clause, but with three per family instead of two. identicalElements := 0 findIdenticalElements: @@ -360,19 +369,7 @@ findIdenticalElements: if n.DataAtom != tagAtom { continue } - if len(n.Attr) != len(attr) { - continue - } - compareAttributes: - for _, t0 := range n.Attr { - for _, t1 := range attr { - if t0.Key == t1.Key && t0.Namespace == t1.Namespace && t0.Val == t1.Val { - // Found a match for this attribute, continue with the next attribute. - continue compareAttributes - } - } - // If we get here, there is no attribute that matches a. - // Therefore the element is not identical to the new one. + if !slices.Equal(n.Attr, attr) { continue findIdenticalElements } @@ -382,7 +379,11 @@ findIdenticalElements: } } - p.afe = append(p.afe, p.top()) + // Sort the attributes to optimize future identical-element searches. + top := p.top() + slices.SortFunc(top.Attr, attrCompare) + + p.afe = append(p.afe, top) } // Section 12.2.4.3. @@ -454,21 +455,6 @@ func (p *parser) resetInsertionMode() { } switch n.DataAtom { - case a.Select: - if !last { - for ancestor, first := n, p.oe[0]; ancestor != first; { - ancestor = p.oe[p.oe.index(ancestor)-1] - switch ancestor.DataAtom { - case a.Template: - p.im = inSelectIM - return - case a.Table: - p.im = inSelectInTableIM - return - } - } - } - p.im = inSelectIM case a.Td, a.Th: // TODO: remove this divergence from the HTML5 spec. // @@ -996,7 +982,10 @@ func inBodyIM(p *parser) bool { p.popUntil(buttonScope, a.P) p.addElement() case a.Button: - p.popUntil(defaultScope, a.Button) + if p.elementInScope(defaultScope, a.Button) { + p.generateImpliedEndTags() + p.popUntil(defaultScope, a.Button) + } p.reconstructActiveFormattingElements() p.addElement() p.framesetOK = false @@ -1034,7 +1023,18 @@ func inBodyIM(p *parser) bool { p.framesetOK = false p.im = inTableIM return true - case a.Area, a.Br, a.Embed, a.Img, a.Input, a.Keygen, a.Wbr: + case a.Area, a.Br, a.Embed, a.Img, a.Keygen, a.Wbr: + p.reconstructActiveFormattingElements() + p.addElement() + p.oe.pop() + p.acknowledgeSelfClosingTag() + p.framesetOK = false + case a.Input: + if p.fragment && p.context.DataAtom == a.Select { + // Ignore the token. + return true + } + p.popUntil(defaultScope, a.Select) p.reconstructActiveFormattingElements() p.addElement() p.oe.pop() @@ -1055,7 +1055,13 @@ func inBodyIM(p *parser) bool { p.oe.pop() p.acknowledgeSelfClosingTag() case a.Hr: - p.popUntil(buttonScope, a.P) + if p.elementInScope(buttonScope, a.P) { + p.generateImpliedEndTags("p") + p.popUntil(defaultScope, a.P) + } + if p.elementInScope(defaultScope, a.Select) { + p.generateImpliedEndTags() + } p.addElement() p.oe.pop() p.acknowledgeSelfClosingTag() @@ -1089,13 +1095,30 @@ func inBodyIM(p *parser) bool { // Don't let the tokenizer go into raw text mode when scripting is disabled. p.tokenizer.NextIsNotRawText() case a.Select: + if p.fragment && p.context.DataAtom == a.Select { + // Ignore the token. + return true + } else if p.popUntil(defaultScope, a.Select) { + return true + } p.reconstructActiveFormattingElements() p.addElement() p.framesetOK = false - p.im = inSelectIM return true - case a.Optgroup, a.Option: - if p.top().DataAtom == a.Option { + case a.Option: + if p.elementInScope(defaultScope, a.Select) { + p.generateImpliedEndTags("optgroup") + // If oe has option element in scope, parse error? + } else if p.top().DataAtom == a.Option { + p.oe.pop() + } + p.reconstructActiveFormattingElements() + p.addElement() + case a.Optgroup: + if p.elementInScope(defaultScope, a.Select) { + p.generateImpliedEndTags() + // If oe has option or optgroup element in scope, parse error? + } else if p.top().DataAtom == a.Option { p.oe.pop() } p.reconstructActiveFormattingElements() @@ -1143,7 +1166,12 @@ func inBodyIM(p *parser) bool { return false } return true - case a.Address, a.Article, a.Aside, a.Blockquote, a.Button, a.Center, a.Details, a.Dialog, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Listing, a.Main, a.Menu, a.Nav, a.Ol, a.Pre, a.Search, a.Section, a.Summary, a.Ul: + case a.Address, a.Article, a.Aside, a.Blockquote, a.Button, a.Center, a.Details, a.Dialog, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Listing, a.Main, a.Menu, a.Nav, a.Ol, a.Pre, a.Search, a.Section, a.Select, a.Summary, a.Ul: + if !p.elementInScope(defaultScope, p.tok.DataAtom) { + // Ignore the token. + return true + } + p.generateImpliedEndTags() p.popUntil(defaultScope, p.tok.DataAtom) case a.Form: if p.oe.contains(a.Template) { @@ -1372,8 +1400,6 @@ func (p *parser) inBodyEndTagFormatting(tagAtom a.Atom, tagName string) { } // inBodyEndTagOther performs the "any other end tag" algorithm for inBodyIM. -// "Any other end tag" handling from 12.2.6.5 The rules for parsing tokens in foreign content -// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inforeign func (p *parser) inBodyEndTagOther(tagAtom a.Atom, tagName string) { for i := len(p.oe) - 1; i >= 0; i-- { // Two element nodes have the same tag if they have the same Data (a @@ -1383,7 +1409,7 @@ func (p *parser) inBodyEndTagOther(tagAtom a.Atom, tagName string) { // Uncommon (custom) tags get a zero DataAtom. // // The if condition here is equivalent to (p.oe[i].Data == tagName). - if (p.oe[i].DataAtom == tagAtom) && + if p.oe[i].Namespace == "" && (p.oe[i].DataAtom == tagAtom) && ((tagAtom != 0) || (p.oe[i].Data == tagName)) { p.oe = p.oe[:i] break @@ -1484,17 +1510,6 @@ func inTableIM(p *parser) bool { } p.addElement() p.form = p.oe.pop() - case a.Select: - p.reconstructActiveFormattingElements() - switch p.top().DataAtom { - case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr: - p.fosterParenting = true - } - p.addElement() - p.fosterParenting = false - p.framesetOK = false - p.im = inSelectInTableIM - return true } case EndTagToken: switch p.tok.DataAtom { @@ -1543,12 +1558,6 @@ func inCaptionIM(p *parser) bool { p.clearActiveFormattingElements() p.im = inTableIM return false - case a.Select: - p.reconstructActiveFormattingElements() - p.addElement() - p.framesetOK = false - p.im = inSelectInTableIM - return true } case EndTagToken: switch p.tok.DataAtom { @@ -1758,12 +1767,6 @@ func inCellIM(p *parser) bool { } // Ignore the token. return true - case a.Select: - p.reconstructActiveFormattingElements() - p.addElement() - p.framesetOK = false - p.im = inSelectInTableIM - return true } case EndTagToken: switch p.tok.DataAtom { @@ -1794,118 +1797,6 @@ func inCellIM(p *parser) bool { return inBodyIM(p) } -// Section 12.2.6.4.16. -func inSelectIM(p *parser) bool { - switch p.tok.Type { - case TextToken: - p.addText(strings.Replace(p.tok.Data, "\x00", "", -1)) - case StartTagToken: - switch p.tok.DataAtom { - case a.Html: - return inBodyIM(p) - case a.Option: - if p.top().DataAtom == a.Option { - p.oe.pop() - } - p.addElement() - case a.Optgroup: - if p.top().DataAtom == a.Option { - p.oe.pop() - } - if p.top().DataAtom == a.Optgroup { - p.oe.pop() - } - p.addElement() - case a.Select: - if !p.popUntil(selectScope, a.Select) { - // Ignore the token. - return true - } - p.resetInsertionMode() - case a.Input, a.Keygen, a.Textarea: - if p.elementInScope(selectScope, a.Select) { - p.parseImpliedToken(EndTagToken, a.Select, a.Select.String()) - return false - } - // In order to properly ignore