From a0278163d53910857b4c779843b0da1f21eadcde Mon Sep 17 00:00:00 2001 From: Elisha Mbadi Date: Mon, 27 Jul 2026 14:12:08 +0300 Subject: [PATCH 1/3] update to committer & orderer v1.0.0 - update templates.go & config/samples to reflect new versions - update to fabric-x-common v 0.2.6 - add consenters to genesis block in genesis_service.go - add configurable PVC sizes to configs - in shared_config.go. Ensure RequestBatchMaxBytes > RequestMaxBytes --- api/v1alpha1/orderergroup_types.go | 6 + .../fabricx.kfsoft.tech_orderergroups.yaml | 3 + config/samples/fabricx_v1alpha1_ca_org1.yaml | 4 +- .../samples/fabricx_v1alpha1_committer.yaml | 2 + ...abricx_v1alpha1_committerqueryservice.yaml | 4 +- config/samples/fabricx_v1alpha1_endorser.yaml | 5 +- .../samples/fabricx_v1alpha1_namespace.yaml | 14 +- .../fabricx_v1alpha1_orderergroup.yaml | 50 +-- ...cx_v1alpha1_orderergroup_sans_example.yaml | 8 +- ...bricx_v1alpha1_orderergroup_with_sans.yaml | 15 +- go.mod | 57 +-- go.sum | 360 +++--------------- internal/controller/certs/ca_provision.go | 29 +- .../controller/chainnamespace_controller.go | 6 +- internal/controller/committer_controller.go | 29 +- .../committercoordinator_controller.go | 4 +- .../committerqueryservice_controller.go | 21 +- .../controller/committersidecar_controller.go | 142 ++++++- .../committervalidator_controller.go | 4 +- .../committerverifier_controller.go | 4 +- .../controller/genesis/genesis_service.go | 68 +++- .../genesis/genesis_service_test.go | 106 +++++- internal/controller/genesis/shared_config.go | 4 +- .../controller/ordererassembler_controller.go | 9 +- .../controller/ordererbatcher_controller.go | 2 +- .../controller/ordererconsenter_controller.go | 2 +- .../controller/orderergroup_controller.go | 6 + .../controller/ordererrouter_controller.go | 8 +- internal/controller/utils/templates.go | 162 ++++---- 29 files changed, 577 insertions(+), 557 deletions(-) diff --git a/api/v1alpha1/orderergroup_types.go b/api/v1alpha1/orderergroup_types.go index 1284eb1..ee425e7 100644 --- a/api/v1alpha1/orderergroup_types.go +++ b/api/v1alpha1/orderergroup_types.go @@ -579,6 +579,9 @@ type BatcherInstance struct { // Shard ID for this batcher instance ShardID int32 `json:"shardID"` + // PVC storage size for the batcher + PVCStorageSize string `json:"pvcStorageSize,omitempty"` + // Component-specific ingress configuration Ingress *IngressConfig `json:"ingress,omitempty"` @@ -609,6 +612,9 @@ type ConsenterInstance struct { // Consenter ID for this consenter instance ConsenterID int32 `json:"consenterID"` + // PVC storage size for the consenter + PVCStorageSize string `json:"pvcStorageSize,omitempty"` + // Component-specific ingress configuration Ingress *IngressConfig `json:"ingress,omitempty"` diff --git a/config/crd/bases/fabricx.kfsoft.tech_orderergroups.yaml b/config/crd/bases/fabricx.kfsoft.tech_orderergroups.yaml index 9b3999d..11a0a7c 100644 --- a/config/crd/bases/fabricx.kfsoft.tech_orderergroups.yaml +++ b/config/crd/bases/fabricx.kfsoft.tech_orderergroups.yaml @@ -3194,6 +3194,9 @@ spec: type: string description: Pod labels type: object + pvcStorageSize: + description: PVC storage size for the batcher + type: string replicas: description: Replicas for this component format: int32 diff --git a/config/samples/fabricx_v1alpha1_ca_org1.yaml b/config/samples/fabricx_v1alpha1_ca_org1.yaml index a6539d1..52d02d9 100644 --- a/config/samples/fabricx_v1alpha1_ca_org1.yaml +++ b/config/samples/fabricx_v1alpha1_ca_org1.yaml @@ -54,12 +54,12 @@ spec: type: sqlite3 image: hyperledger/fabric-ca ingress: - istio: + gateway: hosts: - ca.org1.localho.st - ca.org1.example.com - ingressGateway: istio-ingressgateway port: 443 + ingressGateway: istio-ingressgateway tls: enabled: true secretName: org1-ca-tls-secret diff --git a/config/samples/fabricx_v1alpha1_committer.yaml b/config/samples/fabricx_v1alpha1_committer.yaml index 7b31d4a..c80bc4e 100644 --- a/config/samples/fabricx_v1alpha1_committer.yaml +++ b/config/samples/fabricx_v1alpha1_committer.yaml @@ -53,6 +53,8 @@ spec: value: INFO sidecar: replicas: 1 + storage: + size: 1Gi resources: requests: cpu: 300m diff --git a/config/samples/fabricx_v1alpha1_committerqueryservice.yaml b/config/samples/fabricx_v1alpha1_committerqueryservice.yaml index b1ba443..73ff737 100644 --- a/config/samples/fabricx_v1alpha1_committerqueryservice.yaml +++ b/config/samples/fabricx_v1alpha1_committerqueryservice.yaml @@ -67,11 +67,9 @@ spec: # Ingress configuration with Istio ingress: istio: - enabled: true hosts: - query-service.example.com - ingressGateway: ingressgateway - port: 443 + gateway: ingressgateway # Enrollment configuration for certificates enrollment: diff --git a/config/samples/fabricx_v1alpha1_endorser.yaml b/config/samples/fabricx_v1alpha1_endorser.yaml index d6b19d2..1fbce7c 100644 --- a/config/samples/fabricx_v1alpha1_endorser.yaml +++ b/config/samples/fabricx_v1alpha1_endorser.yaml @@ -216,10 +216,7 @@ spec: istio: hosts: - endorser1.localho.st - ingressGateway: istio-ingressgateway - port: 443 - tls: - enabled: true + gateway: istio-ingressgateway --- # Example: PVC for SQLite database (if using persistentVolumeClaim above) diff --git a/config/samples/fabricx_v1alpha1_namespace.yaml b/config/samples/fabricx_v1alpha1_namespace.yaml index 1fb086a..c6de615 100644 --- a/config/samples/fabricx_v1alpha1_namespace.yaml +++ b/config/samples/fabricx_v1alpha1_namespace.yaml @@ -12,18 +12,20 @@ spec: # Orderer endpoint to broadcast the namespace transaction orderer: "orderer.example.com:7050" - # CA certificate reference - caCert: - name: "orderer-ca-cert" - namespace: "default" - key: "ca.crt" + # TLS configuration for orderer connection + tls: + enabled: true + caCert: + name: "orderer-ca-cert" + namespace: "default" + key: "ca.crt" # MSP ID mspID: "Org1MSP" # Identity (cert + key) reference identity: - name: "org1-admin-identity" + name: "org1-admin" namespace: "default" key: "identity.pem" diff --git a/config/samples/fabricx_v1alpha1_orderergroup.yaml b/config/samples/fabricx_v1alpha1_orderergroup.yaml index f1c0a5f..dbb5fce 100644 --- a/config/samples/fabricx_v1alpha1_orderergroup.yaml +++ b/config/samples/fabricx_v1alpha1_orderergroup.yaml @@ -39,10 +39,9 @@ spec: secretName: fabric-x-certs components: assembler: - certificates: - sans: - dnsNames: - - assembler-org1.localho.st + sans: + dnsNames: + - assembler-org1.localho.st env: - name: ASSEMBLER_MAX_MESSAGE_COUNT value: '500' @@ -50,15 +49,13 @@ spec: istio: hosts: - assembler-org1.localho.st - ingressGateway: istio-ingressgateway - port: 443 + gateway: istio-ingressgateway replicas: 1 batchers: - shardID: 1 - certificates: - sans: - dnsNames: - - batcher-1-org1.localho.st + sans: + dnsNames: + - batcher-1-org1.localho.st env: - name: BATCHER_TIMEOUT value: 2s @@ -68,8 +65,7 @@ spec: istio: hosts: - batcher-1-org1.localho.st - ingressGateway: istio-ingressgateway - port: 443 + gateway: istio-ingressgateway podLabels: fabric-x/batcher-type: high-throughput fabric-x/shard-id: '1' @@ -80,10 +76,9 @@ spec: operator: Equal value: batcher - shardID: 2 - certificates: - sans: - dnsNames: - - batcher-2-org1.localho.st + sans: + dnsNames: + - batcher-2-org1.localho.st env: - name: BATCHER_TIMEOUT value: 2s @@ -93,8 +88,7 @@ spec: istio: hosts: - batcher-2-org1.localho.st - ingressGateway: istio-ingressgateway - port: 443 + gateway: istio-ingressgateway podLabels: fabric-x/batcher-type: high-throughput fabric-x/shard-id: '2' @@ -106,10 +100,9 @@ spec: value: batcher consenter: consenterID: 1 - certificates: - sans: - dnsNames: - - consenter-1-org1.localho.st + sans: + dnsNames: + - consenter-1-org1.localho.st endpoints: - consenter-1-org1.localho.st:7050 env: @@ -121,8 +114,7 @@ spec: istio: hosts: - consenter-1-org1.localho.st - ingressGateway: istio-ingressgateway - port: 443 + gateway: istio-ingressgateway podAnnotations: fabric-x/consenter-batch-size: '500' fabric-x/consenter-type: raft @@ -130,10 +122,9 @@ spec: volumeMounts: [] volumes: [] router: - certificates: - sans: - dnsNames: - - router-org1.localho.st + sans: + dnsNames: + - router-org1.localho.st env: - name: ROUTER_CHANNEL_BUFFER_SIZE value: '1000' @@ -142,8 +133,7 @@ spec: istio: hosts: - router-org1.localho.st - ingressGateway: istio-ingressgateway - port: 443 + gateway: istio-ingressgateway replicas: 1 volumes: - name: router-cache diff --git a/config/samples/fabricx_v1alpha1_orderergroup_sans_example.yaml b/config/samples/fabricx_v1alpha1_orderergroup_sans_example.yaml index eec9ef2..d29d5a7 100644 --- a/config/samples/fabricx_v1alpha1_orderergroup_sans_example.yaml +++ b/config/samples/fabricx_v1alpha1_orderergroup_sans_example.yaml @@ -24,10 +24,9 @@ spec: secretKey: "genesis.block" enrollment: sign: - cahost: "ca.example.com" - # CA configuration for signing certificates ca: caname: "ca-org1" + cahost: "ca.example.com" caport: 7054 enrollid: "admin" enrollsecret: "adminpw" @@ -37,7 +36,6 @@ spec: name: "ca-tls-secret" key: "ca.crt" namespace: "default" - # SANS configuration for signing certificates sans: dnsNames: - "orderer1.example.com" @@ -47,10 +45,9 @@ spec: - "192.168.1.10" - "10.0.0.10" tls: - cahost: "ca.example.com" - # CA configuration for TLS certificates ca: caname: "tls-ca-org1" + cahost: "ca.example.com" caport: 7054 enrollid: "tls-admin" enrollsecret: "tls-adminpw" @@ -60,7 +57,6 @@ spec: name: "tls-ca-secret" key: "tls-ca.crt" namespace: "default" - # SANS configuration for TLS certificates sans: dnsNames: - "orderer1-tls.example.com" diff --git a/config/samples/fabricx_v1alpha1_orderergroup_with_sans.yaml b/config/samples/fabricx_v1alpha1_orderergroup_with_sans.yaml index 66fd4a7..1d2b20e 100644 --- a/config/samples/fabricx_v1alpha1_orderergroup_with_sans.yaml +++ b/config/samples/fabricx_v1alpha1_orderergroup_with_sans.yaml @@ -85,8 +85,7 @@ spec: istio: hosts: - assembler-custom.localho.st - ingressGateway: ingressgateway - port: 443 + gateway: ingressgateway replicas: 1 batchers: - shardID: 1 @@ -138,8 +137,7 @@ spec: istio: hosts: - batcher-1-custom.localho.st - ingressGateway: ingressgateway - port: 443 + gateway: ingressgateway podLabels: fabric-x/batcher-type: high-throughput fabric-x/shard-id: '1' @@ -198,8 +196,7 @@ spec: istio: hosts: - batcher-2-custom.localho.st - ingressGateway: ingressgateway - port: 443 + gateway: ingressgateway podLabels: fabric-x/batcher-type: high-throughput fabric-x/shard-id: '2' @@ -261,8 +258,7 @@ spec: istio: hosts: - consenter-custom.localho.st - ingressGateway: ingressgateway - port: 443 + gateway: ingressgateway podAnnotations: fabric-x/consenter-batch-size: '500' fabric-x/consenter-type: raft @@ -317,8 +313,7 @@ spec: istio: hosts: - router-custom.localho.st - ingressGateway: ingressgateway - port: 443 + gateway: ingressgateway replicas: 1 volumes: - name: router-cache diff --git a/go.mod b/go.mod index 0292367..be3396d 100644 --- a/go.mod +++ b/go.mod @@ -1,14 +1,14 @@ module github.com/kfsoftware/fabric-x-operator -go 1.25.5 +go 1.26.3 require ( github.com/docker/docker v28.5.2+incompatible github.com/hyperledger/fabric-config v0.3.0 - github.com/hyperledger/fabric-lib-go v1.1.3-0.20240523144151-25edd1eaf5f5 + github.com/hyperledger/fabric-lib-go v1.1.5-0.20260607181445-fc4b05c5d38f github.com/hyperledger/fabric-protos-go-apiv2 v0.3.7 - github.com/onsi/ginkgo/v2 v2.27.2 - github.com/onsi/gomega v1.38.2 + github.com/onsi/ginkgo/v2 v2.28.3 + github.com/onsi/gomega v1.40.0 github.com/stretchr/testify v1.11.1 github.com/testcontainers/testcontainers-go v0.39.0 gopkg.in/yaml.v2 v2.4.0 @@ -46,11 +46,12 @@ require ( github.com/docker/go-units v0.5.0 // indirect github.com/ebitengine/purego v0.8.4 // indirect github.com/getsentry/sentry-go v0.27.0 // indirect - github.com/go-kit/kit v0.10.0 // indirect - github.com/go-logfmt/logfmt v0.5.1 // indirect + github.com/go-kit/kit v0.13.0 // indirect + github.com/go-kit/log v0.2.1 // indirect + github.com/go-logfmt/logfmt v0.6.1 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-sql-driver/mysql v1.5.0 // indirect - github.com/go-viper/mapstructure/v2 v2.4.0 // indirect + github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/certificate-transparency-go v1.0.21 // indirect github.com/gorilla/handlers v1.5.2 // indirect @@ -72,7 +73,7 @@ require ( github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/magiconair/properties v1.8.10 // indirect github.com/mattn/go-sqlite3 v1.14.18 // indirect - github.com/miekg/pkcs11 v1.1.1 // indirect + github.com/miekg/pkcs11 v1.1.2 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/go-archive v0.1.0 // indirect @@ -109,14 +110,14 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/crypto v0.47.0 // indirect - golang.org/x/mod v0.32.0 // indirect + golang.org/x/crypto v0.51.0 // indirect + golang.org/x/mod v0.36.0 // indirect gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect gopkg.in/ldap.v2 v2.5.1 // indirect ) require ( - cel.dev/expr v0.24.0 // indirect + cel.dev/expr v0.25.1 // indirect github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect @@ -140,11 +141,11 @@ require ( github.com/google/cel-go v0.23.2 // indirect github.com/google/gnostic-models v0.6.9 // indirect github.com/google/go-cmp v0.7.0 // indirect - github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 // indirect + github.com/google/pprof v0.0.0-20260402051712-545e8a4df936 // indirect github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.4 // indirect github.com/hyperledger/fabric-ca v1.5.15 - github.com/hyperledger/fabric-x-common v0.1.1-0.20260219094834-26c5a49ed548 + github.com/hyperledger/fabric-x-common v0.2.6 github.com/hyperledger/fabric-x-orderer v0.0.24 github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -164,28 +165,28 @@ require ( github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect - go.opentelemetry.io/otel v1.38.0 // indirect + go.opentelemetry.io/otel v1.43.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 // indirect - go.opentelemetry.io/otel/metric v1.38.0 // indirect - go.opentelemetry.io/otel/sdk v1.38.0 // indirect - go.opentelemetry.io/otel/trace v1.38.0 // indirect + go.opentelemetry.io/otel/metric v1.43.0 // indirect + go.opentelemetry.io/otel/sdk v1.43.0 // indirect + go.opentelemetry.io/otel/trace v1.43.0 // indirect go.opentelemetry.io/proto/otlp v1.7.1 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.1 // indirect + go.uber.org/zap v1.28.0 // indirect golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect - golang.org/x/net v0.49.0 // indirect - golang.org/x/oauth2 v0.34.0 // indirect - golang.org/x/sync v0.19.0 // indirect - golang.org/x/sys v0.40.0 // indirect - golang.org/x/term v0.39.0 // indirect - golang.org/x/text v0.33.0 // indirect + golang.org/x/net v0.54.0 // indirect + golang.org/x/oauth2 v0.36.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/sys v0.44.0 // indirect + golang.org/x/term v0.43.0 // indirect + golang.org/x/text v0.37.0 // indirect golang.org/x/time v0.14.0 // indirect - golang.org/x/tools v0.41.0 // indirect + golang.org/x/tools v0.45.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260120221211-b8f7ae30c516 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 // indirect - google.golang.org/grpc v1.78.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260226221140-a57be14db171 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect + google.golang.org/grpc v1.81.1 // indirect google.golang.org/protobuf v1.36.11 gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/go.sum b/go.sum index 3d67e9d..316768e 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,7 @@ bitbucket.org/liamstask/goose v0.0.0-20150115234039-8488cc47d90c/go.mod h1:hSVuE3qU7grINVSwrmzHfpg9k87ALBk+XaualNyUzI4= -cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY= -cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= +cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= +cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= @@ -28,52 +27,28 @@ github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1 github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= -github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= -github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= -github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= -github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3MdfoPyRVU= github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce h1:YtWJF7RHm2pYCvA5t0RPmAaLUhREsKuKd+SLhxFbFeQ= github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= -github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= -github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= -github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/certifi/gocertifi v0.0.0-20180118203423-deb3ae2ef261/go.mod h1:GJKEexRPVJrBSOjoqN5VNOIKJ5Q3RViH6eu3puDRwx4= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/backoff v0.0.0-20161212185259-647f3cdfc87a/go.mod h1:rzgs2ZOiguV6/NpiDgADjRLPNyZlApIWxKpkT+X8SdY= github.com/cloudflare/cfssl v1.4.1 h1:vScfU2DrIUI9VPHBVeeAQ0q5A+9yshO1Gz+3QoUQiKw= @@ -81,14 +56,12 @@ github.com/cloudflare/cfssl v1.4.1/go.mod h1:KManx/OJPb5QY+y0+o/898AMcM128sF0bUR github.com/cloudflare/go-metrics v0.0.0-20151117154305-6a9aea36fb41/go.mod h1:eaZPlJWD+G9wseg1BuRXlHnjntPMrywMsyxf+LTOdP4= github.com/cloudflare/redoctober v0.0.0-20171127175943-746a508df14c/go.mod h1:6Se34jNoqrd8bTxrmJB2Bg2aoZ2CdSXonils9NsiNgo= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/cockroachdb/errors v1.12.0 h1:d7oCs6vuIMUQRVbi6jWWWEJZahLCfJpnJSVobd1/sUo= github.com/cockroachdb/errors v1.12.0/go.mod h1:SvzfYNNBshAVbZ8wzNc/UPK3w1vf0dKDUP41ucAIf7g= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= -github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/consensys/gnark-crypto v0.19.2 h1:qrEAIXq3T4egxqiliFFoNrepkIWVEeIYwt3UL0fvS80= github.com/consensys/gnark-crypto v0.19.2/go.mod h1:rT23F0XSZqE0mUA0+pRtnL56IbPxs6gp4CeRsBk4XS0= github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= @@ -99,14 +72,9 @@ github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= @@ -115,7 +83,6 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM= @@ -124,16 +91,10 @@ github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pM github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= -github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw= github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= -github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/emicklei/go-restful/v3 v3.12.0 h1:y2DdzBAURM29NFF94q6RaY4vjIH1rtwDapwQtU84iWk= github.com/emicklei/go-restful/v3 v3.12.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -142,11 +103,8 @@ github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= -github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -159,7 +117,6 @@ github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXE github.com/getsentry/raven-go v0.0.0-20180121060056-563b81fc02b7/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs= github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo= github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M= @@ -168,16 +125,14 @@ github.com/gkampitakis/go-snaps v0.5.15 h1:amyJrvM1D33cPHwVrjo9jQxX8g/7E2wYdZ+01 github.com/gkampitakis/go-snaps v0.5.15/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc= 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-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.10.0 h1:dXFJfIHVvUcpSgDOV+Ne6t7jXri8Tfv2uOLHUZ2XNuo= -github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= +github.com/go-kit/kit v0.13.0 h1:OoneCcHKHQ03LfBpoQCUfCluwd2Vt3ohz+kvbJneZAU= +github.com/go-kit/kit v0.13.0/go.mod h1:phqEHMMUbyrCFCTgH48JueqrM3md2HcAZ8N3XE4FKDg= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= +github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logfmt/logfmt v0.6.1 h1:4hvbpePJKnIzH1B+8OR/JPbTx37NktoI9LE2QZBBkvE= +github.com/go-logfmt/logfmt v0.6.1/go.mod h1:EV2pOAQoZaT1ZXZbqDl5hrymndi4SY9ED9/z6CO0XAk= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -197,24 +152,17 @@ github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= -github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= +github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= -github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -228,9 +176,6 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/cel-go v0.23.2 h1:UdEe3CvQh3Nv+E/j9r1Y//WO0K0cSyD7/y0bzyLIMI4= @@ -250,52 +195,22 @@ github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 h1:EEHtgt9IwisQ2AZ4pIsMjahcegHh6rmhqxzIRQIyepY= -github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U= +github.com/google/pprof v0.0.0-20260402051712-545e8a4df936 h1:EwtI+Al+DeppwYX2oXJCETMO23COyaKGP6fHVpkpWpg= +github.com/google/pprof v0.0.0-20260402051712-545e8a4df936/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= -github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/grantae/certinfo v0.0.0-20170412194111-59d56a35515b h1:NGgE5ELokSf2tZ/bydyDUKrvd/jP8lrAoPNeBuMOTOk= github.com/grantae/certinfo v0.0.0-20170412194111-59d56a35515b/go.mod h1:zT/uzhdQGTqlwTq7Lpbj3JoJQWfPfIJ1tE0OidAmih8= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.4 h1:kEISI/Gx67NzH3nJxAmY/dGac80kKZgZt134u7Y/k1s= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.4/go.mod h1:6Nz966r3vQYCqIzWsuEl9d7cf7mRhtDmm++sOxlnfxI= -github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= -github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/hyperledger-labs/SmartBFT v0.0.0-20251222105915-424e45b7a9fb h1:PE6QxDgJ+IXXZiwE4dgAbdR7fP4JRMnk6izsLnCcaG8= github.com/hyperledger-labs/SmartBFT v0.0.0-20251222105915-424e45b7a9fb/go.mod h1:bRqyzSrLUIOwdQjdFvMHOVc3A+Z5pwujWcw2CAxF48s= github.com/hyperledger/aries-bbs-go v0.0.0-20240528084656-761671ea73bc h1:3Ykk6MtyfnlzMOQry9zkxsoLWpCWZwDPqehO/BJwArM= @@ -306,42 +221,32 @@ github.com/hyperledger/fabric-ca v1.5.15 h1:55lQb2B/nWjpZZ8Ke/5dczoSh9juHT3OgrUF github.com/hyperledger/fabric-ca v1.5.15/go.mod h1:hNlaUH2hxoVHaapBdRPa2paSHaST+TCxbqQ5xNQQYfo= github.com/hyperledger/fabric-config v0.3.0 h1:FS5/dc9GAniljP6RYxQRG92AaiBVoN2vTvtOvnWqeQs= github.com/hyperledger/fabric-config v0.3.0/go.mod h1:kSevTn78K83Suc++JsEo7Nt1tYIPqDajW+ORz3OhWlg= -github.com/hyperledger/fabric-lib-go v1.1.3-0.20240523144151-25edd1eaf5f5 h1:RPWTL5wxAb+xDOrsCU3QYZP65305F8v3PaOyzdbPVMU= -github.com/hyperledger/fabric-lib-go v1.1.3-0.20240523144151-25edd1eaf5f5/go.mod h1:SHNCq8AB0VpHAmvJEtdbzabv6NNV1F48JdmDihasBjc= +github.com/hyperledger/fabric-lib-go v1.1.5-0.20260607181445-fc4b05c5d38f h1:o2JaCIczHd6VxUKvv3uZkEckVziGVP6kGZugLv8Mmpc= +github.com/hyperledger/fabric-lib-go v1.1.5-0.20260607181445-fc4b05c5d38f/go.mod h1:amVM/GTZB4fhzHdSk8MEHpoC3N34zCIvkWjFX70wZiA= github.com/hyperledger/fabric-protos-go-apiv2 v0.3.7 h1:sQ5qv8vQQfwewa1JlCiSCC8dLElmaU2/frLolpgibEY= github.com/hyperledger/fabric-protos-go-apiv2 v0.3.7/go.mod h1:bJnwzfv03oZQeCc863pdGTDgf5nmCy6Za3RAE7d2XsQ= -github.com/hyperledger/fabric-x-common v0.1.1-0.20260219094834-26c5a49ed548 h1:KTmH/ZtM43z39lcLxGm7G3I5iC7J7iZSVHqbCFcUUNo= -github.com/hyperledger/fabric-x-common v0.1.1-0.20260219094834-26c5a49ed548/go.mod h1:+VPYRRCGAZo7+rlT55yK3aRmUbRJwQGlWg7lz0SLdMY= +github.com/hyperledger/fabric-x-common v0.2.6 h1:nmrWvLC9oM91Xff1s5ae0hjWjX7fnAW4p3GLD/wkXZY= +github.com/hyperledger/fabric-x-common v0.2.6/go.mod h1:xkAVXn2qEb/JMFOI8MrCMJC+rg0u8ip4tZz/2cAEXAk= github.com/hyperledger/fabric-x-orderer v0.0.24 h1:otX8Z+U7yrj8puNHuFbruhFHy2pXQuf2w1HAtemOW8k= github.com/hyperledger/fabric-x-orderer v0.0.24/go.mod h1:IYsGkjkTSSUGgod6gFvfUPlPYOjIC6RC0AJ6BExu9LI= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmhodges/clock v0.0.0-20160418191101-880ee4c33548 h1:dYTbLf4m0a5u0KLmPfB6mgxbcV7588bOCx79hxa5Sr4= github.com/jmhodges/clock v0.0.0-20160418191101-880ee4c33548/go.mod h1:hGT6jSUVzF6no3QaDSMLGLEHtHSBSefs+MgcDWnmhmo= github.com/jmoiron/sqlx v0.0.0-20180124204410-05cef0741ade/go.mod h1:IiEW3SEiiErVyFdH8NTuWjSifiEQKUoyK3LNqr2kCHU= github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE= github.com/joshdk/go-junit v1.0.0/go.mod h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kilic/bls12-381 v0.1.0 h1:encrdjqKMEvabVQ7qYOKu1OvhqpK4s47wDYtNiPtlp4= github.com/kilic/bls12-381 v0.1.0/go.mod h1:vDTTHJONJ6G+P2R74EhnyotQDTliQDnFEwhdmfzw1ig= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/sqlstruct v0.0.0-20150923205031-648daed35d49/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE= @@ -351,7 +256,6 @@ github.com/kisom/goutils v1.1.0/go.mod h1:+UBTfd78habUYWFbNWTJNG+jNG/i/lGURakr4A github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -368,38 +272,22 @@ github.com/lib/pq v0.0.0-20180201184707-88edab080323/go.mod h1:5WUZQaWbwv1U+lTRe github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg= github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= -github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= -github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.14.18 h1:JL0eqdCOq6DJVNPSvArO/bIV9/P7fbGrV00LZHc+5aI= github.com/mattn/go-sqlite3 v1.14.18/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE= github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU= -github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/miekg/pkcs11 v1.1.2 h1:/VxmeAX5qU6Q3EwafypogwWbYryHFmF2RpkJmw3m4MQ= +github.com/miekg/pkcs11 v1.1.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= @@ -421,8 +309,6 @@ github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= @@ -430,56 +316,30 @@ github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7P github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= -github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= -github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= -github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= -github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= -github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= -github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns= -github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/ginkgo/v2 v2.28.3 h1:4JvMdwtFU0imd8fHx25OJXoDMRexnf8v5NHKYSTTji4= +github.com/onsi/ginkgo/v2 v2.28.3/go.mod h1:+aXOY+vzZ5mu2iI2HpTZUPmM//oQfsNFX6gU9kNcA44= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= -github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= +github.com/onsi/gomega v1.40.0 h1:Vtol0e1MghCD2ZVIilPDIg44XSL9l2QAn8ZNaljWcJc= +github.com/onsi/gomega v1.40.0/go.mod h1:M/Uqpu/8qTjtzCLUA2zJHX9Iilrau25x1PdoSRbWh5A= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= -github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= -github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= -github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= -github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= -github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= -github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= -github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= @@ -487,72 +347,41 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs= github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= -github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= -github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shirou/gopsutil/v4 v4.25.6 h1:kLysI2JsKorfaFPcYmcJqbzROzsBWEOAtw6A7dIfqXs= github.com/shirou/gopsutil/v4 v4.25.6/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= -github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -560,9 +389,6 @@ github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= github.com/stoewer/go-strcase v1.3.1 h1:iS0MdW+kVTxgMoE1LAZyMiYJFKlOzLooE4MxjirtkAs= github.com/stoewer/go-strcase v1.3.1/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= -github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -597,9 +423,6 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/weppos/publicsuffix-go v0.4.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k= @@ -607,7 +430,6 @@ github.com/weppos/publicsuffix-go v0.5.0 h1:rutRtjBJViU/YjcI5d80t4JAVvDltS6bciJg github.com/weppos/publicsuffix-go v0.5.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= @@ -619,131 +441,98 @@ github.com/zmap/zcrypto v0.0.0-20190729165852-9051775e6a2e h1:mvOa4+/DXStR4ZXOks github.com/zmap/zcrypto v0.0.0-20190729165852-9051775e6a2e/go.mod h1:w7kd3qXHh8FNaczNjslXqvFQiv5mMWRXlL9klTUAHc8= github.com/zmap/zlint v0.0.0-20190806154020-fd021b4cfbeb h1:vxqkjztXSaPVDc8FQCdHTaejm2x747f6yPbnu1h2xkg= github.com/zmap/zlint v0.0.0-20190806154020-fd021b4cfbeb/go.mod h1:29UiAJNsiVdvTBFCJW8e3q6dcDbOoPkhMgttOSCIMMY= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= -go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= -go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= -go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= +go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 h1:aTL7F04bJHUlztTsNGJ2l+6he8c+y/b//eR0jjjemT4= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0/go.mod h1:kldtb7jDTeol0l3ewcmd8SDvx3EmIE7lyvqbasU3QC4= -go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= -go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= -go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= -go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= -go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= -go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= -go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= -go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= +go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= +go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= +go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= +go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= +go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= +go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= +go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4= go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 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/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.12.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= -go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= -go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= +go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= 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.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= -golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= +golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI= +golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU= golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= -golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= +golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4= +golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= -golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= +golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w= +golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= -golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -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/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -755,75 +544,56 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= -golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= -golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= +golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ= +golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= +golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= -golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= -golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= +golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8= +golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= -gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto/googleapis/api v0.0.0-20260120221211-b8f7ae30c516 h1:vmC/ws+pLzWjj/gzApyoZuSVrDtF1aod4u/+bbj8hgM= -google.golang.org/genproto/googleapis/api v0.0.0-20260120221211-b8f7ae30c516/go.mod h1:p3MLuOwURrGBRoEyFHBT3GjUwaCQVKeNqqWxlcISGdw= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 h1:vzOYHDZEHIsPYYnaSYo60AqHkJronSu0rzTz/s4quL0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/genproto/googleapis/api v0.0.0-20260226221140-a57be14db171 h1:tu/dtnW1o3wfaxCOjSLn5IRX4YDcJrtlpzYkhHhGaC4= +google.golang.org/genproto/googleapis/api v0.0.0-20260226221140-a57be14db171/go.mod h1:M5krXqk4GhBKvB596udGL3UyjL4I1+cTbK0orROM9ng= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= -google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= +google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ= +google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -832,29 +602,22 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d h1:TxyelI5cVkbREznMhfzycHdkp5cLA7DpE+GKjSslYhM= gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ldap.v2 v2.5.1 h1:wiu0okdNfjlBzg6UWvd1Hn8Y+Ux17/u/4nlk4CQr6tU= gopkg.in/ldap.v2 v2.5.1/go.mod h1:oI0cpe/D7HRtBQl8aTg+ZmzFUAvu4lsv3eLXMLGFxWk= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -867,7 +630,6 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= @@ -902,7 +664,5 @@ 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/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc= sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= -sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/internal/controller/certs/ca_provision.go b/internal/controller/certs/ca_provision.go index ae41c44..42c7bd6 100644 --- a/internal/controller/certs/ca_provision.go +++ b/internal/controller/certs/ca_provision.go @@ -231,16 +231,17 @@ func RevokeUser(ctx context.Context, k8sClient client.Client, params RevokeUserR } type RegisterUserRequest struct { - TLSCert string - URL string - Name string - MSPID string - EnrollID string - EnrollSecret string - User string - Secret string - Type string - Attributes []api.Attribute + TLSCert string + URL string + Name string + MSPID string + EnrollID string + EnrollSecret string + User string + Secret string + Type string + EnrollmentType string + Attributes []api.Attribute } func RegisterUser(ctx context.Context, k8sClient client.Client, params RegisterUserRequest) (string, error) { @@ -256,12 +257,16 @@ func RegisterUser(ctx context.Context, k8sClient client.Client, params RegisterU if err != nil { return "", err } + enrollType := params.EnrollmentType + if enrollType == "" { + enrollType = params.Type + } enrollResponse, err := caClient.Enroll(&api.EnrollmentRequest{ Name: params.EnrollID, Secret: params.EnrollSecret, CAName: params.Name, AttrReqs: []*api.AttributeRequest{}, - Type: params.Type, + Type: enrollType, }) if err != nil { log.Error(err, "Failed to register user") @@ -736,7 +741,7 @@ func provisionComponentCertificateWithClient(ctx context.Context, k8sClient clie if certType == "tls" { enrollRequest.Profile = "tls" } else { - enrollRequest.Profile = "ca" + enrollRequest.Profile = "" } // Enroll the component diff --git a/internal/controller/chainnamespace_controller.go b/internal/controller/chainnamespace_controller.go index c3a838f..f2460b4 100644 --- a/internal/controller/chainnamespace_controller.go +++ b/internal/controller/chainnamespace_controller.go @@ -349,8 +349,10 @@ func (r *ChainNamespaceReconciler) createSignedEnvelope(signer identity.SignerSe tx.Endorsements = make([]*applicationpb.Endorsements, len(tx.GetNamespaces())) for idx, ns := range tx.GetNamespaces() { // Note that a default msp signer hashes the msg before signing. - // For that reason we use the TxNamespace message as ASN1 encoded msg - msg, err := ns.ASN1Marshal(txID) + // For that reason we use the TxNamespace message as ASN1 encoded msg. + // The verifier reconstructs the same bytes from Tx.Metadata, so we must + // include it when signing. + msg, err := ns.ASN1Marshal(txID, tx.Metadata) if err != nil { return nil, "", fmt.Errorf("failed asn1 marshal tx: %w", err) } diff --git a/internal/controller/committer_controller.go b/internal/controller/committer_controller.go index 68323f2..818c318 100644 --- a/internal/controller/committer_controller.go +++ b/internal/controller/committer_controller.go @@ -223,7 +223,7 @@ func (r *CommitterReconciler) buildCoordinatorSpec(committer *fabricxv1alpha1.Co } imageTag := committer.Spec.ImageTag if imageTag == "" { - imageTag = "0.1.5" + imageTag = "0.1.9" } spec := fabricxv1alpha1.CommitterCoordinatorSpec{ @@ -252,6 +252,9 @@ func (r *CommitterReconciler) buildCoordinatorSpec(committer *fabricxv1alpha1.Co // Copy component-specific configuration if available if committer.Spec.Components.Coordinator != nil { + if committer.Spec.Components.Coordinator.Replicas != 0 { + spec.Replicas = committer.Spec.Components.Coordinator.Replicas + } spec.Ingress = committer.Spec.Components.Coordinator.Ingress spec.Enrollment = committer.Spec.Components.Coordinator.Enrollment spec.SANS = committer.Spec.Components.Coordinator.SANS @@ -318,7 +321,7 @@ func (r *CommitterReconciler) buildSidecarSpec(committer *fabricxv1alpha1.Commit } imageTag := committer.Spec.ImageTag if imageTag == "" { - imageTag = "0.1.5" + imageTag = "0.1.9" } spec := fabricxv1alpha1.CommitterSidecarSpec{ @@ -348,6 +351,10 @@ func (r *CommitterReconciler) buildSidecarSpec(committer *fabricxv1alpha1.Commit // Copy component-specific configuration if available if committer.Spec.Components.Sidecar != nil { + // Sidecar should only have 1 replica + if committer.Spec.Components.Sidecar.Replicas != 0 { + spec.Replicas = committer.Spec.Components.Sidecar.Replicas + } spec.Ingress = committer.Spec.Components.Sidecar.Ingress spec.Enrollment = committer.Spec.Components.Sidecar.Enrollment spec.SANS = committer.Spec.Components.Sidecar.SANS @@ -355,6 +362,9 @@ func (r *CommitterReconciler) buildSidecarSpec(committer *fabricxv1alpha1.Commit spec.Env = committer.Spec.Components.Sidecar.Env spec.Command = committer.Spec.Components.Sidecar.Command spec.Args = committer.Spec.Components.Sidecar.Args + if committer.Spec.Components.Sidecar.Storage != nil { + spec.Storage = committer.Spec.Components.Sidecar.Storage + } } // Resolve orderer endpoints precedence: component-level overrides parent-level shared config @@ -426,7 +436,7 @@ func (r *CommitterReconciler) buildValidatorSpec(committer *fabricxv1alpha1.Comm } imageTag := committer.Spec.ImageTag if imageTag == "" { - imageTag = "0.1.5" + imageTag = "0.1.9" } spec := fabricxv1alpha1.CommitterValidatorSpec{ @@ -455,6 +465,9 @@ func (r *CommitterReconciler) buildValidatorSpec(committer *fabricxv1alpha1.Comm // Copy component-specific configuration if available if committer.Spec.Components.Validator != nil { + if committer.Spec.Components.Validator.Replicas != 0 { + spec.Replicas = committer.Spec.Components.Validator.Replicas + } spec.Ingress = committer.Spec.Components.Validator.Ingress spec.Enrollment = committer.Spec.Components.Validator.Enrollment spec.SANS = committer.Spec.Components.Validator.SANS @@ -519,7 +532,7 @@ func (r *CommitterReconciler) buildVerifierSpec(committer *fabricxv1alpha1.Commi } imageTag := committer.Spec.ImageTag if imageTag == "" { - imageTag = "0.1.5" + imageTag = "0.1.9" } spec := fabricxv1alpha1.CommitterVerifierSpec{ @@ -548,6 +561,9 @@ func (r *CommitterReconciler) buildVerifierSpec(committer *fabricxv1alpha1.Commi // Copy component-specific configuration if available if committer.Spec.Components.VerifierService != nil { + if committer.Spec.Components.VerifierService.Replicas != 0 { + spec.Replicas = committer.Spec.Components.VerifierService.Replicas + } spec.Ingress = committer.Spec.Components.VerifierService.Ingress spec.Enrollment = committer.Spec.Components.VerifierService.Enrollment spec.SANS = committer.Spec.Components.VerifierService.SANS @@ -611,7 +627,7 @@ func (r *CommitterReconciler) buildQueryServiceSpec(committer *fabricxv1alpha1.C } imageTag := committer.Spec.ImageTag if imageTag == "" { - imageTag = "0.1.5" + imageTag = "0.1.9" } spec := fabricxv1alpha1.CommitterQueryServiceSpec{ @@ -640,6 +656,9 @@ func (r *CommitterReconciler) buildQueryServiceSpec(committer *fabricxv1alpha1.C // Copy component-specific configuration if available if committer.Spec.Components.QueryService != nil { + if committer.Spec.Components.QueryService.Replicas != 0 { + spec.Replicas = committer.Spec.Components.QueryService.Replicas + } spec.Ingress = committer.Spec.Components.QueryService.Ingress spec.Enrollment = committer.Spec.Components.QueryService.Enrollment spec.SANS = committer.Spec.Components.QueryService.SANS diff --git a/internal/controller/committercoordinator_controller.go b/internal/controller/committercoordinator_controller.go index d5d63e1..d9bbc5f 100644 --- a/internal/controller/committercoordinator_controller.go +++ b/internal/controller/committercoordinator_controller.go @@ -709,13 +709,13 @@ func (r *CommitterCoordinatorReconciler) reconcileDeployment(ctx context.Context if committerCoordinator.Spec.ImageTag != "" { return committerCoordinator.Spec.ImageTag } - return "0.1.9" + return "1.0.0" }()), Command: []string{ "committer", }, Args: []string{ - "start-coordinator", + "start", "coordinator", "--config=/etc/hyperledger/fabricx/coordinator/coordinator_config.yaml", }, Ports: []corev1.ContainerPort{ diff --git a/internal/controller/committerqueryservice_controller.go b/internal/controller/committerqueryservice_controller.go index b3f1658..a33881d 100644 --- a/internal/controller/committerqueryservice_controller.go +++ b/internal/controller/committerqueryservice_controller.go @@ -488,16 +488,25 @@ func (r *CommitterQueryServiceReconciler) reconcileSecret(ctx context.Context, c // Add PostgreSQL configuration if provided if committerCoordinator.Spec.PostgreSQL != nil { - templateData.PostgreSQL = &utils.PostgreSQLTemplateData{ + postgresData := &utils.PostgreSQLTemplateData{ Host: committerCoordinator.Spec.PostgreSQL.Host, Port: committerCoordinator.Spec.PostgreSQL.Port, Database: committerCoordinator.Spec.PostgreSQL.Database, Username: committerCoordinator.Spec.PostgreSQL.Username, Password: "", // Password will be fetched from secret - MaxConnections: committerCoordinator.Spec.PostgreSQL.MaxConnections, - MinConnections: committerCoordinator.Spec.PostgreSQL.MinConnections, + MaxConnections: 50, + MinConnections: 10, LoadBalance: committerCoordinator.Spec.PostgreSQL.LoadBalance, - MaxElapsedTime: committerCoordinator.Spec.PostgreSQL.Retry.MaxElapsedTime, + } + if committerCoordinator.Spec.PostgreSQL.MaxConnections != 0 { + postgresData.MaxConnections = committerCoordinator.Spec.PostgreSQL.MaxConnections + } + if committerCoordinator.Spec.PostgreSQL.MinConnections != 0 { + postgresData.MinConnections = committerCoordinator.Spec.PostgreSQL.MinConnections + } + templateData.PostgreSQL = postgresData + if committerCoordinator.Spec.PostgreSQL.Retry != nil && committerCoordinator.Spec.PostgreSQL.Retry.MaxElapsedTime != "" { + templateData.PostgreSQL.MaxElapsedTime = committerCoordinator.Spec.PostgreSQL.Retry.MaxElapsedTime } // Fetch password from secret if provided @@ -732,13 +741,13 @@ func (r *CommitterQueryServiceReconciler) reconcileDeployment(ctx context.Contex if committerCoordinator.Spec.ImageTag != "" { return committerCoordinator.Spec.ImageTag } - return "0.1.9" + return "1.0.0" }()), Command: []string{ "committer", }, Args: []string{ - "start-query", + "start", "query", "--config=/config/config.yaml", }, Ports: []corev1.ContainerPort{ diff --git a/internal/controller/committersidecar_controller.go b/internal/controller/committersidecar_controller.go index b6ce441..209d80f 100644 --- a/internal/controller/committersidecar_controller.go +++ b/internal/controller/committersidecar_controller.go @@ -455,12 +455,17 @@ func (r *CommitterSidecarReconciler) reconcileDeployMode(ctx context.Context, co return fmt.Errorf("failed to reconcile service: %w", err) } - // 5. Create/Update Deployment for Sidecar + // 5. Create/Update PVC for Sidecar (if storage is configured) + if err := r.reconcilePVC(ctx, committerSidecar); err != nil { + return fmt.Errorf("failed to reconcile pvc: %w", err) + } + + // 6. Create/Update Deployment for Sidecar if err := r.reconcileDeployment(ctx, committerSidecar); err != nil { return fmt.Errorf("failed to reconcile deployment: %w", err) } - // 6. Create/Update Ingress for Sidecar (if configured) + // 7. Create/Update Ingress for Sidecar (if configured) if committerSidecar.Spec.Ingress != nil { if err := r.reconcileIngress(ctx, committerSidecar); err != nil { return fmt.Errorf("failed to reconcile ingress: %w", err) @@ -729,12 +734,12 @@ func (r *CommitterSidecarReconciler) reconcileDeployment(ctx context.Context, co "/bin/sh", "-c", fmt.Sprintf( - "mkdir -p /var/hyperledger/fabricx/msp/signcerts && "+ - "mkdir -p /var/hyperledger/fabricx/msp/keystore && "+ - "mkdir -p /var/hyperledger/fabricx/msp/cacerts && "+ - "cp /sign-certs/cert.pem /var/hyperledger/fabricx/msp/signcerts/ && "+ - "cp /sign-certs/key.pem /var/hyperledger/fabricx/msp/keystore/priv_sk && "+ - "cp /sign-certs/ca.pem /var/hyperledger/fabricx/msp/cacerts/ && "+ + "mkdir -p /var/hyperledger/fabricx/msp/signcerts && " + + "mkdir -p /var/hyperledger/fabricx/msp/keystore && " + + "mkdir -p /var/hyperledger/fabricx/msp/cacerts && " + + "cp /sign-certs/cert.pem /var/hyperledger/fabricx/msp/signcerts/ && " + + "cp /sign-certs/key.pem /var/hyperledger/fabricx/msp/keystore/priv_sk && " + + "cp /sign-certs/ca.pem /var/hyperledger/fabricx/msp/cacerts/ && " + `cat > /var/hyperledger/fabricx/msp/config.yaml <<'EOF' NodeOUs: Enable: true @@ -785,7 +790,7 @@ EOF`), "committer", }, Args: []string{ - "start-sidecar", + "start", "sidecar", "--config=/etc/hyperledger/fabricx/sidecar/sidecar_config.yaml", }, Ports: []corev1.ContainerPort{ @@ -844,12 +849,24 @@ EOF`), }, }, }, - { - Name: "data", - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, - }, - }, + func() corev1.Volume { + if committerSidecar.Spec.Storage != nil { + return corev1.Volume{ + Name: "data", + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: fmt.Sprintf("%s-data-pvc", committerSidecar.Name), + }, + }, + } + } + return corev1.Volume{ + Name: "data", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + } + }(), { Name: "sign-certs", VolumeSource: corev1.VolumeSource{ @@ -898,6 +915,63 @@ EOF`), return nil } +// reconcilePVC creates or updates the PVC for Sidecar +func (r *CommitterSidecarReconciler) reconcilePVC(ctx context.Context, committerSidecar *fabricxv1alpha1.CommitterSidecar) error { + log := logf.FromContext(ctx) + + // Check if storage is configured + if committerSidecar.Spec.Storage == nil { + log.Info("No storage configuration found, skipping PVC creation") + return nil + } + + pvcName := fmt.Sprintf("%s-data-pvc", committerSidecar.Name) + pvc := &corev1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: pvcName, + Namespace: committerSidecar.Namespace, + Labels: map[string]string{ + "app": "fabric-x", + "committersidecar": committerSidecar.Name, + }, + }, + Spec: corev1.PersistentVolumeClaimSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{ + corev1.ReadWriteOnce, + }, + Resources: corev1.VolumeResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceStorage: resource.MustParse(committerSidecar.Spec.Storage.Size), + }, + }, + StorageClassName: func() *string { + if committerSidecar.Spec.Storage.StorageClass != "" { + return &committerSidecar.Spec.Storage.StorageClass + } + return nil + }(), + }, + } + + // Set the controller reference + if err := controllerutil.SetControllerReference(committerSidecar, pvc, r.Scheme); err != nil { + return fmt.Errorf("failed to set controller reference for PVC: %w", err) + } + + if err := r.Create(ctx, pvc); err != nil { + // If PVC already exists, skip update (immutable fields like VolumeName + // are set by the system and cannot be overwritten). + if strings.Contains(err.Error(), "already exists") { + log.Info("PVC already exists, skipping update", "name", pvcName) + } else { + return fmt.Errorf("failed to create PVC: %w", err) + } + } + + log.Info("PVC reconciled successfully", "sidecar", committerSidecar.Name) + return nil +} + // updateDeployment updates a deployment with template data func (r *CommitterSidecarReconciler) updateDeployment(ctx context.Context, committerSidecar *fabricxv1alpha1.CommitterSidecar, deployment *appsv1.Deployment, template *appsv1.Deployment) error { _, err := controllerutil.CreateOrUpdate(ctx, r.Client, deployment, func() error { @@ -1019,11 +1093,10 @@ func (r *CommitterSidecarReconciler) handleDeletion(ctx context.Context, committ // Set status to indicate deletion r.updateCommitterSidecarStatus(ctx, committerSidecar, fabricxv1alpha1.PendingStatus, "Deleting CommitterSidecar resources") - // TODO: Clean up resources based on deployment mode - // - Delete Deployments/StatefulSets - // - Delete Services - // - Delete PVCs - // - Delete ConfigMaps and Secrets + // Clean up resources based on deployment mode + if err := r.cleanupDeploymentResources(ctx, committerSidecar); err != nil { + log.Error(err, "Failed to cleanup deployment resources") + } // Remove finalizer if err := r.removeFinalizer(ctx, committerSidecar); err != nil { @@ -1052,6 +1125,35 @@ func (r *CommitterSidecarReconciler) removeFinalizer(ctx context.Context, commit return r.Update(ctx, committerSidecar) } +// cleanupDeploymentResources cleans up deployment resources for the CommitterSidecar +func (r *CommitterSidecarReconciler) cleanupDeploymentResources(ctx context.Context, committerSidecar *fabricxv1alpha1.CommitterSidecar) error { + log := logf.FromContext(ctx) + + // Delete PVC if storage was configured + if committerSidecar.Spec.Storage != nil { + pvc := &corev1.PersistentVolumeClaim{} + if err := r.Get(ctx, client.ObjectKey{Name: fmt.Sprintf("%s-data-pvc", committerSidecar.Name), Namespace: committerSidecar.Namespace}, pvc); err != nil { + if !errors.IsNotFound(err) { + return fmt.Errorf("failed to get PVC: %w", err) + } + log.Info("PVC not found, skipping deletion", "name", fmt.Sprintf("%s-data-pvc", committerSidecar.Name)) + } else { + if err := r.Delete(ctx, pvc); err != nil { + return fmt.Errorf("failed to delete PVC: %w", err) + } + log.Info("Deleted PVC", "name", pvc.Name) + } + } + + // TODO: Clean up more resources based on deployment mode + // - Delete Deployments/StatefulSets + // - Delete Services + // - DONE: Delete PVCs + // - Delete ConfigMaps and Secrets + + return nil +} + // updateCommitterSidecarStatus updates the CommitterSidecar status with the given status and message func (r *CommitterSidecarReconciler) updateCommitterSidecarStatus(ctx context.Context, committerSidecar *fabricxv1alpha1.CommitterSidecar, status fabricxv1alpha1.DeploymentStatus, message string) { log := logf.FromContext(ctx) diff --git a/internal/controller/committervalidator_controller.go b/internal/controller/committervalidator_controller.go index 1c45018..b221673 100644 --- a/internal/controller/committervalidator_controller.go +++ b/internal/controller/committervalidator_controller.go @@ -776,13 +776,13 @@ func (r *CommitterValidatorReconciler) reconcileDeployment(ctx context.Context, if committerValidator.Spec.ImageTag != "" { return committerValidator.Spec.ImageTag } - return "0.1.9" + return "1.0.0" }()), Command: []string{ "committer", }, Args: []string{ - "start-vc", + "start", "vc", "--config=/etc/hyperledger/fabricx/validator/validator_config.yaml", }, Ports: []corev1.ContainerPort{ diff --git a/internal/controller/committerverifier_controller.go b/internal/controller/committerverifier_controller.go index 5a89aae..44d93dd 100644 --- a/internal/controller/committerverifier_controller.go +++ b/internal/controller/committerverifier_controller.go @@ -705,13 +705,13 @@ func (r *CommitterVerifierReconciler) reconcileDeployment(ctx context.Context, c if committerVerifier.Spec.ImageTag != "" { return committerVerifier.Spec.ImageTag } - return "0.1.9" + return "1.0.0" }()), Command: []string{ "committer", }, Args: []string{ - "start-verifier", + "start", "verifier", "--config=/etc/hyperledger/fabricx/verifier/verifier_config.yaml", }, Ports: []corev1.ContainerPort{ diff --git a/internal/controller/genesis/genesis_service.go b/internal/controller/genesis/genesis_service.go index 3a814b2..e18e61f 100644 --- a/internal/controller/genesis/genesis_service.go +++ b/internal/controller/genesis/genesis_service.go @@ -159,8 +159,51 @@ func (s *GenesisService) CreateGenesisBlock(ctx context.Context, req *GenesisReq return nil, errors.Wrap(err, "failed to write shared config to temporary file") } + // Resolve consenter certificates and build ConsenterMapping. + // The encoder loads Identity/ClientTLSCert/ServerTLSCert from file paths. + var consenterMapping []*configtxgen.Consenter + for _, n := range req.Genesis.Spec.Consenters { + identityPEM, err := s.fetchAndValidateX509Certificate(ctx, n.IdentityRef) + if err != nil { + return nil, errors.Wrapf(err, "failed to fetch identity cert for consenter %d", n.ID) + } + clientTLSPEM, err := s.fetchAndValidateX509Certificate(ctx, n.ClientTLSCertRef) + if err != nil { + return nil, errors.Wrapf(err, "failed to fetch client TLS cert for consenter %d", n.ID) + } + serverTLSPEM, err := s.fetchAndValidateX509Certificate(ctx, n.ServerTLSCertRef) + if err != nil { + return nil, errors.Wrapf(err, "failed to fetch server TLS cert for consenter %d", n.ID) + } + consenterDir := filepath.Join(tempDir, fmt.Sprintf("consenter-%d", n.ID)) + if err := os.MkdirAll(consenterDir, 0755); err != nil { + return nil, errors.Wrapf(err, "failed to create consenter dir for %d", n.ID) + } + identityPath := filepath.Join(consenterDir, "identity.pem") + clientTLSPath := filepath.Join(consenterDir, "client-tls.pem") + serverTLSPath := filepath.Join(consenterDir, "server-tls.pem") + if err := os.WriteFile(identityPath, identityPEM, 0644); err != nil { + return nil, errors.Wrapf(err, "failed to write identity cert for consenter %d", n.ID) + } + if err := os.WriteFile(clientTLSPath, clientTLSPEM, 0644); err != nil { + return nil, errors.Wrapf(err, "failed to write client TLS cert for consenter %d", n.ID) + } + if err := os.WriteFile(serverTLSPath, serverTLSPEM, 0644); err != nil { + return nil, errors.Wrapf(err, "failed to write server TLS cert for consenter %d", n.ID) + } + consenterMapping = append(consenterMapping, &configtxgen.Consenter{ + ID: uint32(n.ID), + Host: n.Host, + Port: uint32(n.Port), + MSPID: n.MSPID, + Identity: identityPath, + ClientTLSCert: clientTLSPath, + ServerTLSCert: serverTLSPath, + }) + } + // Create genesis block with SharedConfig as consensus metadata - genesisBlock, err := s.createGenesisBlock(allOrgs, ordererOrgs, sharedConfigPath) + genesisBlock, err := s.createGenesisBlock(allOrgs, ordererOrgs, sharedConfigPath, consenterMapping) if err != nil { return nil, errors.Wrap(err, "failed to create genesis block") } @@ -441,6 +484,14 @@ func (s *GenesisService) processExternalApplicationOrg(ctx context.Context, org Type: "ImplicitMeta", Rule: "MAJORITY Admins", }, + "Endorsement": { + Type: "Signature", + Rule: fmt.Sprintf("OR('%s.peer')", org.MSPID), + }, + "LifecycleEndorsement": { + Type: "Signature", + Rule: fmt.Sprintf("OR('%s.peer')", org.MSPID), + }, }, } @@ -517,7 +568,7 @@ func (s *GenesisService) provisionMSPDirectory(mspID string, signCACert, tlsCACe } // createGenesisBlock creates the genesis block programmatically -func (s *GenesisService) createGenesisBlock(allOrgs []*configtxgen.Organization, ordererOrgs []*configtxgen.Organization, sharedConfigPath string) ([]byte, error) { +func (s *GenesisService) createGenesisBlock(allOrgs []*configtxgen.Organization, ordererOrgs []*configtxgen.Organization, sharedConfigPath string, consenterMapping []*configtxgen.Consenter) ([]byte, error) { // Validate that we have at least one orderer organization if len(ordererOrgs) == 0 { return nil, errors.New("at least one orderer organization is required") @@ -543,8 +594,9 @@ func (s *GenesisService) createGenesisBlock(allOrgs []*configtxgen.Organization, Arma: &configtxgen.ConsensusMetadata{ Path: sharedConfigPath, }, - Addresses: []string{}, - BatchTimeout: 2 * time.Second, + ConsenterMapping: consenterMapping, + Addresses: []string{}, + BatchTimeout: 2 * time.Second, BatchSize: configtxgen.BatchSize{ MaxMessageCount: 500, AbsoluteMaxBytes: 10 * 1024 * 1024, @@ -591,6 +643,14 @@ func (s *GenesisService) createGenesisBlock(allOrgs []*configtxgen.Organization, Type: "ImplicitMeta", Rule: "MAJORITY Admins", }, + "LifecycleEndorsement": { + Type: "ImplicitMeta", + Rule: "ANY Endorsement", // Bypasses MAJORITY for test environments + }, + "Endorsement": { + Type: "ImplicitMeta", + Rule: "ANY Endorsement", + }, }, }, Capabilities: map[string]bool{ diff --git a/internal/controller/genesis/genesis_service_test.go b/internal/controller/genesis/genesis_service_test.go index 40dc619..bbef4c9 100644 --- a/internal/controller/genesis/genesis_service_test.go +++ b/internal/controller/genesis/genesis_service_test.go @@ -49,9 +49,27 @@ func TestGenesisService_CreateGenesisBlock(t *testing.T) { require.NoError(t, err) // Generate orderer nodes - consenters, _, err := GenerateConsenters("OrdererOrgMSP", 2, "orderer", 7050) + consenters, consenterCertBundle, err := GenerateConsenters("OrdererOrgMSP", 2, "orderer", 7050) require.NoError(t, err) + // Create consenter certificate secrets + consenterIdentitySecret1 := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "orderer1-identity-secret", Namespace: "default"}, + Data: map[string][]byte{"identity.crt": consenterCertBundle.Orderer}, + } + consenterTLSSecret1 := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "orderer1-tls-secret", Namespace: "default"}, + Data: map[string][]byte{"tls.crt": consenterCertBundle.TLS}, + } + consenterIdentitySecret2 := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "orderer2-identity-secret", Namespace: "default"}, + Data: map[string][]byte{"identity.crt": consenterCertBundle.Orderer}, + } + consenterTLSSecret2 := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "orderer2-tls-secret", Namespace: "default"}, + Data: map[string][]byte{"tls.crt": consenterCertBundle.TLS}, + } + // Create mock CA resources with correct names ca1 := &v1alpha1.CA{ ObjectMeta: metav1.ObjectMeta{ @@ -271,6 +289,7 @@ func TestGenesisService_CreateGenesisBlock(t *testing.T) { WithObjects(appOrg1SignSecret, appOrg1TLSSecret, appOrg1AdminSecret). WithObjects(appOrg2SignSecret, appOrg2TLSSecret, appOrg2AdminSecret). WithObjects(metaNamespaceCASecret, partyCertSecret). + WithObjects(consenterIdentitySecret1, consenterTLSSecret1, consenterIdentitySecret2, consenterTLSSecret2). Build() opts := zap.Options{ @@ -412,6 +431,16 @@ func TestGenesisService_CreateGenesisBlock_ExternalOrgsOnly(t *testing.T) { consenters, _, err := GenerateConsenters("ExternalOrg1MSP", 1, "orderer", 7050) require.NoError(t, err) + // Create consenter certificate secrets + consenterIdentitySecret1 := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "orderer1-identity-secret", Namespace: "default"}, + Data: map[string][]byte{"identity.crt": certBundle.Orderer}, + } + consenterTLSSecret1 := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "orderer1-tls-secret", Namespace: "default"}, + Data: map[string][]byte{"tls.crt": certBundle.TLS}, + } + // Create mock CA resource for orderer organization ordererCA := &v1alpha1.CA{ ObjectMeta: metav1.ObjectMeta{ @@ -534,7 +563,7 @@ func TestGenesisService_CreateGenesisBlock_ExternalOrgsOnly(t *testing.T) { // Create party cert secrets extPartyCertSecret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Name: "ext-party1-certs", Namespace: "default"}, - Data: map[string][]byte{"cert.pem": certBundle.TLS, "sign.pem": certBundle.CA}, + Data: map[string][]byte{"cert.pem": certBundle.TLS, "sign.pem": certBundle.CA}, } // Create fake client with CA resource and secrets @@ -545,6 +574,7 @@ func TestGenesisService_CreateGenesisBlock_ExternalOrgsOnly(t *testing.T) { WithObjects(external1SignSecret, external1TLSSecret, external1AdminSecret). WithObjects(external2SignSecret, external2TLSSecret, external2AdminSecret). WithObjects(metaNamespaceCASecret, extPartyCertSecret). + WithObjects(consenterIdentitySecret1, consenterTLSSecret1). Build() opts := zap.Options{ @@ -569,11 +599,11 @@ func TestGenesisService_CreateGenesisBlock_ExternalOrgsOnly(t *testing.T) { Key: "ca.pem", }, Parties: []v1alpha1.PartyConfig{{ - PartyID: 1, - CACerts: []v1alpha1.SecretKeyNSSelector{{Name: "ext-party1-certs", Namespace: "default", Key: "sign.pem"}}, - TLSCACerts: []v1alpha1.SecretKeyNSSelector{{Name: "ext-party1-certs", Namespace: "default", Key: "cert.pem"}}, - RouterConfig: &v1alpha1.PartyRouterConfig{Host: "p1-router.local", Port: 7150, TLSCert: v1alpha1.SecretKeyNSSelector{Name: "ext-party1-certs", Namespace: "default", Key: "cert.pem"}}, - BatchersConfig: []v1alpha1.PartyBatcherConfig{{ShardID: 1, Host: "p1-batcher.local", Port: 7151, SignCert: v1alpha1.SecretKeyNSSelector{Name: "ext-party1-certs", Namespace: "default", Key: "sign.pem"}, TLSCert: v1alpha1.SecretKeyNSSelector{Name: "ext-party1-certs", Namespace: "default", Key: "cert.pem"}}}, + PartyID: 1, + CACerts: []v1alpha1.SecretKeyNSSelector{{Name: "ext-party1-certs", Namespace: "default", Key: "sign.pem"}}, + TLSCACerts: []v1alpha1.SecretKeyNSSelector{{Name: "ext-party1-certs", Namespace: "default", Key: "cert.pem"}}, + RouterConfig: &v1alpha1.PartyRouterConfig{Host: "p1-router.local", Port: 7150, TLSCert: v1alpha1.SecretKeyNSSelector{Name: "ext-party1-certs", Namespace: "default", Key: "cert.pem"}}, + BatchersConfig: []v1alpha1.PartyBatcherConfig{{ShardID: 1, Host: "p1-batcher.local", Port: 7151, SignCert: v1alpha1.SecretKeyNSSelector{Name: "ext-party1-certs", Namespace: "default", Key: "sign.pem"}, TLSCert: v1alpha1.SecretKeyNSSelector{Name: "ext-party1-certs", Namespace: "default", Key: "cert.pem"}}}, ConsenterConfig: &v1alpha1.PartyConsenterConfig{Host: "p1-consenter.local", Port: 7052, SignCert: v1alpha1.SecretKeyNSSelector{Name: "ext-party1-certs", Namespace: "default", Key: "sign.pem"}, TLSCert: v1alpha1.SecretKeyNSSelector{Name: "ext-party1-certs", Namespace: "default", Key: "cert.pem"}}, AssemblerConfig: &v1alpha1.PartyAssemblerConfig{Host: "p1-assembler.local", Port: 7050, TLSCert: v1alpha1.SecretKeyNSSelector{Name: "ext-party1-certs", Namespace: "default", Key: "cert.pem"}}, }}, @@ -618,6 +648,24 @@ func TestGenesisService_CreateGenesisBlock_ApplicationOrgsOnly(t *testing.T) { consenters, _, err := GenerateConsenters("OrdererOrgMSP", 2, "orderer", 7050) require.NoError(t, err) + // Create consenter certificate secrets + consenterIdentitySecret1 := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "orderer1-identity-secret", Namespace: "default"}, + Data: map[string][]byte{"identity.crt": certBundle.Orderer}, + } + consenterTLSSecret1 := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "orderer1-tls-secret", Namespace: "default"}, + Data: map[string][]byte{"tls.crt": certBundle.TLS}, + } + consenterIdentitySecret2 := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "orderer2-identity-secret", Namespace: "default"}, + Data: map[string][]byte{"identity.crt": certBundle.Orderer}, + } + consenterTLSSecret2 := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "orderer2-tls-secret", Namespace: "default"}, + Data: map[string][]byte{"tls.crt": certBundle.TLS}, + } + // Create mock CA resource for orderer organization ordererCA := &v1alpha1.CA{ ObjectMeta: metav1.ObjectMeta{ @@ -739,7 +787,7 @@ func TestGenesisService_CreateGenesisBlock_ApplicationOrgsOnly(t *testing.T) { } appPartyCertSecret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Name: "app-party1-certs", Namespace: "default"}, - Data: map[string][]byte{"cert.pem": certBundle.TLS, "sign.pem": certBundle.CA}, + Data: map[string][]byte{"cert.pem": certBundle.TLS, "sign.pem": certBundle.CA}, } fakeClient := fake.NewClientBuilder(). WithScheme(scheme.Scheme). @@ -748,6 +796,7 @@ func TestGenesisService_CreateGenesisBlock_ApplicationOrgsOnly(t *testing.T) { WithObjects(appOrg1SignSecret, appOrg1TLSSecret, appOrg1AdminSecret). WithObjects(appOrg2SignSecret, appOrg2TLSSecret, appOrg2AdminSecret). WithObjects(metaNamespaceCASecret, appPartyCertSecret). + WithObjects(consenterIdentitySecret1, consenterTLSSecret1, consenterIdentitySecret2, consenterTLSSecret2). Build() opts := zap.Options{ @@ -773,11 +822,11 @@ func TestGenesisService_CreateGenesisBlock_ApplicationOrgsOnly(t *testing.T) { Key: "ca.pem", }, Parties: []v1alpha1.PartyConfig{{ - PartyID: 1, - CACerts: []v1alpha1.SecretKeyNSSelector{{Name: "app-party1-certs", Namespace: "default", Key: "sign.pem"}}, - TLSCACerts: []v1alpha1.SecretKeyNSSelector{{Name: "app-party1-certs", Namespace: "default", Key: "cert.pem"}}, - RouterConfig: &v1alpha1.PartyRouterConfig{Host: "p1-router.local", Port: 7150, TLSCert: v1alpha1.SecretKeyNSSelector{Name: "app-party1-certs", Namespace: "default", Key: "cert.pem"}}, - BatchersConfig: []v1alpha1.PartyBatcherConfig{{ShardID: 1, Host: "p1-batcher.local", Port: 7151, SignCert: v1alpha1.SecretKeyNSSelector{Name: "app-party1-certs", Namespace: "default", Key: "sign.pem"}, TLSCert: v1alpha1.SecretKeyNSSelector{Name: "app-party1-certs", Namespace: "default", Key: "cert.pem"}}}, + PartyID: 1, + CACerts: []v1alpha1.SecretKeyNSSelector{{Name: "app-party1-certs", Namespace: "default", Key: "sign.pem"}}, + TLSCACerts: []v1alpha1.SecretKeyNSSelector{{Name: "app-party1-certs", Namespace: "default", Key: "cert.pem"}}, + RouterConfig: &v1alpha1.PartyRouterConfig{Host: "p1-router.local", Port: 7150, TLSCert: v1alpha1.SecretKeyNSSelector{Name: "app-party1-certs", Namespace: "default", Key: "cert.pem"}}, + BatchersConfig: []v1alpha1.PartyBatcherConfig{{ShardID: 1, Host: "p1-batcher.local", Port: 7151, SignCert: v1alpha1.SecretKeyNSSelector{Name: "app-party1-certs", Namespace: "default", Key: "sign.pem"}, TLSCert: v1alpha1.SecretKeyNSSelector{Name: "app-party1-certs", Namespace: "default", Key: "cert.pem"}}}, ConsenterConfig: &v1alpha1.PartyConsenterConfig{Host: "p1-consenter.local", Port: 7052, SignCert: v1alpha1.SecretKeyNSSelector{Name: "app-party1-certs", Namespace: "default", Key: "sign.pem"}, TLSCert: v1alpha1.SecretKeyNSSelector{Name: "app-party1-certs", Namespace: "default", Key: "cert.pem"}}, AssemblerConfig: &v1alpha1.PartyAssemblerConfig{Host: "p1-assembler.local", Port: 7050, TLSCert: v1alpha1.SecretKeyNSSelector{Name: "app-party1-certs", Namespace: "default", Key: "cert.pem"}}, }}, @@ -923,7 +972,25 @@ func TestGenesisService_CreateGenesisBlock_WithInternalOrgs(t *testing.T) { intPartyCertSecret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Name: "int-party1-certs", Namespace: "default"}, - Data: map[string][]byte{"cert.pem": certBundle.TLS, "sign.pem": certBundle.CA}, + Data: map[string][]byte{"cert.pem": certBundle.TLS, "sign.pem": certBundle.CA}, + } + + // Create consenter certificate secrets (referenced by CertificateRefs in nodes) + consenterIdentitySecret1 := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "orderer1-identity-secret", Namespace: "default"}, + Data: map[string][]byte{"identity.crt": certBundle.Orderer}, + } + consenterTLSSecret1 := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "orderer1-tls-secret", Namespace: "default"}, + Data: map[string][]byte{"tls.crt": certBundle.TLS}, + } + consenterIdentitySecret2 := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "orderer2-identity-secret", Namespace: "default"}, + Data: map[string][]byte{"identity.crt": certBundle.Orderer}, + } + consenterTLSSecret2 := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "orderer2-tls-secret", Namespace: "default"}, + Data: map[string][]byte{"tls.crt": certBundle.TLS}, } // Create fake client with CA resource and secrets @@ -932,6 +999,7 @@ func TestGenesisService_CreateGenesisBlock_WithInternalOrgs(t *testing.T) { WithObjects(ca). WithObjects(ordererSignSecret, ordererTLSSecret, ordererAdminSecret). WithObjects(metaNamespaceCASecret, intPartyCertSecret). + WithObjects(consenterIdentitySecret1, consenterTLSSecret1, consenterIdentitySecret2, consenterTLSSecret2). Build() opts := zap.Options{ @@ -956,11 +1024,11 @@ func TestGenesisService_CreateGenesisBlock_WithInternalOrgs(t *testing.T) { Key: "ca.pem", }, Parties: []v1alpha1.PartyConfig{{ - PartyID: 1, - CACerts: []v1alpha1.SecretKeyNSSelector{{Name: "int-party1-certs", Namespace: "default", Key: "sign.pem"}}, - TLSCACerts: []v1alpha1.SecretKeyNSSelector{{Name: "int-party1-certs", Namespace: "default", Key: "cert.pem"}}, - RouterConfig: &v1alpha1.PartyRouterConfig{Host: "p1-router.local", Port: 7150, TLSCert: v1alpha1.SecretKeyNSSelector{Name: "int-party1-certs", Namespace: "default", Key: "cert.pem"}}, - BatchersConfig: []v1alpha1.PartyBatcherConfig{{ShardID: 1, Host: "p1-batcher.local", Port: 7151, SignCert: v1alpha1.SecretKeyNSSelector{Name: "int-party1-certs", Namespace: "default", Key: "sign.pem"}, TLSCert: v1alpha1.SecretKeyNSSelector{Name: "int-party1-certs", Namespace: "default", Key: "cert.pem"}}}, + PartyID: 1, + CACerts: []v1alpha1.SecretKeyNSSelector{{Name: "int-party1-certs", Namespace: "default", Key: "sign.pem"}}, + TLSCACerts: []v1alpha1.SecretKeyNSSelector{{Name: "int-party1-certs", Namespace: "default", Key: "cert.pem"}}, + RouterConfig: &v1alpha1.PartyRouterConfig{Host: "p1-router.local", Port: 7150, TLSCert: v1alpha1.SecretKeyNSSelector{Name: "int-party1-certs", Namespace: "default", Key: "cert.pem"}}, + BatchersConfig: []v1alpha1.PartyBatcherConfig{{ShardID: 1, Host: "p1-batcher.local", Port: 7151, SignCert: v1alpha1.SecretKeyNSSelector{Name: "int-party1-certs", Namespace: "default", Key: "sign.pem"}, TLSCert: v1alpha1.SecretKeyNSSelector{Name: "int-party1-certs", Namespace: "default", Key: "cert.pem"}}}, ConsenterConfig: &v1alpha1.PartyConsenterConfig{Host: "p1-consenter.local", Port: 7052, SignCert: v1alpha1.SecretKeyNSSelector{Name: "int-party1-certs", Namespace: "default", Key: "sign.pem"}, TLSCert: v1alpha1.SecretKeyNSSelector{Name: "int-party1-certs", Namespace: "default", Key: "cert.pem"}}, AssemblerConfig: &v1alpha1.PartyAssemblerConfig{Host: "p1-assembler.local", Port: 7050, TLSCert: v1alpha1.SecretKeyNSSelector{Name: "int-party1-certs", Namespace: "default", Key: "cert.pem"}}, }}, diff --git a/internal/controller/genesis/shared_config.go b/internal/controller/genesis/shared_config.go index a682c5a..5878caa 100644 --- a/internal/controller/genesis/shared_config.go +++ b/internal/controller/genesis/shared_config.go @@ -304,7 +304,7 @@ func (s *SharedConfigService) generateConsensusConfig(genesis *v1alpha1.Genesis) // Fallback to default configuration smartBFTConfig := &protos.SmartBFTConfig{ RequestBatchMaxCount: 500, - RequestBatchMaxBytes: 10 * 1024 * 1024, // 10MB + RequestBatchMaxBytes: 100 * 1024 * 1024, // 100MB RequestBatchMaxInterval: "2s", IncomingMessageBufferSize: 200, RequestPoolSize: 1000, @@ -379,7 +379,7 @@ func (s *SharedConfigService) generateBatchingConfig(genesis *v1alpha1.Genesis) batchingConfig := &protos.BatchingConfig{ BatchTimeouts: batchTimeouts, BatchSize: batchSize, - RequestMaxBytes: 100 * 1024 * 1024, // 100MB + RequestMaxBytes: 10 * 1024 * 1024, // 10MB } return batchingConfig, nil diff --git a/internal/controller/ordererassembler_controller.go b/internal/controller/ordererassembler_controller.go index 2d32e6f..5b76e13 100644 --- a/internal/controller/ordererassembler_controller.go +++ b/internal/controller/ordererassembler_controller.go @@ -1123,7 +1123,7 @@ EOF`, if ordererAssembler.Spec.ImageTag != "" { return ordererAssembler.Spec.ImageTag } - return "0.0.24" + return "1.0.0" }()), Args: []string{ "assembler", @@ -1334,11 +1334,10 @@ func (r *OrdererAssemblerReconciler) reconcilePVC(ctx context.Context, ordererAs } if err := r.Create(ctx, pvc); err != nil { - // If PVC already exists, update it + // If PVC already exists, skip update (immutable fields like VolumeName + // are set by the system and cannot be overwritten). if strings.Contains(err.Error(), "already exists") { - if err := r.Update(ctx, pvc); err != nil { - return fmt.Errorf("failed to update PVC: %w", err) - } + log.Info("PVC already exists, skipping update", "name", pvcName) } else { return fmt.Errorf("failed to create PVC: %w", err) } diff --git a/internal/controller/ordererbatcher_controller.go b/internal/controller/ordererbatcher_controller.go index 05eb5a3..778c80d 100644 --- a/internal/controller/ordererbatcher_controller.go +++ b/internal/controller/ordererbatcher_controller.go @@ -1331,7 +1331,7 @@ func (r *OrdererBatcherReconciler) getDeploymentTemplate(ctx context.Context, or if ordererBatcher.Spec.ImageTag != "" { return ordererBatcher.Spec.ImageTag } - return "0.0.24" + return "1.0.0" }()), Args: []string{ "batcher", diff --git a/internal/controller/ordererconsenter_controller.go b/internal/controller/ordererconsenter_controller.go index 6aff1e0..4d1c407 100644 --- a/internal/controller/ordererconsenter_controller.go +++ b/internal/controller/ordererconsenter_controller.go @@ -1230,7 +1230,7 @@ func (r *OrdererConsenterReconciler) getDeploymentTemplate(ctx context.Context, if ordererConsenter.Spec.ImageTag != "" { return ordererConsenter.Spec.ImageTag } - return "0.0.24" + return "1.0.0" }()), Args: []string{ "consensus", diff --git a/internal/controller/orderergroup_controller.go b/internal/controller/orderergroup_controller.go index e00a4d1..1f70722 100644 --- a/internal/controller/orderergroup_controller.go +++ b/internal/controller/orderergroup_controller.go @@ -463,6 +463,9 @@ func (r *OrdererGroupReconciler) buildConsenterSpecFromInstance(ordererGroup *fa spec.Env = config.Env spec.Command = config.Command spec.Args = config.Args + if config.PVCStorageSize != "" { + spec.PVCStorageSize = config.PVCStorageSize + } } return spec @@ -743,6 +746,9 @@ func (r *OrdererGroupReconciler) buildBatcherSpec(ordererGroup *fabricxv1alpha1. spec.Env = config.Env spec.Command = config.Command spec.Args = config.Args + if config.PVCStorageSize != "" { + spec.PVCStorageSize = config.PVCStorageSize + } } return spec diff --git a/internal/controller/ordererrouter_controller.go b/internal/controller/ordererrouter_controller.go index ccb0758..00543ec 100644 --- a/internal/controller/ordererrouter_controller.go +++ b/internal/controller/ordererrouter_controller.go @@ -926,7 +926,7 @@ EOF`, if ordererRouter.Spec.ImageTag != "" { return ordererRouter.Spec.ImageTag } - return "0.0.24" + return "1.0.0" }()), Args: []string{ "router", @@ -1102,11 +1102,9 @@ func (r *OrdererRouterReconciler) reconcilePVC(ctx context.Context, ordererRoute } if err := r.Create(ctx, pvc); err != nil { - // If PVC already exists, update it + // If PVC already exists, skip update (immutable fields cannot be overwritten). if strings.Contains(err.Error(), "already exists") { - if err := r.Update(ctx, pvc); err != nil { - return fmt.Errorf("failed to update PVC: %w", err) - } + log.Info("PVC already exists, skipping update", "name", pvcName) } else { return fmt.Errorf("failed to create PVC: %w", err) } diff --git a/internal/controller/utils/templates.go b/internal/controller/utils/templates.go index 51289f3..a0ec121 100644 --- a/internal/controller/utils/templates.go +++ b/internal/controller/utils/templates.go @@ -110,6 +110,8 @@ type CommitterCoordinatorTemplateData struct { Port int32 VerifierEndpoints []string ValidatorCommitterEndpoints []string + DataDir string + TLSEnabled bool } // CommitterQueryServiceTemplateData holds configuration data for CommitterQueryService @@ -124,23 +126,14 @@ type CommitterQueryServiceTemplateData struct { // BuildQueryServiceConfig returns a Query Service config template func BuildQueryServiceConfig(data CommitterQueryServiceTemplateData) string { config := fmt.Sprintf(`server: - # The server's endpoint configuration - endpoint: 0.0.0.0:%d - -# Resource limit configurations -# A batch will execute once it accumulated this number of keys. -min-batch-keys: 1024 -# A batch will execute once it waited this much time. -max-batch-wait: 100ms -# A new view will be created if the previous view was created before this much time. -view-aggregation-window: 100ms -# A new view will be created if the previous view aggregated this number of views. -max-aggregated-views: 1024 -# A view will be closed if it was opened for longer than this time. -max-view-timeout: 10s + endpoint: :%d +monitoring: + endpoint: :2117 + rate-limit: + requests-per-second: 0 + burst: 0 `, data.Port) - // Add database configuration if provided if data.PostgreSQL != nil { config += fmt.Sprintf(` database: @@ -179,25 +172,26 @@ func BuildCoordinatorConfig(data CommitterCoordinatorTemplateData, verifier []st vc += " - " + e + "\n" } return `server: - endpoint: - host: 0.0.0.0 - port: {{.Port}} + endpoint: :{{.Port}} +monitoring: + endpoint: :2120 + rate-limit: + requests-per-second: 0 + burst: 0 verifier: endpoints:` + v + ` validator-committer: endpoints:` + vc + ` dependency-graph: - num-of-local-dep-constructors: 4 - waiting-txs-limit: 20000000 - num-of-workers-for-global-dep-manager: 1 + num-of-local-dep-constructors: 1 + waiting-txs-limit: 100000 per-channel-buffer-size-per-goroutine: 10 -monitoring: - prometheus: - enabled: true - port: 2120 +startup-timeout: 5m logging: - level: debug - format: json` + logSpec: info:grpc=error + format: >- + %%{color}%%{time:2006-01-02 15:04:05.000 MST} [%%{module}] %%{shortfunc} + -> %%{level:.4s}%%{color:reset} %%{message}` } // CommitterSidecarTemplateData represents data specific to sidecar templates @@ -408,9 +402,12 @@ Assembler: // CoordinatorConfigTemplate is the Go template for coordinator configuration CoordinatorConfigTemplate = `server: - endpoint: - host: 0.0.0.0 - port: {{.Port}} + endpoint: :{{.Port}} +monitoring: + endpoint: :2120 + rate-limit: + requests-per-second: 0 + burst: 0 verifier: endpoints: {{- range .VerifierEndpoints }} @@ -423,22 +420,20 @@ validator-committer: - {{ . }} {{- end }} +startup-timeout: 5m dependency-graph: - num-of-local-dep-constructors: 4 - waiting-txs-limit: 20000000 - num-of-workers-for-global-dep-manager: 1 + num-of-local-dep-constructors: 1 + waiting-txs-limit: 100000 per-channel-buffer-size-per-goroutine: 10 -monitoring: - prometheus: - enabled: true - port: 2120 logging: - level: debug - format: json` + logSpec: info:grpc=error + format: >- + %{color}%{time:2006-01-02 15:04:05.000 MST} [%{module}] %{shortfunc} + -> %{level:.4s}%{color:reset} %{message}` - // SidecarConfigTemplate is the Go template for sidecar configuration + // SidecarConfigTemplate is the Go template for sidecar configuration (committer v1.0.0). SidecarConfigTemplate = `server: - endpoint: 0.0.0.0:{{.Port}} + endpoint: :{{.Port}} keep-alive: params: time: 300s @@ -446,38 +441,43 @@ logging: enforcement-policy: min-time: 60s permit-without-stream: false +monitoring: + endpoint: :2114 + rate-limit: + requests-per-second: 0 + burst: 0 orderer: - channel-id: {{ .ChannelID }} - consensus-type: CFT + fault-tolerance-level: CFT + latest-known-config-block-path: /etc/hyperledger/fabricx/genesis/genesis.block identity: msp-id: {{ .MSPID }} msp-dir: /var/hyperledger/fabricx/msp committer: - endpoint: - host: {{ .CommitterHost }} - port: {{ .CommitterPort }} + endpoint: {{ .CommitterHost }}:{{ .CommitterPort }} ledger: path: /var/hyperledger/fabricx/ledger + sync-interval: 100 last-committed-block-set-interval: 5s -bootstrap: - genesis-block-file-path: /etc/hyperledger/fabricx/genesis/genesis.block -monitoring: - prometheus: - enabled: true - port: 2111 +waiting-txs-limit: 20000000 +channel-buffer-size: 100 logging: - level: debug - format: json` + logSpec: info:grpc=error + format: >- + %{color}%{time:2006-01-02 15:04:05.000 MST} [%{module}] %{shortfunc} + -> %{level:.4s}%{color:reset} %{message}` - // ValidatorConfigTemplate is the Go template for validator configuration + // ValidatorConfigTemplate is the Go template for validator configuration (committer v1.0.0). ValidatorConfigTemplate = `server: - endpoint: - host: 0.0.0.0 - port: {{.Port}} + endpoint: :{{.Port}} +monitoring: + endpoint: :2116 + rate-limit: + requests-per-second: 0 + burst: 0 +startup-timeout: 5m database: endpoints: - {{.PostgreSQL.Host}}:{{.PostgreSQL.Port}} - username: {{.PostgreSQL.Username}} password: {{.PostgreSQL.Password}} database: {{.PostgreSQL.Database}} @@ -485,33 +485,35 @@ database: min-connections: {{.PostgreSQL.MinConnections}} load-balance: {{.PostgreSQL.LoadBalance}} retry: + initial-interval: 500ms + randomization-factor: 0.5 + multiplier: 1.5 + max-interval: 60s max-elapsed-time: {{.PostgreSQL.MaxElapsedTime}} resource-limits: - max-workers-for-preparer: 2 - max-workers-for-validator: 2 + max-workers-for-preparer: 1 + max-workers-for-validator: 1 max-workers-for-committer: 20 - min-transaction-batch-size: 1000 -monitoring: - prometheus: - enabled: true - port: 2116 + min-transaction-batch-size: 1 + timeout-for-min-transaction-batch-size: 2s logging: - level: debug - format: json` + logSpec: info:grpc=error + format: >- + %{color}%{time:2006-01-02 15:04:05.000 MST} [%{module}] %{shortfunc} + -> %{level:.4s}%{color:reset} %{message}` - // VerifierConfigTemplate is the Go template for verifier configuration + // VerifierConfigTemplate is the Go template for verifier configuration (committer v1.0.0). VerifierConfigTemplate = `server: - endpoint: 0.0.0.0:{{.Port}} -parallel-executor: - batch-size-cutoff: 500 - batch-time-cutoff: 2ms - channel-buffer-size: 1000 - parallelism: 80 + endpoint: :{{.Port}} monitoring: - prometheus: - enabled: true - port: 2115 + endpoint: :2115 + rate-limit: + requests-per-second: 0 + burst: 0 +batch-size-cutoff: 50 +batch-time-cutoff: 1ms +channel-buffer-size: 50 +parallelism: 40 logging: - level: debug - format: json` + logSpec: info:grpc=error` ) From c8a6728e172b73e34e276de8f194b7440a8085c7 Mon Sep 17 00:00:00 2001 From: Elisha Mbadi Date: Mon, 27 Jul 2026 14:13:51 +0300 Subject: [PATCH 2/3] add orderer PVC --- config/crd/bases/fabricx.kfsoft.tech_orderergroups.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config/crd/bases/fabricx.kfsoft.tech_orderergroups.yaml b/config/crd/bases/fabricx.kfsoft.tech_orderergroups.yaml index 11a0a7c..e1ad83c 100644 --- a/config/crd/bases/fabricx.kfsoft.tech_orderergroups.yaml +++ b/config/crd/bases/fabricx.kfsoft.tech_orderergroups.yaml @@ -4561,6 +4561,9 @@ spec: type: string description: Pod labels type: object + pvcStorageSize: + description: PVC storage size for the consenter + type: string replicas: description: Replicas for this component format: int32 From 3fdaf700a9faa712384553b6fe7ff99f585d0a3a Mon Sep 17 00:00:00 2001 From: Elisha Mbadi Date: Tue, 25 Aug 2026 10:59:36 +0300 Subject: [PATCH 3/3] Add BlockExplorer controller tests and configuration template - Implement unit tests for the BlockExplorer controller, covering both 'configure' and 'deploy' modes. - Ensure the tests validate the creation of necessary Kubernetes resources such as secrets and deployments. - Introduce BlockExplorerTemplateData struct and BuildBlockExplorerConfig function to generate block explorer configuration. - Update the templates.go file to include the new configuration generation logic. --- PROJECT | 9 + api/v1alpha1/blockexplorer_types.go | 119 +++ api/v1alpha1/zz_generated.deepcopy.go | 141 ++- cmd/main.go | 7 + .../fabricx.kfsoft.tech_blockexplorers.yaml | 410 ++++++++ config/crd/kustomization.yaml | 1 + config/rbac/blockexplorer_admin_role.yaml | 27 + config/rbac/blockexplorer_editor_role.yaml | 33 + config/rbac/blockexplorer_viewer_role.yaml | 29 + config/rbac/kustomization.yaml | 4 +- config/rbac/role.yaml | 3 + .../fabricx_v1alpha1_blockexplorer.yaml | 17 + config/samples/kustomization.yaml | 1 + .../controller/blockexplorer_controller.go | 960 ++++++++++++++++++ .../blockexplorer_controller_test.go | 229 +++++ internal/controller/utils/templates.go | 53 + 16 files changed, 2041 insertions(+), 2 deletions(-) create mode 100644 api/v1alpha1/blockexplorer_types.go create mode 100644 config/crd/bases/fabricx.kfsoft.tech_blockexplorers.yaml create mode 100644 config/rbac/blockexplorer_admin_role.yaml create mode 100644 config/rbac/blockexplorer_editor_role.yaml create mode 100644 config/rbac/blockexplorer_viewer_role.yaml create mode 100644 config/samples/fabricx_v1alpha1_blockexplorer.yaml create mode 100644 internal/controller/blockexplorer_controller.go create mode 100644 internal/controller/blockexplorer_controller_test.go diff --git a/PROJECT b/PROJECT index 82eabec..c00008e 100644 --- a/PROJECT +++ b/PROJECT @@ -165,4 +165,13 @@ resources: kind: Namespace path: github.com/kfsoftware/fabric-x-operator/api/v1alpha1 version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: kfsoft.tech + group: fabricx + kind: BlockExplorer + path: github.com/kfsoftware/fabric-x-operator/api/v1alpha1 + version: v1alpha1 version: "3" diff --git a/api/v1alpha1/blockexplorer_types.go b/api/v1alpha1/blockexplorer_types.go new file mode 100644 index 0000000..225cf11 --- /dev/null +++ b/api/v1alpha1/blockexplorer_types.go @@ -0,0 +1,119 @@ +/* +Copyright 2025. + +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 ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// BlockExplorerSpec defines the desired state of BlockExplorer. +type BlockExplorerSpec struct { + // Bootstrap mode: "configure" or "deploy" + BootstrapMode string `json:"bootstrapMode,omitempty"` + + // Image for the block explorer (combined backend + UI container) + // +kubebuilder:default="ghcr.io/lf-decentralized-trust-labs/fabric-x-block-explorer" + Image string `json:"image,omitempty"` + + // ImageTag for the block explorer + // +kubebuilder:default="0.1.0" + ImageTag string `json:"imageTag,omitempty"` + + // Replicas for this component + // +kubebuilder:default=1 + Replicas int32 `json:"replicas,omitempty"` + + // Sidecar gRPC endpoint the explorer ingests blocks from (host:port). + // e.g. fabric-x-committer-sidecar-service.default.svc.cluster.local:5050 + SidecarEndpoint string `json:"sidecarEndpoint"` + + // StartBlock is the first block number to stream from the sidecar (defaults to 0) + StartBlock uint64 `json:"startBlock,omitempty"` + + // DatabaseName for the explorer PostgreSQL database (defaults to "explorer") + DatabaseName string `json:"databaseName,omitempty"` + + // DatabaseUser for the explorer PostgreSQL database (defaults to "explorer") + DatabaseUser string `json:"databaseUser,omitempty"` + + // Storage configuration for the managed PostgreSQL PVC + Storage *StorageConfig `json:"storage,omitempty"` + + // Resources configuration applied to the explorer and postgres containers + Resources *corev1.ResourceRequirements `json:"resources,omitempty"` + + // Pod annotations + PodAnnotations map[string]string `json:"podAnnotations,omitempty"` + + // Pod labels + PodLabels map[string]string `json:"podLabels,omitempty"` + + // Extra environment variables for the explorer container + Env []corev1.EnvVar `json:"env,omitempty"` + + // Image pull secrets + ImagePullSecrets []ImagePullSecret `json:"imagePullSecrets,omitempty"` + + // Tolerations + Tolerations []Toleration `json:"tolerations,omitempty"` +} + +// BlockExplorerStatus defines the observed state of BlockExplorer. +type BlockExplorerStatus struct { + // Status of the BlockExplorer + Status DeploymentStatus `json:"status,omitempty"` + + // Message describing the current state + Message string `json:"message,omitempty"` + + // Endpoint of the BlockExplorer REST API (e.g. -service:8080) + Endpoint string `json:"endpoint,omitempty"` + + // Conditions represent the latest available observations of an object's state + Conditions []metav1.Condition `json:"conditions,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:scope=Namespaced,shortName=blockexplorer,singular=blockexplorer +// +kubebuilder:printcolumn:name="State",type="string",JSONPath=".status.status" +// +kubebuilder:printcolumn:name="Endpoint",type="string",JSONPath=".status.endpoint" +// +kubebuilder:printcolumn:name="Message",type="string",JSONPath=".status.message" +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" + +// BlockExplorer is the Schema for the blockexplorers API. +type BlockExplorer struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec BlockExplorerSpec `json:"spec,omitempty"` + Status BlockExplorerStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// BlockExplorerList contains a list of BlockExplorer. +type BlockExplorerList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []BlockExplorer `json:"items"` +} + +func init() { + SchemeBuilder.Register(&BlockExplorer{}, &BlockExplorerList{}) +} diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 231374d..e011479 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -21,7 +21,7 @@ limitations under the License. package v1alpha1 import ( - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) @@ -219,6 +219,145 @@ func (in *BatchingConfig) DeepCopy() *BatchingConfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BlockExplorer) DeepCopyInto(out *BlockExplorer) { + *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 BlockExplorer. +func (in *BlockExplorer) DeepCopy() *BlockExplorer { + if in == nil { + return nil + } + out := new(BlockExplorer) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *BlockExplorer) 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 *BlockExplorerList) DeepCopyInto(out *BlockExplorerList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]BlockExplorer, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BlockExplorerList. +func (in *BlockExplorerList) DeepCopy() *BlockExplorerList { + if in == nil { + return nil + } + out := new(BlockExplorerList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *BlockExplorerList) 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 *BlockExplorerSpec) DeepCopyInto(out *BlockExplorerSpec) { + *out = *in + if in.Storage != nil { + in, out := &in.Storage, &out.Storage + *out = new(StorageConfig) + **out = **in + } + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = new(v1.ResourceRequirements) + (*in).DeepCopyInto(*out) + } + if in.PodAnnotations != nil { + in, out := &in.PodAnnotations, &out.PodAnnotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.PodLabels != nil { + in, out := &in.PodLabels, &out.PodLabels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Env != nil { + in, out := &in.Env, &out.Env + *out = make([]v1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ImagePullSecrets != nil { + in, out := &in.ImagePullSecrets, &out.ImagePullSecrets + *out = make([]ImagePullSecret, len(*in)) + copy(*out, *in) + } + if in.Tolerations != nil { + in, out := &in.Tolerations, &out.Tolerations + *out = make([]Toleration, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BlockExplorerSpec. +func (in *BlockExplorerSpec) DeepCopy() *BlockExplorerSpec { + if in == nil { + return nil + } + out := new(BlockExplorerSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BlockExplorerStatus) DeepCopyInto(out *BlockExplorerStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]metav1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BlockExplorerStatus. +func (in *BlockExplorerStatus) DeepCopy() *BlockExplorerStatus { + if in == nil { + return nil + } + out := new(BlockExplorerStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CA) DeepCopyInto(out *CA) { *out = *in diff --git a/cmd/main.go b/cmd/main.go index b1de93e..43e5cf0 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -325,6 +325,13 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "ChainNamespace") os.Exit(1) } + if err := (&controller.BlockExplorerReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "BlockExplorer") + os.Exit(1) + } // +kubebuilder:scaffold:builder if metricsCertWatcher != nil { diff --git a/config/crd/bases/fabricx.kfsoft.tech_blockexplorers.yaml b/config/crd/bases/fabricx.kfsoft.tech_blockexplorers.yaml new file mode 100644 index 0000000..e9f907d --- /dev/null +++ b/config/crd/bases/fabricx.kfsoft.tech_blockexplorers.yaml @@ -0,0 +1,410 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.18.0 + name: blockexplorers.fabricx.kfsoft.tech +spec: + group: fabricx.kfsoft.tech + names: + kind: BlockExplorer + listKind: BlockExplorerList + plural: blockexplorers + shortNames: + - blockexplorer + singular: blockexplorer + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.status + name: State + type: string + - jsonPath: .status.endpoint + name: Endpoint + type: string + - jsonPath: .status.message + name: Message + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: BlockExplorer is the Schema for the blockexplorers API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: BlockExplorerSpec defines the desired state of BlockExplorer. + properties: + bootstrapMode: + description: 'Bootstrap mode: "configure" or "deploy"' + type: string + databaseName: + description: DatabaseName for the explorer PostgreSQL database (defaults + to "explorer") + type: string + databaseUser: + description: DatabaseUser for the explorer PostgreSQL database (defaults + to "explorer") + type: string + env: + description: Extra environment variables for the explorer container + items: + description: EnvVar represents an environment variable present in + a Container. + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". + type: string + valueFrom: + description: Source for the environment variable's value. Cannot + be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. + properties: + apiVersion: + description: Version of the schema the FieldPath is + written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified + API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the exposed + resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + image: + default: ghcr.io/lf-decentralized-trust-labs/fabric-x-block-explorer + description: Image for the block explorer (combined backend + UI container) + type: string + imagePullSecrets: + description: Image pull secrets + items: + description: ImagePullSecret defines an image pull secret + properties: + name: + description: Secret name + type: string + required: + - name + type: object + type: array + imageTag: + default: 0.1.0 + description: ImageTag for the block explorer + type: string + podAnnotations: + additionalProperties: + type: string + description: Pod annotations + type: object + podLabels: + additionalProperties: + type: string + description: Pod labels + type: object + replicas: + default: 1 + description: Replicas for this component + format: int32 + type: integer + resources: + description: Resources configuration applied to the explorer and postgres + containers + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + sidecarEndpoint: + description: |- + Sidecar gRPC endpoint the explorer ingests blocks from (host:port). + e.g. fabric-x-committer-sidecar-service.default.svc.cluster.local:5050 + type: string + startBlock: + description: StartBlock is the first block number to stream from the + sidecar (defaults to 0) + format: int64 + type: integer + storage: + description: Storage configuration for the managed PostgreSQL PVC + properties: + accessMode: + description: Access mode + type: string + pvcName: + description: PVC name to use (if empty, will be auto-generated) + type: string + size: + description: Storage size + type: string + storageClass: + description: Storage class to use + type: string + required: + - size + type: object + tolerations: + description: Tolerations + items: + description: Toleration defines a toleration + properties: + effect: + description: Effect + type: string + key: + description: Key + type: string + operator: + description: Operator + type: string + tolerationSeconds: + description: Toleration seconds + format: int64 + type: integer + value: + description: Value + type: string + type: object + type: array + required: + - sidecarEndpoint + type: object + status: + description: BlockExplorerStatus defines the observed state of BlockExplorer. + properties: + conditions: + description: Conditions represent the latest available observations + of an object's state + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + endpoint: + description: Endpoint of the BlockExplorer REST API (e.g. -service:8080) + type: string + message: + description: Message describing the current state + type: string + status: + description: Status of the BlockExplorer + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 1933d12..e963647 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -19,6 +19,7 @@ resources: - bases/fabricx.kfsoft.tech_caenrollments.yaml - bases/fabricx.kfsoft.tech_identities.yaml - bases/fabricx.kfsoft.tech_chainnamespaces.yaml +- bases/fabricx.kfsoft.tech_blockexplorers.yaml # +kubebuilder:scaffold:crdkustomizeresource patches: diff --git a/config/rbac/blockexplorer_admin_role.yaml b/config/rbac/blockexplorer_admin_role.yaml new file mode 100644 index 0000000..5b01b52 --- /dev/null +++ b/config/rbac/blockexplorer_admin_role.yaml @@ -0,0 +1,27 @@ +# This rule is not used by the project fabric-x-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants full permissions ('*') over fabricx.kfsoft.tech. +# This role is intended for users authorized to modify roles and bindings within the cluster, +# enabling them to delegate specific permissions to other users or groups as needed. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: fabric-x-operator + app.kubernetes.io/managed-by: kustomize + name: blockexplorer-admin-role +rules: +- apiGroups: + - fabricx.kfsoft.tech + resources: + - blockexplorers + verbs: + - '*' +- apiGroups: + - fabricx.kfsoft.tech + resources: + - blockexplorers/status + verbs: + - get diff --git a/config/rbac/blockexplorer_editor_role.yaml b/config/rbac/blockexplorer_editor_role.yaml new file mode 100644 index 0000000..2eee9e8 --- /dev/null +++ b/config/rbac/blockexplorer_editor_role.yaml @@ -0,0 +1,33 @@ +# This rule is not used by the project fabric-x-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants permissions to create, update, and delete resources within the fabricx.kfsoft.tech. +# This role is intended for users who need to manage these resources +# but should not control RBAC or manage permissions for others. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: fabric-x-operator + app.kubernetes.io/managed-by: kustomize + name: blockexplorer-editor-role +rules: +- apiGroups: + - fabricx.kfsoft.tech + resources: + - blockexplorers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - fabricx.kfsoft.tech + resources: + - blockexplorers/status + verbs: + - get diff --git a/config/rbac/blockexplorer_viewer_role.yaml b/config/rbac/blockexplorer_viewer_role.yaml new file mode 100644 index 0000000..648b6a2 --- /dev/null +++ b/config/rbac/blockexplorer_viewer_role.yaml @@ -0,0 +1,29 @@ +# This rule is not used by the project fabric-x-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants read-only access to fabricx.kfsoft.tech resources. +# This role is intended for users who need visibility into these resources +# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: fabric-x-operator + app.kubernetes.io/managed-by: kustomize + name: blockexplorer-viewer-role +rules: +- apiGroups: + - fabricx.kfsoft.tech + resources: + - blockexplorers + verbs: + - get + - list + - watch +- apiGroups: + - fabricx.kfsoft.tech + resources: + - blockexplorers/status + verbs: + - get diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml index 50e69a0..2d4042f 100644 --- a/config/rbac/kustomization.yaml +++ b/config/rbac/kustomization.yaml @@ -70,4 +70,6 @@ resources: - committer_admin_role.yaml - committer_editor_role.yaml - committer_viewer_role.yaml - +- blockexplorer_admin_role.yaml +- blockexplorer_editor_role.yaml +- blockexplorer_viewer_role.yaml diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index e468e79..6bce8b5 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -60,6 +60,7 @@ rules: - apiGroups: - fabricx.kfsoft.tech resources: + - blockexplorers - caenrollments - cas - chainnamespaces @@ -88,6 +89,7 @@ rules: - apiGroups: - fabricx.kfsoft.tech resources: + - blockexplorers/finalizers - caenrollments/finalizers - cas/finalizers - chainnamespaces/finalizers @@ -109,6 +111,7 @@ rules: - apiGroups: - fabricx.kfsoft.tech resources: + - blockexplorers/status - caenrollments/status - cas/status - chainnamespaces/status diff --git a/config/samples/fabricx_v1alpha1_blockexplorer.yaml b/config/samples/fabricx_v1alpha1_blockexplorer.yaml new file mode 100644 index 0000000..6dab575 --- /dev/null +++ b/config/samples/fabricx_v1alpha1_blockexplorer.yaml @@ -0,0 +1,17 @@ +apiVersion: fabricx.kfsoft.tech/v1alpha1 +kind: BlockExplorer +metadata: + name: blockexplorer-sample +spec: + bootstrapMode: deploy + image: ghcr.io/lf-decentralized-trust-labs/fabric-x-block-explorer + imageTag: "0.1.0" + replicas: 1 + # gRPC endpoint of the committer sidecar managed by this operator. + # The operator's sidecar listens on port 5050 (ClusterIP service -service). + sidecarEndpoint: fabric-x-committer-sidecar-service.default.svc.cluster.local:5050 + startBlock: 0 + databaseName: explorer + databaseUser: explorer + storage: + size: 5Gi diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index 2f17ba2..4edbc36 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -18,4 +18,5 @@ resources: - fabricx.kfsoft.tech_v1alpha1_caenrollment.yaml - fabricx_v1alpha1_identity.yaml - fabricx_v1alpha1_namespace.yaml +- fabricx_v1alpha1_blockexplorer.yaml # +kubebuilder:scaffold:manifestskustomizesamples diff --git a/internal/controller/blockexplorer_controller.go b/internal/controller/blockexplorer_controller.go new file mode 100644 index 0000000..8d0652a --- /dev/null +++ b/internal/controller/blockexplorer_controller.go @@ -0,0 +1,960 @@ +/* +Copyright 2025. + +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 controller + +import ( + "context" + "crypto/rand" + "crypto/sha256" + "encoding/hex" + "fmt" + "net" + "sort" + "strconv" + "strings" + "time" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/intstr" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + logf "sigs.k8s.io/controller-runtime/pkg/log" + + fabricxv1alpha1 "github.com/kfsoftware/fabric-x-operator/api/v1alpha1" + "github.com/kfsoftware/fabric-x-operator/internal/controller/utils" +) + +const ( + // BlockExplorerFinalizerName is the name of the finalizer used by BlockExplorer + BlockExplorerFinalizerName = "blockexplorer.fabricx.kfsoft.tech/finalizer" + + blockExplorerDBPort = 5432 + blockExplorerDBImage = "postgres:16-alpine" + blockExplorerRESTPort = 8080 + blockExplorerUIPort = 3000 +) + +// BlockExplorerReconciler reconciles a BlockExplorer object +type BlockExplorerReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +// +kubebuilder:rbac:groups=fabricx.kfsoft.tech,resources=blockexplorers,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=fabricx.kfsoft.tech,resources=blockexplorers/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=fabricx.kfsoft.tech,resources=blockexplorers/finalizers,verbs=update +// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=core,resources=services,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=core,resources=secrets,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=core,resources=persistentvolumeclaims,verbs=get;list;watch;create;update;patch;delete + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +func (r *BlockExplorerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := logf.FromContext(ctx) + + // Add panic recovery + defer func() { + if panicErr := recover(); panicErr != nil { + log.Error(fmt.Errorf("panic recovered: %v", panicErr), "Panic in BlockExplorer reconciliation", + "namespace", req.Namespace, "name", req.Name) + + blockExplorer := &fabricxv1alpha1.BlockExplorer{} + if err := r.Get(ctx, req.NamespacedName, blockExplorer); err == nil { + panicMsg := fmt.Sprintf("Panic in BlockExplorer reconciliation: %v", panicErr) + r.updateBlockExplorerStatus(ctx, blockExplorer, fabricxv1alpha1.FailedStatus, panicMsg) + } + } + }() + + var blockExplorer fabricxv1alpha1.BlockExplorer + if err := r.Get(ctx, req.NamespacedName, &blockExplorer); err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + // Check if the BlockExplorer is being deleted + if !blockExplorer.DeletionTimestamp.IsZero() { + return r.handleDeletion(ctx, &blockExplorer) + } + + // Set initial status if not set + if blockExplorer.Status.Status == "" { + r.updateBlockExplorerStatus(ctx, &blockExplorer, fabricxv1alpha1.PendingStatus, "Initializing BlockExplorer") + } + + // Ensure finalizer is present + if err := r.ensureFinalizer(ctx, &blockExplorer); err != nil { + errorMsg := fmt.Sprintf("Failed to ensure finalizer: %v", err) + log.Error(err, "Failed to ensure finalizer") + r.updateBlockExplorerStatus(ctx, &blockExplorer, fabricxv1alpha1.FailedStatus, errorMsg) + return ctrl.Result{}, err + } + + // Reconcile the BlockExplorer + if err := r.reconcileBlockExplorer(ctx, &blockExplorer); err != nil { + if blockExplorer.Status.Status != fabricxv1alpha1.FailedStatus { + errorMsg := fmt.Sprintf("Failed to reconcile BlockExplorer: %v", err) + r.updateBlockExplorerStatus(ctx, &blockExplorer, fabricxv1alpha1.FailedStatus, errorMsg) + } + log.Error(err, "Failed to reconcile BlockExplorer") + return ctrl.Result{}, err + } + + // Requeue after 1 minute to ensure continuous monitoring + return ctrl.Result{RequeueAfter: time.Minute}, nil +} + +// reconcileBlockExplorer handles the reconciliation of a BlockExplorer +func (r *BlockExplorerReconciler) reconcileBlockExplorer(ctx context.Context, blockExplorer *fabricxv1alpha1.BlockExplorer) error { + log := logf.FromContext(ctx) + + log.Info("Starting BlockExplorer reconciliation", + "name", blockExplorer.Name, + "namespace", blockExplorer.Namespace, + "bootstrapMode", blockExplorer.Spec.BootstrapMode) + + switch blockExplorer.Spec.BootstrapMode { + case "", "configure": + // The explorer needs no certificates or MSP; configure mode is a no-op. + r.updateBlockExplorerStatus(ctx, blockExplorer, fabricxv1alpha1.RunningStatus, + "BlockExplorer configured (deploy with bootstrapMode: deploy)") + return nil + case "deploy": + if err := r.reconcileDeployMode(ctx, blockExplorer); err != nil { + errorMsg := fmt.Sprintf("Failed to reconcile in deploy mode: %v", err) + log.Error(err, "Failed to reconcile in deploy mode") + r.updateBlockExplorerStatus(ctx, blockExplorer, fabricxv1alpha1.FailedStatus, errorMsg) + return fmt.Errorf("failed to reconcile in deploy mode: %w", err) + } + default: + errorMsg := fmt.Sprintf("Invalid bootstrap mode: %s", blockExplorer.Spec.BootstrapMode) + log.Error(fmt.Errorf("%s", errorMsg), "Invalid bootstrap mode") + r.updateBlockExplorerStatus(ctx, blockExplorer, fabricxv1alpha1.FailedStatus, errorMsg) + return fmt.Errorf("%s", errorMsg) + } + + r.updateBlockExplorerStatus(ctx, blockExplorer, fabricxv1alpha1.RunningStatus, + fmt.Sprintf("BlockExplorer reconciled successfully, endpoint %s-service:8080", blockExplorer.Name)) + + log.Info("BlockExplorer reconciliation completed successfully") + return nil +} + +// reconcileDeployMode handles reconciliation in deploy mode (full deployment) +func (r *BlockExplorerReconciler) reconcileDeployMode(ctx context.Context, blockExplorer *fabricxv1alpha1.BlockExplorer) error { + log := logf.FromContext(ctx) + + // Validate the sidecar endpoint + sidecarHost, sidecarPort, err := net.SplitHostPort(blockExplorer.Spec.SidecarEndpoint) + if err != nil { + return fmt.Errorf("invalid sidecarEndpoint %q (expected host:port): %w", blockExplorer.Spec.SidecarEndpoint, err) + } + + // 1. Create/Update the managed PostgreSQL database + if err := r.reconcileDBSecret(ctx, blockExplorer); err != nil { + return fmt.Errorf("failed to reconcile db secret: %w", err) + } + if err := r.reconcilePostgresPVC(ctx, blockExplorer); err != nil { + return fmt.Errorf("failed to reconcile postgres pvc: %w", err) + } + if err := r.reconcilePostgresDeployment(ctx, blockExplorer); err != nil { + return fmt.Errorf("failed to reconcile postgres deployment: %w", err) + } + if err := r.reconcilePostgresService(ctx, blockExplorer); err != nil { + return fmt.Errorf("failed to reconcile postgres service: %w", err) + } + + // 2. Create/Update the explorer config Secret + if err := r.reconcileConfigSecret(ctx, blockExplorer, sidecarHost, sidecarPort); err != nil { + return fmt.Errorf("failed to reconcile config secret: %w", err) + } + + // 3. Create/Update the explorer Service and Deployment + if err := r.reconcileService(ctx, blockExplorer); err != nil { + return fmt.Errorf("failed to reconcile service: %w", err) + } + if err := r.reconcileDeployment(ctx, blockExplorer); err != nil { + return fmt.Errorf("failed to reconcile deployment: %w", err) + } + + log.Info("BlockExplorer deploy mode reconciliation completed") + return nil +} + +// reconcileDBSecret creates or updates the Secret holding the managed PostgreSQL credentials +func (r *BlockExplorerReconciler) reconcileDBSecret(ctx context.Context, blockExplorer *fabricxv1alpha1.BlockExplorer) error { + log := logf.FromContext(ctx) + + secretName := fmt.Sprintf("%s-db-secret", blockExplorer.Name) + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + Namespace: blockExplorer.Namespace, + }, + } + + _, err := controllerutil.CreateOrUpdate(ctx, r.Client, secret, func() error { + if err := controllerutil.SetControllerReference(blockExplorer, secret, r.Scheme); err != nil { + return err + } + if secret.Data == nil { + secret.Data = make(map[string][]byte) + } + if secret.Data["username"] == nil { + secret.Data["username"] = []byte(defaultString(blockExplorer.Spec.DatabaseUser, "explorer")) + } + if secret.Data["database"] == nil { + secret.Data["database"] = []byte(defaultString(blockExplorer.Spec.DatabaseName, "explorer")) + } + if secret.Data["password"] == nil { + password, err := randomPassword() + if err != nil { + return fmt.Errorf("failed to generate postgres password: %w", err) + } + secret.Data["password"] = []byte(password) + } + return nil + }) + if err != nil { + return fmt.Errorf("failed to reconcile db secret %s: %w", secretName, err) + } + + log.Info("DB secret reconciled successfully", "secret", secretName) + return nil +} + +// reconcilePostgresPVC creates the PVC backing the managed PostgreSQL data dir +func (r *BlockExplorerReconciler) reconcilePostgresPVC(ctx context.Context, blockExplorer *fabricxv1alpha1.BlockExplorer) error { + log := logf.FromContext(ctx) + + if blockExplorer.Spec.Storage == nil { + return nil + } + + pvcName := fmt.Sprintf("%s-postgres-pvc", blockExplorer.Name) + pvc := &corev1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: pvcName, + Namespace: blockExplorer.Namespace, + Labels: map[string]string{ + "app": "fabric-x", + "blockexplorer": blockExplorer.Name, + }, + }, + Spec: corev1.PersistentVolumeClaimSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{ + corev1.ReadWriteOnce, + }, + Resources: corev1.VolumeResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceStorage: resource.MustParse(blockExplorer.Spec.Storage.Size), + }, + }, + StorageClassName: func() *string { + if blockExplorer.Spec.Storage.StorageClass != "" { + return &blockExplorer.Spec.Storage.StorageClass + } + return nil + }(), + }, + } + + if err := r.updatePVC(ctx, blockExplorer, pvc); err != nil { + log.Error(err, "Failed to reconcile PVC", "name", pvcName) + return fmt.Errorf("failed to reconcile PVC %s: %w", pvcName, err) + } + + log.Info("Postgres PVC reconciled successfully", "pvc", pvcName) + return nil +} + +// reconcilePostgresDeployment creates or updates the managed PostgreSQL Deployment +func (r *BlockExplorerReconciler) reconcilePostgresDeployment(ctx context.Context, blockExplorer *fabricxv1alpha1.BlockExplorer) error { + log := logf.FromContext(ctx) + + deploymentName := fmt.Sprintf("%s-postgres", blockExplorer.Name) + deployment := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: deploymentName, + Namespace: blockExplorer.Namespace, + }, + } + + template := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: deploymentName, + Namespace: blockExplorer.Namespace, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: int32Ptr(1), + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "postgres", + "release": blockExplorer.Name, + }, + }, + Strategy: appsv1.DeploymentStrategy{ + Type: appsv1.RecreateDeploymentStrategyType, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "app": "postgres", + "release": blockExplorer.Name, + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "postgres", + Image: blockExplorerDBImage, + Ports: []corev1.ContainerPort{ + { + Name: "postgres", + ContainerPort: blockExplorerDBPort, + Protocol: corev1.ProtocolTCP, + }, + }, + Env: []corev1.EnvVar{ + {Name: "POSTGRES_USER", ValueFrom: r.dbSecretEnvSource(blockExplorer, "username")}, + {Name: "POSTGRES_PASSWORD", ValueFrom: r.dbSecretEnvSource(blockExplorer, "password")}, + {Name: "POSTGRES_DB", ValueFrom: r.dbSecretEnvSource(blockExplorer, "database")}, + }, + VolumeMounts: func() []corev1.VolumeMount { + if blockExplorer.Spec.Storage == nil { + return nil + } + return []corev1.VolumeMount{ + { + Name: "data", + MountPath: "/var/lib/postgresql/data", + }, + } + }(), + Resources: func() corev1.ResourceRequirements { + if blockExplorer.Spec.Resources != nil { + return *blockExplorer.Spec.Resources + } + return corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("128Mi"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("512Mi"), + }, + } + }(), + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + Exec: &corev1.ExecAction{ + Command: []string{"pg_isready", "-U", "$(POSTGRES_USER)"}, + }, + }, + InitialDelaySeconds: 5, + PeriodSeconds: 10, + }, + }, + }, + Volumes: func() []corev1.Volume { + if blockExplorer.Spec.Storage == nil { + return nil + } + return []corev1.Volume{ + { + Name: "data", + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: fmt.Sprintf("%s-postgres-pvc", blockExplorer.Name), + }, + }, + }, + } + }(), + }, + }, + }, + } + + if err := r.updateDeployment(ctx, blockExplorer, deployment, template); err != nil { + log.Error(err, "Failed to reconcile Deployment", "name", deploymentName) + return fmt.Errorf("failed to reconcile Deployment %s: %w", deploymentName, err) + } + + log.Info("Postgres deployment reconciled successfully", "deployment", deploymentName) + return nil +} + +// reconcilePostgresService creates or updates the Service for the managed PostgreSQL +func (r *BlockExplorerReconciler) reconcilePostgresService(ctx context.Context, blockExplorer *fabricxv1alpha1.BlockExplorer) error { + log := logf.FromContext(ctx) + + serviceName := fmt.Sprintf("%s-postgres", blockExplorer.Name) + service := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: serviceName, + Namespace: blockExplorer.Namespace, + }, + } + + template := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: serviceName, + Namespace: blockExplorer.Namespace, + }, + Spec: corev1.ServiceSpec{ + Type: corev1.ServiceTypeClusterIP, + Ports: []corev1.ServicePort{ + { + Port: blockExplorerDBPort, + TargetPort: intstr.FromInt(blockExplorerDBPort), + Protocol: corev1.ProtocolTCP, + Name: "postgres", + }, + }, + Selector: map[string]string{ + "app": "postgres", + "release": blockExplorer.Name, + }, + }, + } + + if err := r.updateService(ctx, blockExplorer, service, template); err != nil { + log.Error(err, "Failed to reconcile Service", "name", serviceName) + return fmt.Errorf("failed to reconcile Service %s: %w", serviceName, err) + } + + log.Info("Postgres service reconciled successfully", "service", serviceName) + return nil +} + +// reconcileConfigSecret creates or updates the explorer config Secret +func (r *BlockExplorerReconciler) reconcileConfigSecret(ctx context.Context, blockExplorer *fabricxv1alpha1.BlockExplorer, sidecarHost string, sidecarPort string) error { + log := logf.FromContext(ctx) + + secretName := fmt.Sprintf("%s-config", blockExplorer.Name) + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + Namespace: blockExplorer.Namespace, + }, + } + + // Fetch the managed DB credentials + dbSecret := &corev1.Secret{} + if err := r.Get(ctx, client.ObjectKey{ + Name: fmt.Sprintf("%s-db-secret", blockExplorer.Name), + Namespace: blockExplorer.Namespace, + }, dbSecret); err != nil { + return fmt.Errorf("failed to get db secret: %w", err) + } + + templateData := utils.BlockExplorerTemplateData{ + Name: blockExplorer.Name, + DatabaseHost: fmt.Sprintf("%s-postgres", blockExplorer.Name), + DatabasePort: blockExplorerDBPort, + DatabaseName: string(dbSecret.Data["database"]), + DatabaseUser: string(dbSecret.Data["username"]), + DatabasePass: string(dbSecret.Data["password"]), + SidecarHost: sidecarHost, + SidecarPort: sidecarPortInt(sidecarPort), + StartBlock: blockExplorer.Spec.StartBlock, + } + + template := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + Namespace: blockExplorer.Namespace, + }, + Type: corev1.SecretTypeOpaque, + Data: map[string][]byte{ + "config.yaml": []byte(utils.BuildBlockExplorerConfig(templateData)), + }, + } + + if err := r.updateSecret(ctx, blockExplorer, secret, template); err != nil { + log.Error(err, "Failed to reconcile Secret", "name", secretName) + return fmt.Errorf("failed to reconcile Secret %s: %w", secretName, err) + } + + log.Info("Config secret reconciled successfully", "secret", secretName) + return nil +} + +// reconcileService creates or updates the explorer Service +func (r *BlockExplorerReconciler) reconcileService(ctx context.Context, blockExplorer *fabricxv1alpha1.BlockExplorer) error { + log := logf.FromContext(ctx) + + service := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-service", blockExplorer.Name), + Namespace: blockExplorer.Namespace, + }, + } + + template := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-service", blockExplorer.Name), + Namespace: blockExplorer.Namespace, + }, + Spec: corev1.ServiceSpec{ + Type: corev1.ServiceTypeClusterIP, + Ports: []corev1.ServicePort{ + { + Port: blockExplorerRESTPort, + TargetPort: intstr.FromInt(blockExplorerRESTPort), + Protocol: corev1.ProtocolTCP, + Name: "http", + }, + { + Port: blockExplorerUIPort, + TargetPort: intstr.FromInt(blockExplorerUIPort), + Protocol: corev1.ProtocolTCP, + Name: "ui", + }, + }, + Selector: map[string]string{ + "app": "blockexplorer", + "release": blockExplorer.Name, + }, + }, + } + + if err := r.updateService(ctx, blockExplorer, service, template); err != nil { + log.Error(err, "Failed to reconcile Service", "name", service.Name) + return fmt.Errorf("failed to reconcile Service %s: %w", service.Name, err) + } + + log.Info("Service reconciled successfully", "service", service.Name) + return nil +} + +// reconcileDeployment creates or updates the explorer Deployment +func (r *BlockExplorerReconciler) reconcileDeployment(ctx context.Context, blockExplorer *fabricxv1alpha1.BlockExplorer) error { + log := logf.FromContext(ctx) + + // Compute a combined hash of the mounted secrets and extra env for rollout on change + var hashParts []string + + configSecretName := fmt.Sprintf("%s-config", blockExplorer.Name) + if hash, err := r.computeSecretHash(ctx, configSecretName, blockExplorer.Namespace); err != nil { + log.Error(err, "Failed to compute config secret hash", "secretName", configSecretName) + } else { + hashParts = append(hashParts, hash) + } + + if len(blockExplorer.Spec.Env) > 0 { + envHash := sha256.Sum256([]byte(fmt.Sprintf("%+v", blockExplorer.Spec.Env))) + hashParts = append(hashParts, hex.EncodeToString(envHash[:])) + } + + sort.Strings(hashParts) + combinedHashSum := sha256.Sum256([]byte(strings.Join(hashParts, "|"))) + configMapHash := hex.EncodeToString(combinedHashSum[:]) + + deployment := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: blockExplorer.Name, + Namespace: blockExplorer.Namespace, + }, + } + + template := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: blockExplorer.Name, + Namespace: blockExplorer.Namespace, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: &blockExplorer.Spec.Replicas, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "blockexplorer", + "release": blockExplorer.Name, + }, + }, + Strategy: appsv1.DeploymentStrategy{ + Type: appsv1.RecreateDeploymentStrategyType, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: r.podLabels(blockExplorer), + Annotations: func() map[string]string { + annotations := make(map[string]string) + for k, v := range blockExplorer.Spec.PodAnnotations { + annotations[k] = v + } + if configMapHash != "" { + annotations["fabricx.kfsoft.tech/config-hash"] = configMapHash + } + return annotations + }(), + }, + Spec: corev1.PodSpec{ + ImagePullSecrets: r.imagePullSecrets(blockExplorer), + Tolerations: r.tolerations(blockExplorer), + Containers: []corev1.Container{ + { + Name: "blockexplorer", + Image: fmt.Sprintf("%s:%s", + defaultString(blockExplorer.Spec.Image, "ghcr.io/lf-decentralized-trust-labs/fabric-x-block-explorer"), + defaultString(blockExplorer.Spec.ImageTag, "0.1.0")), + Env: blockExplorer.Spec.Env, + Ports: []corev1.ContainerPort{ + { + Name: "http", + ContainerPort: blockExplorerRESTPort, + Protocol: corev1.ProtocolTCP, + }, + { + Name: "ui", + ContainerPort: blockExplorerUIPort, + Protocol: corev1.ProtocolTCP, + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "config", + ReadOnly: true, + MountPath: "/home/explorer/config.yaml", + SubPath: "config.yaml", + }, + }, + Resources: func() corev1.ResourceRequirements { + if blockExplorer.Spec.Resources != nil { + return *blockExplorer.Spec.Resources + } + return corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("128Mi"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("768Mi"), + }, + } + }(), + LivenessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/healthz", + Port: intstr.FromInt(blockExplorerRESTPort), + }, + }, + InitialDelaySeconds: 15, + PeriodSeconds: 20, + }, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/healthz", + Port: intstr.FromInt(blockExplorerRESTPort), + }, + }, + InitialDelaySeconds: 5, + PeriodSeconds: 10, + }, + }, + }, + Volumes: []corev1.Volume{ + { + Name: "config", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: configSecretName, + }, + }, + }, + }, + }, + }, + }, + } + + if err := r.updateDeployment(ctx, blockExplorer, deployment, template); err != nil { + log.Error(err, "Failed to reconcile Deployment", "name", deployment.Name) + return fmt.Errorf("failed to reconcile Deployment %s: %w", deployment.Name, err) + } + + log.Info("Deployment reconciled successfully", "deployment", deployment.Name) + return nil +} + +// podLabels returns the pod labels for the explorer deployment merged with user-provided ones +func (r *BlockExplorerReconciler) podLabels(blockExplorer *fabricxv1alpha1.BlockExplorer) map[string]string { + labels := map[string]string{ + "app": "blockexplorer", + "release": blockExplorer.Name, + } + for k, v := range blockExplorer.Spec.PodLabels { + labels[k] = v + } + return labels +} + +// imagePullSecrets converts the API image pull secrets to core types +func (r *BlockExplorerReconciler) imagePullSecrets(blockExplorer *fabricxv1alpha1.BlockExplorer) []corev1.LocalObjectReference { + var refs []corev1.LocalObjectReference + for _, s := range blockExplorer.Spec.ImagePullSecrets { + refs = append(refs, corev1.LocalObjectReference{Name: s.Name}) + } + return refs +} + +// tolerations converts the API tolerations to core types +func (r *BlockExplorerReconciler) tolerations(blockExplorer *fabricxv1alpha1.BlockExplorer) []corev1.Toleration { + var tolerations []corev1.Toleration + for _, t := range blockExplorer.Spec.Tolerations { + tolerations = append(tolerations, corev1.Toleration{ + Key: t.Key, + Operator: corev1.TolerationOperator(t.Operator), + Value: t.Value, + Effect: corev1.TaintEffect(t.Effect), + TolerationSeconds: t.TolerationSeconds, + }) + } + return tolerations +} + +// dbSecretEnvSource returns an EnvVarSource pointing at a key in the managed db secret +func (r *BlockExplorerReconciler) dbSecretEnvSource(blockExplorer *fabricxv1alpha1.BlockExplorer, key string) *corev1.EnvVarSource { + return &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: fmt.Sprintf("%s-db-secret", blockExplorer.Name), + }, + Key: key, + }, + } +} + +// updateSecret updates a secret with template data +func (r *BlockExplorerReconciler) updateSecret(ctx context.Context, blockExplorer *fabricxv1alpha1.BlockExplorer, secret *corev1.Secret, template *corev1.Secret) error { + _, err := controllerutil.CreateOrUpdate(ctx, r.Client, secret, func() error { + if err := controllerutil.SetControllerReference(blockExplorer, secret, r.Scheme); err != nil { + return err + } + secret.Type = template.Type + secret.Data = template.Data + if secret.Labels == nil { + secret.Labels = make(map[string]string) + } + for k, v := range template.Labels { + secret.Labels[k] = v + } + if secret.Annotations == nil { + secret.Annotations = make(map[string]string) + } + for k, v := range template.Annotations { + secret.Annotations[k] = v + } + return nil + }) + return err +} + +// updateService updates a service with template data +func (r *BlockExplorerReconciler) updateService(ctx context.Context, blockExplorer *fabricxv1alpha1.BlockExplorer, service *corev1.Service, template *corev1.Service) error { + _, err := controllerutil.CreateOrUpdate(ctx, r.Client, service, func() error { + if err := controllerutil.SetControllerReference(blockExplorer, service, r.Scheme); err != nil { + return err + } + service.Spec = template.Spec + if service.Labels == nil { + service.Labels = make(map[string]string) + } + for k, v := range template.Labels { + service.Labels[k] = v + } + if service.Annotations == nil { + service.Annotations = make(map[string]string) + } + for k, v := range template.Annotations { + service.Annotations[k] = v + } + return nil + }) + return err +} + +// updateDeployment updates a deployment with template data +func (r *BlockExplorerReconciler) updateDeployment(ctx context.Context, blockExplorer *fabricxv1alpha1.BlockExplorer, deployment *appsv1.Deployment, template *appsv1.Deployment) error { + _, err := controllerutil.CreateOrUpdate(ctx, r.Client, deployment, func() error { + if err := controllerutil.SetControllerReference(blockExplorer, deployment, r.Scheme); err != nil { + return err + } + deployment.Spec = template.Spec + if deployment.Labels == nil { + deployment.Labels = make(map[string]string) + } + for k, v := range template.Labels { + deployment.Labels[k] = v + } + if deployment.Annotations == nil { + deployment.Annotations = make(map[string]string) + } + for k, v := range template.Annotations { + deployment.Annotations[k] = v + } + return nil + }) + return err +} + +// updatePVC creates or updates a PersistentVolumeClaim +func (r *BlockExplorerReconciler) updatePVC(ctx context.Context, blockExplorer *fabricxv1alpha1.BlockExplorer, pvc *corev1.PersistentVolumeClaim) error { + _, err := controllerutil.CreateOrUpdate(ctx, r.Client, pvc, func() error { + if err := controllerutil.SetControllerReference(blockExplorer, pvc, r.Scheme); err != nil { + return err + } + return nil + }) + return err +} + +// computeSecretHash computes a deterministic hash of a Secret's data +func (r *BlockExplorerReconciler) computeSecretHash(ctx context.Context, secretName, namespace string) (string, error) { + sec := &corev1.Secret{} + if err := r.Get(ctx, client.ObjectKey{Name: secretName, Namespace: namespace}, sec); err != nil { + return "", err + } + var parts []string + for k, v := range sec.Data { + parts = append(parts, fmt.Sprintf("%s=%s", k, string(v))) + } + sort.Strings(parts) + sum := sha256.Sum256([]byte(strings.Join(parts, "|"))) + return hex.EncodeToString(sum[:]), nil +} + +// handleDeletion handles the deletion of a BlockExplorer +func (r *BlockExplorerReconciler) handleDeletion(ctx context.Context, blockExplorer *fabricxv1alpha1.BlockExplorer) (ctrl.Result, error) { + log := logf.FromContext(ctx) + + log.Info("Handling BlockExplorer deletion", + "name", blockExplorer.Name, + "namespace", blockExplorer.Namespace) + + // Owned resources (deployments, services, secrets, pvc) are garbage-collected + // automatically via their controller reference. + + // Remove finalizer + if err := r.removeFinalizer(ctx, blockExplorer); err != nil { + errorMsg := fmt.Sprintf("Failed to remove finalizer: %v", err) + log.Error(err, "Failed to remove finalizer") + r.updateBlockExplorerStatus(ctx, blockExplorer, fabricxv1alpha1.FailedStatus, errorMsg) + return ctrl.Result{}, err + } + + log.Info("BlockExplorer deletion completed successfully") + return ctrl.Result{}, nil +} + +// ensureFinalizer ensures the finalizer is present on the BlockExplorer +func (r *BlockExplorerReconciler) ensureFinalizer(ctx context.Context, blockExplorer *fabricxv1alpha1.BlockExplorer) error { + if !utils.ContainsString(blockExplorer.Finalizers, BlockExplorerFinalizerName) { + blockExplorer.Finalizers = append(blockExplorer.Finalizers, BlockExplorerFinalizerName) + return r.Update(ctx, blockExplorer) + } + return nil +} + +// removeFinalizer removes the finalizer from the BlockExplorer +func (r *BlockExplorerReconciler) removeFinalizer(ctx context.Context, blockExplorer *fabricxv1alpha1.BlockExplorer) error { + blockExplorer.Finalizers = utils.RemoveString(blockExplorer.Finalizers, BlockExplorerFinalizerName) + return r.Update(ctx, blockExplorer) +} + +// updateBlockExplorerStatus updates the BlockExplorer status with the given status and message +func (r *BlockExplorerReconciler) updateBlockExplorerStatus(ctx context.Context, blockExplorer *fabricxv1alpha1.BlockExplorer, status fabricxv1alpha1.DeploymentStatus, message string) { + log := logf.FromContext(ctx) + + log.Info("Updating BlockExplorer status", + "name", blockExplorer.Name, + "namespace", blockExplorer.Namespace, + "status", status, + "message", message) + + blockExplorer.Status.Status = status + blockExplorer.Status.Message = message + if status == fabricxv1alpha1.RunningStatus { + blockExplorer.Status.Endpoint = fmt.Sprintf("%s-service:8080", blockExplorer.Name) + } + + now := metav1.Now() + blockExplorer.Status.Conditions = []metav1.Condition{ + { + Type: "Ready", + Status: metav1.ConditionTrue, + LastTransitionTime: now, + Reason: "Reconciled", + Message: message, + }, + } + + if err := r.Status().Update(ctx, blockExplorer); err != nil { + if !errors.IsNotFound(err) { + log.Error(err, "Failed to update BlockExplorer status") + } + } else { + log.Info("BlockExplorer status updated successfully", + "name", blockExplorer.Name, + "status", status) + } +} + +// SetupWithManager sets up the controller with the Manager. +func (r *BlockExplorerReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&fabricxv1alpha1.BlockExplorer{}). + Named("blockexplorer"). + Complete(r) +} + +func randomPassword() (string, error) { + buf := make([]byte, 24) + if _, err := rand.Read(buf); err != nil { + return "", err + } + return hex.EncodeToString(buf), nil +} + +func defaultString(value, fallback string) string { + if value == "" { + return fallback + } + return value +} + +func int32Ptr(v int32) *int32 { + return &v +} + +func sidecarPortInt(p string) int32 { + n, err := strconv.Atoi(p) + if err != nil { + return 5050 + } + return int32(n) +} diff --git a/internal/controller/blockexplorer_controller_test.go b/internal/controller/blockexplorer_controller_test.go new file mode 100644 index 0000000..7e2db23 --- /dev/null +++ b/internal/controller/blockexplorer_controller_test.go @@ -0,0 +1,229 @@ +/* +Copyright 2025. + +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 controller + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + fabricxv1alpha1 "github.com/kfsoftware/fabric-x-operator/api/v1alpha1" + "github.com/kfsoftware/fabric-x-operator/internal/controller/utils" +) + +var _ = Describe("BlockExplorer Controller", func() { + Context("When reconciling a resource", func() { + const resourceName = "test-block-explorer" + + ctx := context.Background() + + typeNamespacedName := types.NamespacedName{ + Name: resourceName, + Namespace: "default", + } + + blockExplorer := &fabricxv1alpha1.BlockExplorer{} + + BeforeEach(func() { + By("creating the custom resource for the Kind BlockExplorer") + err := k8sClient.Get(ctx, typeNamespacedName, blockExplorer) + if err != nil && errors.IsNotFound(err) { + resource := &fabricxv1alpha1.BlockExplorer{ + ObjectMeta: metav1.ObjectMeta{ + Name: resourceName, + Namespace: "default", + }, + Spec: fabricxv1alpha1.BlockExplorerSpec{ + BootstrapMode: "configure", + SidecarEndpoint: "fabric-x-committer-sidecar-service.default.svc.cluster.local:5050", + Replicas: 1, + }, + } + Expect(k8sClient.Create(ctx, resource)).To(Succeed()) + } + }) + + AfterEach(func() { + resource := &fabricxv1alpha1.BlockExplorer{} + err := k8sClient.Get(ctx, typeNamespacedName, resource) + Expect(err).NotTo(HaveOccurred()) + + By("Cleanup the specific resource instance BlockExplorer") + Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) + }) + + It("should successfully reconcile the resource in configure mode", func() { + By("Reconciling the created resource") + controllerReconciler := &BlockExplorerReconciler{ + Client: k8sClient, + Scheme: k8sClient.Scheme(), + } + + _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ + NamespacedName: typeNamespacedName, + }) + Expect(err).NotTo(HaveOccurred()) + }) + + It("should create all resources in deploy mode", func() { + // Use a different resource name to avoid conflict with AfterEach cleanup + deployResourceName := "test-block-explorer-deploy" + deployTypeNamespacedName := types.NamespacedName{ + Name: deployResourceName, + Namespace: "default", + } + + By("Creating a resource in deploy mode") + resource := &fabricxv1alpha1.BlockExplorer{ + ObjectMeta: metav1.ObjectMeta{ + Name: deployResourceName, + Namespace: "default", + }, + Spec: fabricxv1alpha1.BlockExplorerSpec{ + BootstrapMode: "deploy", + SidecarEndpoint: "fabric-x-committer-sidecar-service.default.svc.cluster.local:5050", + Replicas: 1, + Storage: &fabricxv1alpha1.StorageConfig{ + Size: "1Gi", + }, + }, + } + Expect(k8sClient.Create(ctx, resource)).To(Succeed()) + + By("Reconciling the resource") + controllerReconciler := &BlockExplorerReconciler{ + Client: k8sClient, + Scheme: k8sClient.Scheme(), + } + _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ + NamespacedName: deployTypeNamespacedName, + }) + Expect(err).NotTo(HaveOccurred()) + + By("Checking the db secret was created") + dbSecret := &corev1.Secret{} + Eventually(func() error { + return k8sClient.Get(ctx, types.NamespacedName{ + Name: deployResourceName + "-db-secret", + Namespace: "default", + }, dbSecret) + }).Should(Succeed()) + Expect(dbSecret.Data).To(HaveKey("username")) + Expect(dbSecret.Data).To(HaveKey("password")) + Expect(dbSecret.Data).To(HaveKey("database")) + + By("Checking the config secret was created") + secret := &corev1.Secret{} + Eventually(func() error { + return k8sClient.Get(ctx, types.NamespacedName{ + Name: deployResourceName + "-config", + Namespace: "default", + }, secret) + }).Should(Succeed()) + Expect(secret.Data).To(HaveKey("config.yaml")) + Expect(string(secret.Data["config.yaml"])).To(ContainSubstring("fabric-x-committer-sidecar-service.default.svc.cluster.local")) + Expect(string(secret.Data["config.yaml"])).To(ContainSubstring("port: 5050")) + + By("Checking the postgres deployment was created") + postgresDeployment := &appsv1.Deployment{} + Eventually(func() error { + return k8sClient.Get(ctx, types.NamespacedName{ + Name: deployResourceName + "-postgres", + Namespace: "default", + }, postgresDeployment) + }).Should(Succeed()) + Expect(postgresDeployment.Spec.Template.Spec.Containers[0].Image).To(Equal("postgres:16-alpine")) + + By("Checking the postgres service was created") + postgresService := &corev1.Service{} + Eventually(func() error { + return k8sClient.Get(ctx, types.NamespacedName{ + Name: deployResourceName + "-postgres", + Namespace: "default", + }, postgresService) + }).Should(Succeed()) + + By("Checking the explorer service was created") + explorerService := &corev1.Service{} + Eventually(func() error { + return k8sClient.Get(ctx, types.NamespacedName{ + Name: deployResourceName + "-service", + Namespace: "default", + }, explorerService) + }).Should(Succeed()) + Expect(explorerService.Spec.Ports).To(HaveLen(2)) + + By("Checking the explorer deployment was created") + explorerDeployment := &appsv1.Deployment{} + Eventually(func() error { + return k8sClient.Get(ctx, types.NamespacedName{ + Name: deployResourceName, + Namespace: "default", + }, explorerDeployment) + }).Should(Succeed()) + Expect(explorerDeployment.Spec.Template.Spec.Containers[0].Image). + To(Equal("ghcr.io/lf-decentralized-trust-labs/fabric-x-block-explorer:0.1.0")) + + By("Checking the status was updated with the endpoint") + updated := &fabricxv1alpha1.BlockExplorer{} + Expect(k8sClient.Get(ctx, deployTypeNamespacedName, updated)).To(Succeed()) + Expect(updated.Status.Status).To(Equal(fabricxv1alpha1.RunningStatus)) + Expect(updated.Status.Endpoint).To(Equal(deployResourceName + "-service:8080")) + + By("Cleaning up the deploy mode resource") + Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) + }) + }) + + Context("When testing BuildBlockExplorerConfig", func() { + It("should generate a valid block explorer configuration", func() { + data := utils.BlockExplorerTemplateData{ + Name: "test-explorer", + DatabaseHost: "test-explorer-postgres", + DatabasePort: 5432, + DatabaseName: "explorer", + DatabaseUser: "explorer", + DatabasePass: "secret", + SidecarHost: "fabric-x-committer-sidecar-service.default.svc.cluster.local", + SidecarPort: 5050, + StartBlock: 0, + } + + config := utils.BuildBlockExplorerConfig(data) + + By("Verifying configuration contains required fields") + Expect(config).To(ContainSubstring("database:")) + Expect(config).To(ContainSubstring("host: test-explorer-postgres")) + Expect(config).To(ContainSubstring("port: 5432")) + Expect(config).To(ContainSubstring("user: explorer")) + Expect(config).To(ContainSubstring("dbname: explorer")) + Expect(config).To(ContainSubstring("host: fabric-x-committer-sidecar-service.default.svc.cluster.local")) + Expect(config).To(ContainSubstring("port: 5050")) + Expect(config).To(ContainSubstring("mode: none")) + Expect(config).To(ContainSubstring("start_block: 0")) + Expect(config).To(ContainSubstring("server:")) + }) + }) +}) diff --git a/internal/controller/utils/templates.go b/internal/controller/utils/templates.go index a0ec121..bad0b2c 100644 --- a/internal/controller/utils/templates.go +++ b/internal/controller/utils/templates.go @@ -236,6 +236,59 @@ type CommitterVerifierTemplateData struct { Port int32 } +// BlockExplorerTemplateData holds configuration data for the block explorer +type BlockExplorerTemplateData struct { + Name string + DatabaseHost string + DatabasePort int32 + DatabaseName string + DatabaseUser string + DatabasePass string + SidecarHost string + SidecarPort int32 + StartBlock uint64 +} + +// BuildBlockExplorerConfig returns the block explorer config template. +// Field names mirror the explorer config schema (see fabric-x-block-explorer config.docker.yaml). +func BuildBlockExplorerConfig(data BlockExplorerTemplateData) string { + return fmt.Sprintf(`database: + endpoints: + - host: %s + port: %d + user: %s + password: %s + dbname: %s + max_conns: 20 + +sidecar: + connection: + endpoint: + host: %s + port: %d + tls: + mode: none + start_block: %d + +buffer: + raw_channel_size: 500 + proc_channel_size: 500 + +workers: + processor_count: 4 + writer_count: 4 + +server: + rest: + endpoint: + host: 0.0.0.0 + port: 8080 + read_header_timeout: 10s + default_tx_limit: 50 +`, data.DatabaseHost, data.DatabasePort, data.DatabaseUser, data.DatabasePass, data.DatabaseName, + data.SidecarHost, data.SidecarPort, data.StartBlock) +} + // Common template constants that can be reused across controllers const ( // BatcherConfigTemplate is the Go template for batcher configuration